diff --git a/.backportrc.json b/.backportrc.json index ea135907bd807..8c673a14a80da 100644 --- a/.backportrc.json +++ b/.backportrc.json @@ -3,6 +3,7 @@ "repoName": "kibana", "targetBranchChoices": [ "main", + "8.12", "8.11", "8.10", "8.9", @@ -48,7 +49,7 @@ "backport" ], "branchLabelMapping": { - "^v8.12.0$": "main", + "^v8.13.0$": "main", "^v(\\d+).(\\d+).\\d+$": "$1.$2" }, "autoMerge": true, diff --git a/.buildkite/disabled_jest_configs.json b/.buildkite/disabled_jest_configs.json index 0d7741e60b385..fe51488c7066f 100644 --- a/.buildkite/disabled_jest_configs.json +++ b/.buildkite/disabled_jest_configs.json @@ -1,3 +1 @@ -[ - "x-pack/plugins/index_management/jest.config.js" -] \ No newline at end of file +[] diff --git a/.buildkite/ftr_configs.yml b/.buildkite/ftr_configs.yml index 662ebbe522ebd..62ca848819d1c 100644 --- a/.buildkite/ftr_configs.yml +++ b/.buildkite/ftr_configs.yml @@ -232,8 +232,6 @@ enabled: - x-pack/test/cloud_security_posture_api/config.ts - x-pack/test/dataset_quality_api_integration/basic/config.ts - x-pack/test/detection_engine_api_integration/basic/config.ts - - x-pack/test/detection_engine_api_integration/security_and_spaces/group1/config.ts - - x-pack/test/detection_engine_api_integration/security_and_spaces/group10/config.ts - x-pack/test/disable_ems/config.ts - x-pack/test/encrypted_saved_objects_api_integration/config.ts - x-pack/test/examples/config.ts @@ -307,6 +305,7 @@ enabled: - x-pack/test/functional/apps/ml/stack_management_jobs/config.ts - x-pack/test/functional/apps/monitoring/config.ts - x-pack/test/functional/apps/observability_log_explorer/config.ts + - x-pack/test/functional/apps/painless_lab/config.ts - x-pack/test/functional/apps/remote_clusters/config.ts - x-pack/test/functional/apps/reporting_management/config.ts - x-pack/test/functional/apps/rollup_job/config.ts @@ -342,6 +341,7 @@ enabled: - x-pack/test/observability_onboarding_api_integration/basic/config.ts - x-pack/test/observability_onboarding_api_integration/cloud/config.ts - x-pack/test/observability_ai_assistant_api_integration/enterprise/config.ts + - x-pack/test/observability_ai_assistant_functional/enterprise/config.ts - x-pack/test/plugin_api_integration/config.ts - x-pack/test/plugin_functional/config.ts - x-pack/test/reporting_api_integration/reporting_and_security.config.ts @@ -498,5 +498,17 @@ enabled: - x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/exception_lists_items/configs/ess.config.ts - x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/configs/serverless.config.ts - x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists/default_license/lists_items/configs/ess.config.ts - - + - x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_delete/configs/serverless.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_delete/configs/ess.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_update/configs/ess.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_update/configs/serverless.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_patch/configs/ess.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_patch/configs/serverless.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_import_export/configs/serverless.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_import_export/configs/ess.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_management/configs/serverless.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_management/configs/ess.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_read/configs/serverless.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_read/configs/ess.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_bulk_actions/configs/ess.config.ts + - x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_bulk_actions/configs/serverless.config.ts diff --git a/.buildkite/pipelines/es_serverless/verify_es_serverless_image.yml b/.buildkite/pipelines/es_serverless/verify_es_serverless_image.yml index e641ed08bf34f..77ae40971a282 100644 --- a/.buildkite/pipelines/es_serverless/verify_es_serverless_image.yml +++ b/.buildkite/pipelines/es_serverless/verify_es_serverless_image.yml @@ -56,8 +56,8 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/functional/security_serverless.sh - label: 'Serverless Security Cypress Tests' + - command: .buildkite/scripts/steps/functional/security_serverless_entity_analytics.sh + label: 'Serverless Entity Analytics - Security Solution Cypress Tests' if: "build.env('SKIP_CYPRESS') != '1' && build.env('SKIP_CYPRESS') != 'true'" agents: queue: n2-4-spot diff --git a/.buildkite/pipelines/flaky_tests/groups.json b/.buildkite/pipelines/flaky_tests/groups.json index 992efad798b8d..292c5fe33397c 100644 --- a/.buildkite/pipelines/flaky_tests/groups.json +++ b/.buildkite/pipelines/flaky_tests/groups.json @@ -1,12 +1,12 @@ { "groups": [ { - "key": "cypress/security_solution", - "name": "Security Solution - Cypress" + "key": "cypress/security_solution_entity_analytics", + "name": "Security Solution Entity Analytics - Cypress" }, { - "key": "cypress/security_serverless", - "name": "[Serverless] Security Solution - Cypress" + "key": "cypress/security_serverless_entity_analytics", + "name": "[Serverless] Security Solution Entity Analytics - Cypress" }, { "key": "cypress/security_solution_investigations", @@ -32,7 +32,6 @@ "key": "cypress/security_serverless_rule_management", "name": "[Serverless] Security Solution Rule Management - Cypress" }, - { "key": "cypress/security_solution_rule_management_prebuilt_rules", "name": "Security Solution Rule Management - Prebuilt Rules - Cypress" diff --git a/.buildkite/pipelines/on_merge.yml b/.buildkite/pipelines/on_merge.yml index ed464e2a184f4..27b48e0d18d4b 100644 --- a/.buildkite/pipelines/on_merge.yml +++ b/.buildkite/pipelines/on_merge.yml @@ -79,8 +79,8 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/functional/security_serverless.sh - label: 'Serverless Security Cypress Tests' + - command: .buildkite/scripts/steps/functional/security_serverless_entity_analytics.sh + label: 'Serverless Entity Analytics - Security Cypress Tests' agents: queue: n2-4-spot depends_on: build @@ -235,13 +235,13 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/functional/security_solution.sh - label: 'Security Solution Cypress Tests' + - command: .buildkite/scripts/steps/functional/security_solution_entity_analytics.sh + label: 'Entity Analytics - Security Solution Cypress Tests' agents: queue: n2-4-spot depends_on: build timeout_in_minutes: 60 - parallelism: 8 + parallelism: 2 retry: automatic: - exit_status: '*' @@ -271,30 +271,6 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/functional/defend_workflows.sh - label: 'Defend Workflows Cypress Tests' - agents: - queue: n2-4-virt - depends_on: build - timeout_in_minutes: 60 - parallelism: 10 - retry: - automatic: - - exit_status: '*' - limit: 1 - - - command: .buildkite/scripts/steps/functional/defend_workflows_serverless.sh - label: 'Defend Workflows Cypress Tests on Serverless' - agents: - queue: n2-4-virt - depends_on: build - timeout_in_minutes: 60 - parallelism: 6 - retry: - automatic: - - exit_status: '*' - limit: 1 - - command: .buildkite/scripts/steps/functional/threat_intelligence.sh label: 'Threat Intelligence Cypress Tests' agents: diff --git a/.buildkite/pipelines/on_merge_unsupported_ftrs.yml b/.buildkite/pipelines/on_merge_unsupported_ftrs.yml index 8221e27ea7502..bf606f2e5abd5 100644 --- a/.buildkite/pipelines/on_merge_unsupported_ftrs.yml +++ b/.buildkite/pipelines/on_merge_unsupported_ftrs.yml @@ -61,3 +61,27 @@ steps: limit: 3 - exit_status: '*' limit: 1 + + - command: .buildkite/scripts/steps/functional/defend_workflows.sh + label: 'Defend Workflows Cypress Tests' + agents: + queue: n2-4-virt + depends_on: build + timeout_in_minutes: 60 + parallelism: 10 + retry: + automatic: + - exit_status: '*' + limit: 1 + + - command: .buildkite/scripts/steps/functional/defend_workflows_serverless.sh + label: 'Defend Workflows Cypress Tests on Serverless' + agents: + queue: n2-4-virt + depends_on: build + timeout_in_minutes: 60 + parallelism: 6 + retry: + automatic: + - exit_status: '*' + limit: 1 diff --git a/.buildkite/pipelines/pull_request/base.yml b/.buildkite/pipelines/pull_request/base.yml index 0040767d5b3d1..6011d1d78a696 100644 --- a/.buildkite/pipelines/pull_request/base.yml +++ b/.buildkite/pipelines/pull_request/base.yml @@ -57,8 +57,8 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/functional/security_serverless.sh - label: 'Serverless Security Cypress Tests' + - command: .buildkite/scripts/steps/functional/security_serverless_entity_analytics.sh + label: 'Serverless Entity Analytics - Security Cypress Tests' agents: queue: n2-4-spot depends_on: build @@ -153,13 +153,13 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/functional/security_solution.sh - label: 'Security Solution Cypress Tests' + - command: .buildkite/scripts/steps/functional/security_solution_entity_analytics.sh + label: 'Entity Analytics - Security Solution Cypress Tests' agents: queue: n2-4-spot depends_on: build timeout_in_minutes: 60 - parallelism: 8 + parallelism: 2 retry: automatic: - exit_status: '*' @@ -249,30 +249,6 @@ steps: - exit_status: '*' limit: 1 - - command: .buildkite/scripts/steps/functional/defend_workflows.sh - label: 'Defend Workflows Cypress Tests' - agents: - queue: n2-4-virt - depends_on: build - timeout_in_minutes: 60 - parallelism: 16 - retry: - automatic: - - exit_status: '*' - limit: 1 - - - command: .buildkite/scripts/steps/functional/defend_workflows_serverless.sh - label: 'Defend Workflows Cypress Tests on Serverless' - agents: - queue: n2-4-virt - depends_on: build - timeout_in_minutes: 60 - parallelism: 6 - retry: - automatic: - - exit_status: '*' - limit: 1 - - command: .buildkite/scripts/steps/functional/threat_intelligence.sh label: 'Threat Intelligence Cypress Tests' agents: diff --git a/.buildkite/pipelines/pull_request/cypress_burn.yml b/.buildkite/pipelines/pull_request/cypress_burn.yml index 20dc2fd7a13b9..e65659a6e038b 100644 --- a/.buildkite/pipelines/pull_request/cypress_burn.yml +++ b/.buildkite/pipelines/pull_request/cypress_burn.yml @@ -1,6 +1,6 @@ steps: - command: .buildkite/scripts/steps/functional/defend_workflows_burn.sh - label: 'Defend Workflows Cypress Tests, burning changed specs' + label: '[Soft fail] Defend Workflows Cypress Tests, burning changed specs' agents: queue: n2-4-virt depends_on: build @@ -11,7 +11,7 @@ steps: automatic: false - command: .buildkite/scripts/steps/functional/defend_workflows_serverless_burn.sh - label: 'Defend Workflows Cypress Tests on Serverless, burning changed specs' + label: '[Soft fail] Defend Workflows Cypress Tests on Serverless, burning changed specs' agents: queue: n2-4-virt depends_on: build @@ -22,7 +22,7 @@ steps: automatic: false - command: .buildkite/scripts/steps/functional/security_solution_burn.sh - label: 'Security Solution Cypress tests, burning changed specs' + label: '[Soft fail] Security Solution Cypress tests, burning changed specs' agents: queue: n2-4-spot depends_on: build @@ -33,7 +33,7 @@ steps: soft_fail: true - command: .buildkite/scripts/steps/functional/osquery_cypress_burn.sh - label: 'Osquery Cypress Tests, burning changed specs' + label: '[Soft fail] Osquery Cypress Tests, burning changed specs' agents: queue: n2-4-spot depends_on: build diff --git a/.buildkite/pipelines/pull_request/defend_workflows.yml b/.buildkite/pipelines/pull_request/defend_workflows.yml new file mode 100644 index 0000000000000..29d90461b04d5 --- /dev/null +++ b/.buildkite/pipelines/pull_request/defend_workflows.yml @@ -0,0 +1,24 @@ +steps: + - command: .buildkite/scripts/steps/functional/defend_workflows.sh + label: 'Defend Workflows Cypress Tests' + agents: + queue: n2-4-virt + depends_on: build + timeout_in_minutes: 60 + parallelism: 16 + retry: + automatic: + - exit_status: '*' + limit: 1 + + - command: .buildkite/scripts/steps/functional/defend_workflows_serverless.sh + label: 'Defend Workflows Cypress Tests on Serverless' + agents: + queue: n2-4-virt + depends_on: build + timeout_in_minutes: 60 + parallelism: 6 + retry: + automatic: + - exit_status: '*' + limit: 1 diff --git a/.buildkite/pipelines/quality-gates/pipeline.kibana-tests.yaml b/.buildkite/pipelines/quality-gates/pipeline.kibana-tests.yaml index 467df501bc9ca..1217853275914 100644 --- a/.buildkite/pipelines/quality-gates/pipeline.kibana-tests.yaml +++ b/.buildkite/pipelines/quality-gates/pipeline.kibana-tests.yaml @@ -10,6 +10,11 @@ # # Docs: https://docs.elastic.dev/serverless/qualitygates +agents: + cpu: 2 + ephemeralStorage: "20G" + memory: "8G" + env: TEAM_CHANNEL: "#kibana-mission-control" ENVIRONMENT: ${ENVIRONMENT?} diff --git a/.buildkite/pipelines/security_solution/api_integration.yml b/.buildkite/pipelines/security_solution/api_integration.yml index 0fbc23bcab68a..21f5b34a6fe21 100644 --- a/.buildkite/pipelines/security_solution/api_integration.yml +++ b/.buildkite/pipelines/security_solution/api_integration.yml @@ -15,7 +15,7 @@ steps: key: exception_operators_date_numeric_types:qa:serverless agents: queue: n2-4-spot - timeout_in_minutes: 120 + timeout_in_minutes: 120 retry: automatic: - exit_status: '*' @@ -31,7 +31,7 @@ steps: 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 @@ -53,7 +53,6 @@ steps: automatic: - exit_status: '1' 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 @@ -76,7 +75,7 @@ steps: 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 @@ -97,7 +96,7 @@ steps: retry: automatic: - exit_status: '1' - limit: 2 + 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 @@ -119,7 +118,7 @@ steps: retry: automatic: - exit_status: '1' - limit: 2 + 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 @@ -130,7 +129,7 @@ steps: retry: automatic: - exit_status: '1' - limit: 2 + 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 @@ -152,8 +151,8 @@ steps: retry: automatic: - exit_status: '1' - limit: 2 - + 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 @@ -163,7 +162,7 @@ steps: retry: automatic: - exit_status: '1' - limit: 2 + limit: 2 - label: Running telemetry:qa:serverless command: .buildkite/scripts/pipelines/security_solution_quality_gate/api-integration-tests.sh telemetry:qa:serverless @@ -175,4 +174,80 @@ steps: automatic: - exit_status: '1' limit: 2 - \ No newline at end of file + + - 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 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 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_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_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_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_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 diff --git a/.buildkite/pipelines/security_solution/security_solution_cypress.yml b/.buildkite/pipelines/security_solution/security_solution_cypress.yml index 3fdd8c16a9b76..58e505927b95c 100644 --- a/.buildkite/pipelines/security_solution/security_solution_cypress.yml +++ b/.buildkite/pipelines/security_solution/security_solution_cypress.yml @@ -93,4 +93,16 @@ steps: retry: automatic: - exit_status: '*' - limit: 1 \ No newline at end of file + 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/pull_requests.json b/.buildkite/pull_requests.json index ebe7bafe9f3d5..02686fe378b4f 100644 --- a/.buildkite/pull_requests.json +++ b/.buildkite/pull_requests.json @@ -13,8 +13,9 @@ "commit_status_context": "kibana-ci", "build_on_commit": true, "build_on_comment": true, - "trigger_comment_regex": "^(?:(?:buildkite\\W+)?(?:build|test)\\W+(?:this|it))", - "always_trigger_comment_regex": "^(?:(?:buildkite\\W+)?(?:build|test)\\W+(?:this|it))", + "build_drafts": false, + "trigger_comment_regex": "^(?:(?:buildkite\\W+)?(?:build|test)\\W+(?:this|it))|^\\/ci$", + "always_trigger_comment_regex": "^(?:(?:buildkite\\W+)?(?:build|test)\\W+(?:this|it))|^\\/ci$", "skip_ci_labels": ["skip-ci", "jenkins-ci"], "skip_target_branches": ["6.8", "7.11", "7.12"], "enable_skippable_commits": true, @@ -64,7 +65,6 @@ "build_on_commit": true, "build_on_comment": false, "trigger_comment_regex": "^(?:(?:buildkite\\W+)?(?:build|test)\\W+(?:this|it))", - "always_trigger_comment_regex": "^(?:(?:buildkite\\W+)?(?:build|test)\\W+(?:this|it))", "skip_ci_labels": [], "labels": ["kme-test"], "skip_target_branches": ["6.8", "7.11", "7.12"], diff --git a/.buildkite/scripts/pipelines/pull_request/pipeline.ts b/.buildkite/scripts/pipelines/pull_request/pipeline.ts index 9c7b02eb98604..0a305f49a811f 100644 --- a/.buildkite/scripts/pipelines/pull_request/pipeline.ts +++ b/.buildkite/scripts/pipelines/pull_request/pipeline.ts @@ -179,6 +179,19 @@ const uploadPipeline = (pipelineContent: string | object) => { pipeline.push(getPipeline('.buildkite/pipelines/pull_request/cypress_burn.yml')); } + if ( + (await doAnyChangesMatch([ + /^packages\/kbn-securitysolution-.*/, + /^x-pack\/plugins\/security_solution/, + /^x-pack\/test\/defend_workflows_cypress/, + /^x-pack\/test\/security_solution_cypress/, + /^fleet_packages\.json/, + ])) || + GITHUB_PR_LABELS.includes('ci:all-cypress-suites') + ) { + pipeline.push(getPipeline('.buildkite/pipelines/pull_request/defend_workflows.yml')); + } + pipeline.push(getPipeline('.buildkite/pipelines/pull_request/post_build.yml')); // remove duplicated steps 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 56b44315bf064..9d353b167ea47 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 @@ -16,6 +16,9 @@ export JOB=kibana-security-solution-chrome buildkite-agent meta-data set "${BUILDKITE_JOB_ID}_is_test_execution_step" "true" +mkdir .ftr +retry 5 5 vault kv get -format=json -field=data secret/kibana-issues/dev/security-quality-gate/role-users > .ftr/role_users.json + cd x-pack/test/security_solution_cypress set +e diff --git a/.buildkite/scripts/serverless/create_deploy_tag/collect_commit_info.ts b/.buildkite/scripts/serverless/create_deploy_tag/collect_commit_info.ts index ce30a3a71d8ee..5f4957f771d45 100644 --- a/.buildkite/scripts/serverless/create_deploy_tag/collect_commit_info.ts +++ b/.buildkite/scripts/serverless/create_deploy_tag/collect_commit_info.ts @@ -13,6 +13,7 @@ import { getSelectedCommitHash, getCommitByHash, makeCommitInfoHtml, + getRecentCommits, } from './info_sections/commit_info'; import { getArtifactBuild, @@ -45,7 +46,8 @@ async function main() { const buildkiteBuild = await getOnMergePRBuild(selectedSha); const nextBuildContainingCommit = await getQAFBuildContainingCommit( selectedSha, - selectedCommitInfo.date! + selectedCommitInfo.date!, + await getRecentCommits(50) ); const artifactBuild = await getArtifactBuild(selectedSha); addBuildkiteInfoSection( diff --git a/.buildkite/scripts/serverless/create_deploy_tag/create_deploy_tag.sh b/.buildkite/scripts/serverless/create_deploy_tag/create_deploy_tag.sh index b0d7660054bce..87c197142a9bb 100755 --- a/.buildkite/scripts/serverless/create_deploy_tag/create_deploy_tag.sh +++ b/.buildkite/scripts/serverless/create_deploy_tag/create_deploy_tag.sh @@ -11,9 +11,12 @@ fi echo "--- Creating deploy tag $DEPLOY_TAG at $KIBANA_COMMIT_SHA" +echo "Fetching user identity from GitHub..." +IDENTITY_JSON=$(ts-node .buildkite/scripts/serverless/create_deploy_tag/get_github_identity.ts) + # Set git identity to whomever triggered the buildkite job -git config user.email "$BUILDKITE_BUILD_CREATOR_EMAIL" -git config user.name "$BUILDKITE_BUILD_CREATOR" +git config user.email "${BUILDKITE_BUILD_CREATOR_EMAIL:-$(echo ${IDENTITY_JSON} | jq .email)}" +git config user.name "${BUILDKITE_BUILD_CREATOR:-$(echo ${IDENTITY_JSON} | jq .name)}" # Create a tag for the deploy git tag -a "$DEPLOY_TAG" "$KIBANA_COMMIT_SHA" \ diff --git a/.buildkite/scripts/serverless/create_deploy_tag/get_github_identity.ts b/.buildkite/scripts/serverless/create_deploy_tag/get_github_identity.ts new file mode 100644 index 0000000000000..f8179b4f0a37a --- /dev/null +++ b/.buildkite/scripts/serverless/create_deploy_tag/get_github_identity.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +// Query the GitHub API for the user's GitHub identity +import { octokit } from './shared'; + +const getGitHubIdentity = async (): Promise<{ + login: string; + name: string | null; + email: string | null; +}> => { + const { data } = await octokit.users.getAuthenticated(); + + return { + login: data.login, + name: data.name, + email: data.email, + }; +}; + +async function main() { + try { + const identity = await getGitHubIdentity(); + + if (!identity.name) { + identity.name = identity.login; + } + + if (!identity.email) { + identity.email = `${identity.login}@elastic.co`; + } + + return identity; + } catch (error) { + console.error(error); + + return { + login: 'unknown-user', + name: 'Unknown User', + email: 'unknown-user@elastic.co', + }; + } +} + +if (require.main === module) { + main().then((identity) => { + console.log(JSON.stringify(identity)); + }); +} else { + module.exports = main; +} diff --git a/.buildkite/scripts/serverless/create_deploy_tag/info_sections/build_info.ts b/.buildkite/scripts/serverless/create_deploy_tag/info_sections/build_info.ts index 7330458703546..0315d59ec9ca4 100644 --- a/.buildkite/scripts/serverless/create_deploy_tag/info_sections/build_info.ts +++ b/.buildkite/scripts/serverless/create_deploy_tag/info_sections/build_info.ts @@ -6,8 +6,8 @@ * Side Public License, v 1. */ -import { components } from '@octokit/openapi-types'; -import { buildkite, buildkiteBuildStateToEmoji, CommitWithStatuses, octokit } from '../shared'; +import { buildkite, buildkiteBuildStateToEmoji, CommitWithStatuses } from '../shared'; +import { GitCommitExtract } from './commit_info'; import { Build } from '#pipeline-utils/buildkite'; const QA_FTR_TEST_SLUG = 'appex-qa-serverless-kibana-ftr-tests'; @@ -70,27 +70,30 @@ export async function getArtifactBuild(commitSha: string): Promise { - // List of commits - const commitShaList = await getCommitListCached(); - // List of QAF builds const qafBuilds = await buildkite.getBuildsAfterDate(QA_FTR_TEST_SLUG, date, 30); + qafBuilds.reverse(); // Find the first build that contains this commit - const build = qafBuilds.find((kbBuild) => { - // Check if build.commit is after commitSha? - const kibanaCommitSha = tryGetKibanaBuildHashFromQAFBuild(kbBuild); - const buildkiteBuildShaIndex = commitShaList.findIndex((c) => c.sha === kibanaCommitSha); - const commitShaIndex = commitShaList.findIndex((c) => c.sha === commitSha); - - return ( - commitShaIndex !== -1 && - buildkiteBuildShaIndex !== -1 && - buildkiteBuildShaIndex < commitShaIndex - ); - }); + const build = qafBuilds + // Only search across scheduled builds, triggered builds might run with different commits + .filter((e) => e.source === 'schedule') + .find((kbBuild) => { + const commitShaIndex = recentGitCommits.findIndex((c) => c.sha === commitSha); + + const kibanaCommitOfFTR = tryGetKibanaBuildHashFromQAFBuild(kbBuild); + const buildkiteBuildShaIndex = recentGitCommits.findIndex((c) => c.sha === kibanaCommitOfFTR); + + // Check if build.commit is after commitSha? + return ( + commitShaIndex !== -1 && + buildkiteBuildShaIndex !== -1 && + buildkiteBuildShaIndex <= commitShaIndex + ); + }); if (!build) { return null; @@ -121,25 +124,6 @@ function tryGetKibanaBuildHashFromQAFBuild(build: Build) { } } -let _commitListCache: Array | null = null; -async function getCommitListCached() { - if (!_commitListCache) { - const resp = await octokit.request<'GET /repos/{owner}/{repo}/commits'>( - 'GET /repos/{owner}/{repo}/commits', - { - owner: 'elastic', - repo: 'kibana', - headers: { - accept: 'application/vnd.github.v3+json', - 'X-GitHub-Api-Version': '2022-11-28', - }, - } - ); - _commitListCache = resp.data; - } - return _commitListCache; -} - function makeBuildInfoSnippetHtml(name: string, build: BuildkiteBuildExtract | null) { if (!build) { return `[❓] ${name} - no build found`; diff --git a/.buildkite/scripts/serverless/create_deploy_tag/list_commit_candidates.ts b/.buildkite/scripts/serverless/create_deploy_tag/list_commit_candidates.ts index 7b20174200096..8245b195789fe 100755 --- a/.buildkite/scripts/serverless/create_deploy_tag/list_commit_candidates.ts +++ b/.buildkite/scripts/serverless/create_deploy_tag/list_commit_candidates.ts @@ -83,7 +83,7 @@ async function enrichWithStatuses(commits: GitCommitExtract[]): Promise { })(); const makeMockExec = (id: string) => { - console.warn("--- Using mock exec, don't use this on CI. ---"); + if (process.env?.CI?.match(/(1|true)/i)) { + throw new Error(`Mock exec is not supported in CI - your commands won't be executed.`); + } + const callStorage = getCallStorage(); const calls = callStorage[id]; diff --git a/.buildkite/scripts/steps/artifacts/trigger.sh b/.buildkite/scripts/steps/artifacts/trigger.sh index aac0c7166bf94..15c93c4c68a0d 100755 --- a/.buildkite/scripts/steps/artifacts/trigger.sh +++ b/.buildkite/scripts/steps/artifacts/trigger.sh @@ -12,9 +12,5 @@ echo "--- Trigger artifact builds" if [ "$BEATS_MANIFEST_LATEST_URL" = "$KIBANA_BEATS_MANIFEST_URL" ]; then echo "Kibana has the latest version of beats, skipping trigger" else - # Staging builds are not necessary on main - if [[ "$BUILDKITE_BRANCH" != "main" ]]; then - ts-node .buildkite/scripts/steps/trigger_pipeline.ts kibana-artifacts-staging "$BUILDKITE_BRANCH" - fi - ts-node .buildkite/scripts/steps/trigger_pipeline.ts kibana-artifacts-snapshot "$BUILDKITE_BRANCH" + ts-node .buildkite/scripts/steps/trigger_pipeline.ts kibana-artifacts-staging "$BUILDKITE_BRANCH" fi diff --git a/.buildkite/scripts/steps/functional/security_serverless.sh b/.buildkite/scripts/steps/functional/security_serverless_entity_analytics.sh similarity index 67% rename from .buildkite/scripts/steps/functional/security_serverless.sh rename to .buildkite/scripts/steps/functional/security_serverless_entity_analytics.sh index 9903f44da0373..0c3822e149d53 100644 --- a/.buildkite/scripts/steps/functional/security_serverless.sh +++ b/.buildkite/scripts/steps/functional/security_serverless_entity_analytics.sh @@ -8,9 +8,9 @@ source .buildkite/scripts/steps/functional/common_cypress.sh export JOB=kibana-serverless-security-cypress export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION} -echo "--- Security Serverless Cypress Tests" +echo "--- Entity Analytics Cypress Tests on Serverless" cd x-pack/test/security_solution_cypress set +e -yarn cypress:run:serverless; status=$?; yarn junit:merge || :; exit $status +yarn cypress:entity_analytics:run:serverless; status=$?; yarn junit:merge || :; exit $status diff --git a/.buildkite/scripts/steps/functional/security_solution.sh b/.buildkite/scripts/steps/functional/security_solution_entity_analytics.sh similarity index 66% rename from .buildkite/scripts/steps/functional/security_solution.sh rename to .buildkite/scripts/steps/functional/security_solution_entity_analytics.sh index fdba86f4dee4e..09f44d4c6b7ab 100755 --- a/.buildkite/scripts/steps/functional/security_solution.sh +++ b/.buildkite/scripts/steps/functional/security_solution_entity_analytics.sh @@ -8,9 +8,9 @@ source .buildkite/scripts/steps/functional/common_cypress.sh export JOB=kibana-security-solution-chrome export KIBANA_INSTALL_DIR=${KIBANA_BUILD_LOCATION} -echo "--- Security Solution Cypress tests (Chrome)" +echo "--- Entity Analytics - Security Solution Cypress Tests" cd x-pack/test/security_solution_cypress set +e -yarn cypress:run:ess; status=$?; yarn junit:merge || :; exit $status +yarn cypress:entity_analytics:run:ess; status=$?; yarn junit:merge || :; exit $status diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 597fbc6cd6944..9ec0cc7fab0a4 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -56,6 +56,7 @@ packages/kbn-babel-register @elastic/kibana-operations packages/kbn-babel-transform @elastic/kibana-operations x-pack/plugins/banners @elastic/appex-sharedux packages/kbn-bazel-runner @elastic/kibana-operations +packages/kbn-bfetch-error @elastic/appex-sharedux examples/bfetch_explorer @elastic/appex-sharedux src/plugins/bfetch @elastic/appex-sharedux packages/kbn-calculate-auto @elastic/obs-ux-management-team @@ -86,6 +87,7 @@ x-pack/plugins/cloud_integrations/cloud_links @elastic/kibana-core x-pack/plugins/cloud @elastic/kibana-core x-pack/plugins/cloud_security_posture @elastic/kibana-cloud-security-posture packages/shared-ux/code_editor @elastic/appex-sharedux +packages/kbn-code-owners @elastic/appex-qa packages/kbn-coloring @elastic/kibana-visualizations packages/kbn-config @elastic/kibana-core packages/kbn-config-mocks @elastic/kibana-core @@ -378,6 +380,7 @@ packages/kbn-eslint-plugin-eslint @elastic/kibana-operations packages/kbn-eslint-plugin-i18n @elastic/obs-knowledge-team @elastic/kibana-operations packages/kbn-eslint-plugin-imports @elastic/kibana-operations packages/kbn-eslint-plugin-telemetry @elastic/obs-knowledge-team +examples/eso_model_version_example @elastic/kibana-security x-pack/test/encrypted_saved_objects_api_integration/plugins/api_consumer_plugin @elastic/kibana-security packages/kbn-event-annotation-common @elastic/kibana-visualizations packages/kbn-event-annotation-components @elastic/kibana-visualizations @@ -423,6 +426,7 @@ packages/kbn-flot-charts @elastic/kibana-operations x-pack/test/ui_capabilities/common/plugins/foo_plugin @elastic/kibana-security src/plugins/ftr_apis @elastic/kibana-core packages/kbn-ftr-common-functional-services @elastic/kibana-operations @elastic/appex-qa +packages/kbn-ftr-common-functional-ui-services @elastic/appex-qa packages/kbn-ftr-screenshot-filename @elastic/kibana-operations @elastic/appex-qa x-pack/test/functional_with_es_ssl/plugins/cases @elastic/response-ops x-pack/examples/gen_ai_streaming_response_example @elastic/response-ops @@ -624,6 +628,7 @@ x-pack/test/plugin_functional/plugins/resolver_test @elastic/security-solution examples/response_stream @elastic/ml-ui packages/kbn-rison @elastic/kibana-operations x-pack/plugins/rollup @elastic/platform-deployment-management +packages/kbn-router-utils @elastic/obs-ux-logs-team examples/routing_example @elastic/kibana-core packages/kbn-rrule @elastic/response-ops packages/kbn-rule-data-utils @elastic/security-detections-response @elastic/response-ops @elastic/obs-ux-management-team @@ -650,6 +655,7 @@ x-pack/examples/screenshotting_example @elastic/appex-sharedux x-pack/plugins/screenshotting @elastic/kibana-reporting-services packages/kbn-search-api-panels @elastic/enterprise-search-frontend packages/kbn-search-connectors @elastic/enterprise-search-frontend +packages/kbn-search-errors @elastic/kibana-data-discovery examples/search_examples @elastic/kibana-data-discovery packages/kbn-search-index-documents @elastic/enterprise-search-frontend packages/kbn-search-response-warnings @elastic/kibana-data-discovery @@ -688,7 +694,7 @@ packages/kbn-securitysolution-rules @elastic/security-detection-engine packages/kbn-securitysolution-t-grid @elastic/security-detection-engine packages/kbn-securitysolution-utils @elastic/security-detection-engine packages/kbn-server-http-tools @elastic/kibana-core -packages/kbn-server-route-repository @elastic/obs-knowledge-team @elastic/obs-ux-management-team +packages/kbn-server-route-repository @elastic/obs-knowledge-team x-pack/plugins/serverless @elastic/appex-sharedux packages/serverless/settings/common @elastic/appex-sharedux @elastic/platform-deployment-management x-pack/plugins/serverless_observability @elastic/appex-sharedux @elastic/obs-ux-management-team @@ -851,6 +857,7 @@ src/plugins/vis_types/vega @elastic/kibana-visualizations src/plugins/vis_types/vislib @elastic/kibana-visualizations src/plugins/vis_types/xy @elastic/kibana-visualizations packages/kbn-visualization-ui-components @elastic/kibana-visualizations +packages/kbn-visualization-utils @elastic/kibana-visualizations src/plugins/visualizations @elastic/kibana-visualizations x-pack/plugins/watcher @elastic/platform-deployment-management packages/kbn-web-worker-stub @elastic/kibana-operations @@ -1247,8 +1254,37 @@ x-pack/plugins/cloud_integrations/cloud_full_story/server/config.ts @elastic/kib # AI assistant in Security Solution tests /x-pack/test/security_solution_cypress/cypress/e2e/ai_assistant @elastic/security-threat-hunting-investigations @elastic/security-detection-rule-management +# Security Solution cross teams ownership +/x-pack/test/security_solution_cypress/cypress/fixtures @elastic/security-detections-response @elastic/security-threat-hunting +/x-pack/test/security_solution_cypress/cypress/helpers @elastic/security-detections-response @elastic/security-threat-hunting +/x-pack/test/security_solution_cypress/cypress/objects @elastic/security-detections-response @elastic/security-threat-hunting +/x-pack/test/security_solution_cypress/cypress/plugins @elastic/security-detections-response @elastic/security-threat-hunting +/x-pack/test/security_solution_cypress/cypress/screens/common @elastic/security-detections-response @elastic/security-threat-hunting +/x-pack/test/security_solution_cypress/cypress/support @elastic/security-detections-response @elastic/security-threat-hunting +/x-pack/test/security_solution_cypress/cypress/urls @elastic/security-threat-hunting-investigations @elastic/security-detection-engine + +/x-pack/plugins/security_solution/common/ecs @elastic/security-threat-hunting-investigations +/x-pack/plugins/security_solution/common/test @elastic/security-detections-response @elastic/security-threat-hunting + +/x-pack/plugins/security_solution/public/common/components/callouts @elastic/security-detections-response +/x-pack/plugins/security_solution/public/common/components/hover_actions @elastic/security-threat-hunting-explore @elastic/security-threat-hunting-investigations + +/x-pack/plugins/security_solution/server/routes @elastic/security-detections-response @elastic/security-threat-hunting +/x-pack/plugins/security_solution/server/utils @elastic/security-detections-response @elastic/security-threat-hunting +x-pack/test/security_solution_api_integration/test_suites/detections_response/utils @elastic/security-detections-response +x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/telemetry @elastic/security-detections-response + # Security Solution sub teams +## Security Solution sub teams - security-engineering-productivity +## NOTE: It's important to keep this above other teams' sections because test automation doesn't process +## the CODEOWNERS file correctly. See https://github.com/elastic/kibana/issues/173307#issuecomment-1855858929 +/x-pack/test/security_solution_cypress/* @elastic/security-engineering-productivity +/x-pack/test/security_solution_cypress/cypress/* @elastic/security-engineering-productivity +/x-pack/test/security_solution_cypress/cypress/tasks/login.ts @elastic/security-engineering-productivity +/x-pack/test/security_solution_cypress/es_archives @elastic/security-engineering-productivity +/x-pack/plugins/security_solution/scripts/run_cypress @MadameSheema @patrykkopycinski @oatkiller @maximpn @banderror + ## Security Solution sub teams - Threat Hunting Investigations /x-pack/plugins/security_solution/common/api/timeline @elastic/security-threat-hunting-investigations @@ -1340,6 +1376,14 @@ x-pack/test/security_solution_cypress/cypress/tasks/expandable_flyout @elastic/ /x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/prebuilt_rules @elastic/security-detection-rule-management /x-pack/plugins/security_solution/docs/testing/test_plans/detection_response/rule_management @elastic/security-detection-rule-management /x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/prebuilt_rules @elastic/security-detection-rule-management +/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_delete @elastic/security-detection-rule-management +x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_update @elastic/security-detection-rule-management +/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_patch @elastic/security-detection-rule-management +/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_import_export @elastic/security-detection-rule-management +/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_management @elastic/security-detection-rule-management +/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_bulk_actions @elastic/security-detection-rule-management +/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_read @elastic/security-detection-rule-management +/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_creation/create_rules_bulk.ts @elastic/security-detection-rule-management /x-pack/plugins/security_solution/public/common/components/health_truncate_text @elastic/security-detection-rule-management /x-pack/plugins/security_solution/public/common/components/links_to_docs @elastic/security-detection-rule-management @@ -1396,39 +1440,28 @@ x-pack/test/security_solution_cypress/cypress/tasks/expandable_flyout @elastic/ /x-pack/plugins/security_solution/server/lib/detection_engine/routes/signals @elastic/security-detection-engine /x-pack/plugins/security_solution/server/lib/sourcerer @elastic/security-detection-engine -/x-pack/test/security_solution_cypress/cypress/e2e/detection_engine @elastic/security-detection-engine +/x-pack/test/security_solution_cypress/cypress/e2e/detection_response/detection_engine @elastic/security-detection-engine + /x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/exceptions @elastic/security-detection-engine -/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_creation @elastic/security-detection-engine +/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_creation/create_new_terms @elastic/security-detection-engine +/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_creation/create_rules @elastic/security-detection-engine +/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_creation/preview_rules @elastic/security-detection-engine /x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/actions @elastic/security-detection-engine /x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/alerts @elastic/security-detection-engine /x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/user_roles @elastic/security-detection-engine /x-pack/test/security_solution_api_integration/test_suites/detections_response/basic_essentials_license/detection_engine @elastic/security-detection-engine /x-pack/plugins/security_solution/server/lib/detection_engine/scripts/roles_users @elastic/security-detection-engine /x-pack/test/security_solution_api_integration/test_suites/lists_and_exception_lists @elastic/security-detection-engine +x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_update/update_rules.ts @elastic/security-detection-engine +x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_update/update_rules_ess.ts @elastic/security-detection-engine +x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_patch/patch_rules.ts @elastic/security-detection-engine +x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_patch/patch_rules_ess.ts @elastic/security-detection-engine +/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_delete/delete_rules.ts @elastic/security-detection-engine +/x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_delete/delete_rules_ess.ts @elastic/security-detection-engine ## Security Threat Intelligence - Under Security Platform /x-pack/plugins/security_solution/public/common/components/threat_match @elastic/security-detection-engine -## Security Solution cross teams ownership -/x-pack/test/security_solution_cypress/cypress/fixtures @elastic/security-detections-response @elastic/security-threat-hunting -/x-pack/test/security_solution_cypress/cypress/helpers @elastic/security-detections-response @elastic/security-threat-hunting -/x-pack/test/security_solution_cypress/cypress/objects @elastic/security-detections-response @elastic/security-threat-hunting -/x-pack/test/security_solution_cypress/cypress/plugins @elastic/security-detections-response @elastic/security-threat-hunting -/x-pack/test/security_solution_cypress/cypress/screens/common @elastic/security-detections-response @elastic/security-threat-hunting -/x-pack/test/security_solution_cypress/cypress/support @elastic/security-detections-response @elastic/security-threat-hunting -/x-pack/test/security_solution_cypress/cypress/urls @elastic/security-threat-hunting-investigations @elastic/security-detection-engine - -/x-pack/plugins/security_solution/common/ecs @elastic/security-threat-hunting-investigations -/x-pack/plugins/security_solution/common/test @elastic/security-detections-response @elastic/security-threat-hunting - -/x-pack/plugins/security_solution/public/common/components/callouts @elastic/security-detections-response -/x-pack/plugins/security_solution/public/common/components/hover_actions @elastic/security-threat-hunting-explore @elastic/security-threat-hunting-investigations - -/x-pack/plugins/security_solution/server/routes @elastic/security-detections-response @elastic/security-threat-hunting -/x-pack/plugins/security_solution/server/utils @elastic/security-detections-response @elastic/security-threat-hunting -x-pack/test/security_solution_api_integration/test_suites/detections_response/utils @elastic/security-detections-response -x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/telemetry @elastic/security-detections-response - ## Security Solution sub teams - security-defend-workflows /x-pack/plugins/security_solution/public/management/ @elastic/security-defend-workflows /x-pack/plugins/security_solution/public/common/lib/endpoint*/ @elastic/security-defend-workflows @@ -1452,13 +1485,6 @@ x-pack/plugins/security_solution/server/usage/ @elastic/security-data-analytics x-pack/plugins/security_solution/server/lib/telemetry/ @elastic/security-data-analytics x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/telemetry @elastic/security-data-analytics -## Security Solution sub teams - security-engineering-productivity -/x-pack/test/security_solution_cypress/* @elastic/security-engineering-productivity -/x-pack/test/security_solution_cypress/cypress/* @elastic/security-engineering-productivity -/x-pack/test/security_solution_cypress/cypress/tasks/login.ts @elastic/security-engineering-productivity -/x-pack/test/security_solution_cypress/es_archives @elastic/security-engineering-productivity -/x-pack/plugins/security_solution/scripts/run_cypress @MadameSheema @patrykkopycinski @oatkiller @maximpn @banderror - ## Security Solution sub teams - adaptive-workload-protection x-pack/plugins/security_solution/public/common/components/sessions_viewer @elastic/kibana-cloud-security-posture x-pack/plugins/security_solution/public/kubernetes @elastic/kibana-cloud-security-posture @@ -1468,7 +1494,7 @@ x-pack/plugins/security_solution/public/threat_intelligence @elastic/protections x-pack/test/threat_intelligence_cypress @elastic/protections-experience ## Security Solution sub teams - Entity Analytics -x-pack/plugins/security_solution/common/risk_engine @elastic/security-entity-analytics @elastic/security-entity-analytics +x-pack/plugins/security_solution/common/entity_analytics @elastic/security-entity-analytics x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score @elastic/security-entity-analytics x-pack/plugins/security_solution/public/entity_analytics @elastic/security-entity-analytics x-pack/plugins/security_solution/public/explore/components/risk_score @elastic/security-entity-analytics @@ -1477,6 +1503,7 @@ x-pack/plugins/security_solution/public/overview/components/entity_analytics x-pack/plugins/security_solution/server/lib/entity_analytics @elastic/security-entity-analytics x-pack/plugins/security_solution/server/lib/risk_score @elastic/security-entity-analytics x-pack/test/security_solution_api_integration/test_suites/entity_analytics @elastic/security-entity-analytics +x-pack/test/security_solution_cypress/cypress/e2e/entity_analytics @elastic/security-entity-analytics x-pack/plugins/security_solution/public/flyout/entity_details @elastic/security-entity-analytics x-pack/plugins/security_solution/common/api/entity_analytics @elastic/security-entity-analytics /x-pack/plugins/security_solution/public/entity_analytics @elastic/security-entity-analytics diff --git a/.github/paths-labeller.yml b/.github/paths-labeller.yml index 4f4057935265a..5f1ac0401ab31 100644 --- a/.github/paths-labeller.yml +++ b/.github/paths-labeller.yml @@ -8,18 +8,17 @@ - "Feature:ExpressionLanguage": - "src/plugins/expressions/**/*.*" - "src/plugins/bfetch/**/*.*" - - "Team:apm": + - "Team:obs-ux-infra_services": - "x-pack/plugins/apm/**/*.*" - "x-pack/test/apm_api_integration/**/*.*" - "packages/kbn-apm-synthtrace/**/*.*" - "packages/kbn-apm-synthtrace-client/**/*.*" - "packages/kbn-apm-utils/**/*.*" - - "Team:Fleet": - - "x-pack/plugins/fleet/**/*.*" - - "x-pack/test/fleet_api_integration/**/*.*" - - "Team:uptime": - "x-pack/plugins/synthetics/**/*.*" - "x-pack/plugins/ux/**/*.*" - "x-pack/plugins/observability/public/components/shared/exploratory_view/**/*.*" + - "Team:Fleet": + - "x-pack/plugins/fleet/**/*.*" + - "x-pack/test/fleet_api_integration/**/*.*" - "Team:obs-ux-management": - "x-pack/plugins/observability/**/*.*" diff --git a/.i18nrc.json b/.i18nrc.json index 2869094e7e152..1af130820cd36 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -9,6 +9,7 @@ "apmOss": "src/plugins/apm_oss", "autocomplete": "packages/kbn-securitysolution-autocomplete/src", "bfetch": "src/plugins/bfetch", + "bfetchError": "packages/kbn-bfetch-error", "cases": ["packages/kbn-cases-components"], "cellActions": "packages/kbn-cell-actions", "charts": "src/plugins/charts", @@ -102,6 +103,7 @@ "share": "src/plugins/share", "sharedUXPackages": "packages/shared-ux", "searchApiPanels": "packages/kbn-search-api-panels/", + "searchErrors": "packages/kbn-search-errors", "searchIndexDocuments": "packages/kbn-search-index-documents", "searchResponseWarnings": "packages/kbn-search-response-warnings", "securitySolutionPackages": "x-pack/packages/security-solution", diff --git a/api_docs/actions.mdx b/api_docs/actions.mdx index 5d8edf115f9ca..4da6a11339828 100644 --- a/api_docs/actions.mdx +++ b/api_docs/actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/actions title: "actions" image: https://source.unsplash.com/400x175/?github description: API docs for the actions plugin -date: 2023-12-06 +date: 2023-12-19 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 90d281c4e9f4c..8b8ab9242a004 100644 --- a/api_docs/advanced_settings.mdx +++ b/api_docs/advanced_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/advancedSettings title: "advancedSettings" image: https://source.unsplash.com/400x175/?github description: API docs for the advancedSettings plugin -date: 2023-12-06 +date: 2023-12-19 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 99ead7eef17fe..4c7b626dd4597 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: 2023-12-06 +date: 2023-12-19 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 b9bc5d05c9b93..8b2ab206b57c0 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: 2023-12-06 +date: 2023-12-19 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 6649c83b16dfe..d8a0e3b50e293 100644 --- a/api_docs/aiops.devdocs.json +++ b/api_docs/aiops.devdocs.json @@ -587,8 +587,8 @@ "pluginId": "uiActions", "scope": "public", "docId": "kibUiActionsPluginApi", - "section": "def-public.UiActionsStart", - "text": "UiActionsStart" + "section": "def-public.UiActionsPublicStart", + "text": "UiActionsPublicStart" }, " | undefined" ], diff --git a/api_docs/aiops.mdx b/api_docs/aiops.mdx index faa3ba01ce406..0cbb8e2642cda 100644 --- a/api_docs/aiops.mdx +++ b/api_docs/aiops.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/aiops title: "aiops" image: https://source.unsplash.com/400x175/?github description: API docs for the aiops plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'aiops'] --- import aiopsObj from './aiops.devdocs.json'; diff --git a/api_docs/alerting.devdocs.json b/api_docs/alerting.devdocs.json index 106f1e26f709a..15f5fd2f156b5 100644 --- a/api_docs/alerting.devdocs.json +++ b/api_docs/alerting.devdocs.json @@ -3489,6 +3489,10 @@ "plugin": "observability", "path": "x-pack/plugins/observability/server/lib/rules/custom_threshold/custom_threshold_executor.ts" }, + { + "plugin": "observability", + "path": "x-pack/plugins/observability/server/lib/rules/custom_threshold/custom_threshold_executor.ts" + }, { "plugin": "observability", "path": "x-pack/plugins/observability/server/lib/rules/slo_burn_rate/executor.ts" @@ -3671,8 +3675,8 @@ "pluginId": "share", "scope": "server", "docId": "kibSharePluginApi", - "section": "def-server.SharePluginStart", - "text": "SharePluginStart" + "section": "def-server.SharePublicStart", + "text": "SharePublicStart" } ], "path": "x-pack/plugins/alerting/server/types.ts", @@ -4796,6 +4800,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "alerting", + "id": "def-server.RULE_SAVED_OBJECT_TYPE", + "type": "string", + "tags": [], + "label": "RULE_SAVED_OBJECT_TYPE", + "description": [], + "signature": [ + "\"alert\"" + ], + "path": "x-pack/plugins/alerting/server/saved_objects/index.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "alerting", "id": "def-server.RuleActionParams", diff --git a/api_docs/alerting.mdx b/api_docs/alerting.mdx index 051f27903fcba..d02d81e0cf53a 100644 --- a/api_docs/alerting.mdx +++ b/api_docs/alerting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/alerting title: "alerting" image: https://source.unsplash.com/400x175/?github description: API docs for the alerting plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'alerting'] --- import alertingObj from './alerting.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-o | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 822 | 1 | 791 | 51 | +| 823 | 1 | 792 | 51 | ## Client diff --git a/api_docs/apm.devdocs.json b/api_docs/apm.devdocs.json index c0893d3b78d0a..56fe4e2f68b03 100644 --- a/api_docs/apm.devdocs.json +++ b/api_docs/apm.devdocs.json @@ -418,7 +418,7 @@ "label": "APIEndpoint", "description": [], "signature": [ - "\"POST /internal/apm/data_view/static\" | \"GET /internal/apm/data_view/index_pattern\" | \"GET /internal/apm/environments\" | \"GET /internal/apm/services/{serviceName}/errors/groups/main_statistics\" | \"GET /internal/apm/services/{serviceName}/errors/groups/main_statistics_by_transaction_name\" | \"POST /internal/apm/services/{serviceName}/errors/groups/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/errors/{groupId}/samples\" | \"GET /internal/apm/services/{serviceName}/errors/{groupId}/error/{errorId}\" | \"GET /internal/apm/services/{serviceName}/errors/distribution\" | \"GET /internal/apm/services/{serviceName}/errors/{groupId}/top_erroneous_transactions\" | \"POST /internal/apm/latency/overall_distribution/transactions\" | \"GET /internal/apm/services/{serviceName}/metrics/charts\" | \"GET /internal/apm/services/{serviceName}/metrics/nodes\" | \"GET /internal/apm/services/{serviceName}/metrics/serverless/charts\" | \"GET /internal/apm/services/{serviceName}/metrics/serverless/summary\" | \"GET /internal/apm/services/{serviceName}/metrics/serverless/functions_overview\" | \"GET /internal/apm/services/{serviceName}/metrics/serverless/active_instances\" | \"GET /internal/apm/observability_overview\" | \"GET /internal/apm/observability_overview/has_data\" | \"GET /internal/apm/service-map\" | \"GET /internal/apm/service-map/service/{serviceName}\" | \"GET /internal/apm/service-map/dependency\" | \"GET /internal/apm/services\" | \"POST /internal/apm/services/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/metadata/details\" | \"GET /internal/apm/services/{serviceName}/metadata/icons\" | \"GET /internal/apm/services/{serviceName}/agent\" | \"GET /internal/apm/services/{serviceName}/transaction_types\" | \"GET /internal/apm/services/{serviceName}/node/{serviceNodeName}/metadata\" | \"GET /api/apm/services/{serviceName}/annotation/search 2023-10-31\" | \"POST /api/apm/services/{serviceName}/annotation 2023-10-31\" | \"GET /internal/apm/services/{serviceName}/service_overview_instances/details/{serviceNodeName}\" | \"GET /internal/apm/services/{serviceName}/throughput\" | \"GET /internal/apm/services/{serviceName}/service_overview_instances/main_statistics\" | \"GET /internal/apm/services/{serviceName}/service_overview_instances/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/dependencies\" | \"GET /internal/apm/services/{serviceName}/dependencies/breakdown\" | \"GET /internal/apm/services/{serviceName}/anomaly_charts\" | \"GET /internal/apm/services/{serviceName}/alerts_count\" | \"GET /internal/apm/service-groups\" | \"GET /internal/apm/service-group\" | \"POST /internal/apm/service-group\" | \"DELETE /internal/apm/service-group\" | \"GET /internal/apm/service-group/services\" | \"GET /internal/apm/service-group/counts\" | \"GET /internal/apm/suggestions\" | \"GET /internal/apm/traces/{traceId}\" | \"GET /internal/apm/traces\" | \"GET /internal/apm/traces/{traceId}/root_transaction\" | \"GET /internal/apm/transactions/{transactionId}\" | \"GET /internal/apm/traces/find\" | \"POST /internal/apm/traces/aggregated_critical_path\" | \"GET /internal/apm/traces/{traceId}/transactions/{transactionId}\" | \"GET /internal/apm/traces/{traceId}/spans/{spanId}\" | \"GET /internal/apm/services/{serviceName}/transactions/groups/main_statistics\" | \"GET /internal/apm/services/{serviceName}/transactions/groups/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/latency\" | \"GET /internal/apm/services/{serviceName}/transactions/traces/samples\" | \"GET /internal/apm/services/{serviceName}/transaction/charts/breakdown\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/error_rate\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/coldstart_rate\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/coldstart_rate_by_transaction_name\" | \"GET /internal/apm/rule_types/transaction_error_rate/chart_preview\" | \"GET /internal/apm/rule_types/error_count/chart_preview\" | \"GET /internal/apm/rule_types/transaction_duration/chart_preview\" | \"GET /api/apm/settings/agent-configuration 2023-10-31\" | \"GET /api/apm/settings/agent-configuration/view 2023-10-31\" | \"DELETE /api/apm/settings/agent-configuration 2023-10-31\" | \"PUT /api/apm/settings/agent-configuration 2023-10-31\" | \"POST /api/apm/settings/agent-configuration/search 2023-10-31\" | \"GET /api/apm/settings/agent-configuration/environments 2023-10-31\" | \"GET /api/apm/settings/agent-configuration/agent_name 2023-10-31\" | \"GET /internal/apm/settings/anomaly-detection/jobs\" | \"POST /internal/apm/settings/anomaly-detection/jobs\" | \"GET /internal/apm/settings/anomaly-detection/environments\" | \"POST /internal/apm/settings/anomaly-detection/update_to_v3\" | \"GET /internal/apm/settings/apm-index-settings\" | \"GET /internal/apm/settings/apm-indices\" | \"POST /internal/apm/settings/apm-indices/save\" | \"GET /internal/apm/settings/custom_links/transaction\" | \"GET /internal/apm/settings/custom_links\" | \"POST /internal/apm/settings/custom_links\" | \"PUT /internal/apm/settings/custom_links/{id}\" | \"DELETE /internal/apm/settings/custom_links/{id}\" | \"GET /api/apm/sourcemaps 2023-10-31\" | \"POST /api/apm/sourcemaps 2023-10-31\" | \"DELETE /api/apm/sourcemaps/{id} 2023-10-31\" | \"POST /internal/apm/sourcemaps/migrate_fleet_artifacts\" | \"GET /internal/apm/fleet/has_apm_policies\" | \"GET /internal/apm/fleet/agents\" | \"POST /api/apm/fleet/apm_server_schema 2023-10-31\" | \"GET /internal/apm/fleet/apm_server_schema/unsupported\" | \"GET /internal/apm/fleet/migration_check\" | \"POST /internal/apm/fleet/cloud_apm_package_policy\" | \"GET /internal/apm/fleet/java_agent_versions\" | \"GET /internal/apm/dependencies/top_dependencies\" | \"GET /internal/apm/dependencies/upstream_services\" | \"GET /internal/apm/dependencies/metadata\" | \"GET /internal/apm/dependencies/charts/latency\" | \"GET /internal/apm/dependencies/charts/throughput\" | \"GET /internal/apm/dependencies/charts/error_rate\" | \"GET /internal/apm/dependencies/operations\" | \"GET /internal/apm/dependencies/charts/distribution\" | \"GET /internal/apm/dependencies/operations/spans\" | \"GET /internal/apm/correlations/field_candidates/transactions\" | \"GET /internal/apm/correlations/field_value_stats/transactions\" | \"POST /internal/apm/correlations/field_value_pairs/transactions\" | \"POST /internal/apm/correlations/significant_correlations/transactions\" | \"POST /internal/apm/correlations/p_values/transactions\" | \"GET /internal/apm/fallback_to_transactions\" | \"GET /internal/apm/has_data\" | \"GET /internal/apm/event_metadata/{processorEvent}/{id}\" | \"GET /internal/apm/agent_keys\" | \"GET /internal/apm/agent_keys/privileges\" | \"POST /internal/apm/api_key/invalidate\" | \"POST /api/apm/agent_keys 2023-10-31\" | \"GET /internal/apm/storage_explorer\" | \"GET /internal/apm/services/{serviceName}/storage_details\" | \"GET /internal/apm/storage_chart\" | \"GET /internal/apm/storage_explorer/privileges\" | \"GET /internal/apm/storage_explorer_summary_stats\" | \"GET /internal/apm/storage_explorer/is_cross_cluster_search\" | \"GET /internal/apm/storage_explorer/get_services\" | \"GET /internal/apm/traces/{traceId}/span_links/{spanId}/parents\" | \"GET /internal/apm/traces/{traceId}/span_links/{spanId}/children\" | \"GET /internal/apm/services/{serviceName}/infrastructure_attributes\" | \"GET /internal/apm/debug-telemetry\" | \"GET /internal/apm/time_range_metadata\" | \"GET /internal/apm/settings/labs\" | \"GET /internal/apm/get_agents_per_service\" | \"GET /internal/apm/get_latest_agent_versions\" | \"GET /internal/apm/services/{serviceName}/agent_instances\" | \"GET /internal/apm/mobile-services/{serviceName}/error/http_error_rate\" | \"GET /internal/apm/mobile-services/{serviceName}/errors/groups/main_statistics\" | \"POST /internal/apm/mobile-services/{serviceName}/errors/groups/detailed_statistics\" | \"GET /internal/apm/mobile-services/{serviceName}/error_terms\" | \"POST /internal/apm/mobile-services/{serviceName}/crashes/groups/detailed_statistics\" | \"GET /internal/apm/mobile-services/{serviceName}/crashes/groups/main_statistics\" | \"GET /internal/apm/mobile-services/{serviceName}/crashes/distribution\" | \"GET /internal/apm/services/{serviceName}/mobile/filters\" | \"GET /internal/apm/mobile-services/{serviceName}/most_used_charts\" | \"GET /internal/apm/mobile-services/{serviceName}/transactions/charts/sessions\" | \"GET /internal/apm/mobile-services/{serviceName}/transactions/charts/http_requests\" | \"GET /internal/apm/mobile-services/{serviceName}/stats\" | \"GET /internal/apm/mobile-services/{serviceName}/location/stats\" | \"GET /internal/apm/mobile-services/{serviceName}/terms\" | \"GET /internal/apm/mobile-services/{serviceName}/main_statistics\" | \"GET /internal/apm/mobile-services/{serviceName}/detailed_statistics\" | \"GET /internal/apm/diagnostics\" | \"POST /internal/apm/assistant/get_apm_timeseries\" | \"GET /internal/apm/assistant/get_service_summary\" | \"GET /internal/apm/assistant/get_error_document\" | \"POST /internal/apm/assistant/get_correlation_values\" | \"GET /internal/apm/assistant/get_downstream_dependencies\" | \"POST /internal/apm/assistant/get_services_list\" | \"GET /internal/apm/services/{serviceName}/profiling/flamegraph\" | \"GET /internal/apm/profiling/status\" | \"GET /internal/apm/services/{serviceName}/profiling/functions\" | \"POST /internal/apm/custom-dashboard\" | \"DELETE /internal/apm/custom-dashboard\" | \"GET /internal/apm/services/{serviceName}/dashboards\"" + "\"POST /internal/apm/data_view/static\" | \"GET /internal/apm/data_view/index_pattern\" | \"GET /internal/apm/environments\" | \"GET /internal/apm/services/{serviceName}/errors/groups/main_statistics\" | \"GET /internal/apm/services/{serviceName}/errors/groups/main_statistics_by_transaction_name\" | \"POST /internal/apm/services/{serviceName}/errors/groups/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/errors/{groupId}/samples\" | \"GET /internal/apm/services/{serviceName}/errors/{groupId}/error/{errorId}\" | \"GET /internal/apm/services/{serviceName}/errors/distribution\" | \"GET /internal/apm/services/{serviceName}/errors/{groupId}/top_erroneous_transactions\" | \"POST /internal/apm/latency/overall_distribution/transactions\" | \"GET /internal/apm/services/{serviceName}/metrics/charts\" | \"GET /internal/apm/services/{serviceName}/metrics/nodes\" | \"GET /internal/apm/services/{serviceName}/metrics/serverless/charts\" | \"GET /internal/apm/services/{serviceName}/metrics/serverless/summary\" | \"GET /internal/apm/services/{serviceName}/metrics/serverless/functions_overview\" | \"GET /internal/apm/services/{serviceName}/metrics/serverless/active_instances\" | \"GET /internal/apm/observability_overview\" | \"GET /internal/apm/observability_overview/has_data\" | \"GET /internal/apm/service-map\" | \"GET /internal/apm/service-map/service/{serviceName}\" | \"GET /internal/apm/service-map/dependency\" | \"GET /internal/apm/services\" | \"POST /internal/apm/services/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/metadata/details\" | \"GET /internal/apm/services/{serviceName}/metadata/icons\" | \"GET /internal/apm/services/{serviceName}/agent\" | \"GET /internal/apm/services/{serviceName}/transaction_types\" | \"GET /internal/apm/services/{serviceName}/node/{serviceNodeName}/metadata\" | \"GET /api/apm/services/{serviceName}/annotation/search 2023-10-31\" | \"POST /api/apm/services/{serviceName}/annotation 2023-10-31\" | \"GET /internal/apm/services/{serviceName}/service_overview_instances/details/{serviceNodeName}\" | \"GET /internal/apm/services/{serviceName}/throughput\" | \"GET /internal/apm/services/{serviceName}/service_overview_instances/main_statistics\" | \"GET /internal/apm/services/{serviceName}/service_overview_instances/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/dependencies\" | \"GET /internal/apm/services/{serviceName}/dependencies/breakdown\" | \"GET /internal/apm/services/{serviceName}/anomaly_charts\" | \"GET /internal/apm/services/{serviceName}/alerts_count\" | \"GET /internal/apm/service-groups\" | \"GET /internal/apm/service-group\" | \"POST /internal/apm/service-group\" | \"DELETE /internal/apm/service-group\" | \"GET /internal/apm/service-group/services\" | \"GET /internal/apm/service-group/counts\" | \"GET /internal/apm/suggestions\" | \"GET /internal/apm/traces/{traceId}\" | \"GET /internal/apm/traces\" | \"GET /internal/apm/traces/{traceId}/root_transaction\" | \"GET /internal/apm/transactions/{transactionId}\" | \"GET /internal/apm/traces/find\" | \"POST /internal/apm/traces/aggregated_critical_path\" | \"GET /internal/apm/traces/{traceId}/transactions/{transactionId}\" | \"GET /internal/apm/traces/{traceId}/spans/{spanId}\" | \"GET /internal/apm/services/{serviceName}/transactions/groups/main_statistics\" | \"GET /internal/apm/services/{serviceName}/transactions/groups/detailed_statistics\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/latency\" | \"GET /internal/apm/services/{serviceName}/transactions/traces/samples\" | \"GET /internal/apm/services/{serviceName}/transaction/charts/breakdown\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/error_rate\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/coldstart_rate\" | \"GET /internal/apm/services/{serviceName}/transactions/charts/coldstart_rate_by_transaction_name\" | \"GET /internal/apm/rule_types/transaction_error_rate/chart_preview\" | \"GET /internal/apm/rule_types/error_count/chart_preview\" | \"GET /internal/apm/rule_types/transaction_duration/chart_preview\" | \"GET /api/apm/settings/agent-configuration 2023-10-31\" | \"GET /api/apm/settings/agent-configuration/view 2023-10-31\" | \"DELETE /api/apm/settings/agent-configuration 2023-10-31\" | \"PUT /api/apm/settings/agent-configuration 2023-10-31\" | \"POST /api/apm/settings/agent-configuration/search 2023-10-31\" | \"GET /api/apm/settings/agent-configuration/environments 2023-10-31\" | \"GET /api/apm/settings/agent-configuration/agent_name 2023-10-31\" | \"GET /internal/apm/settings/anomaly-detection/jobs\" | \"POST /internal/apm/settings/anomaly-detection/jobs\" | \"GET /internal/apm/settings/anomaly-detection/environments\" | \"POST /internal/apm/settings/anomaly-detection/update_to_v3\" | \"GET /internal/apm/settings/apm-index-settings\" | \"GET /internal/apm/settings/apm-indices\" | \"POST /internal/apm/settings/apm-indices/save\" | \"GET /internal/apm/settings/custom_links/transaction\" | \"GET /internal/apm/settings/custom_links\" | \"POST /internal/apm/settings/custom_links\" | \"PUT /internal/apm/settings/custom_links/{id}\" | \"DELETE /internal/apm/settings/custom_links/{id}\" | \"GET /api/apm/sourcemaps 2023-10-31\" | \"POST /api/apm/sourcemaps 2023-10-31\" | \"DELETE /api/apm/sourcemaps/{id} 2023-10-31\" | \"POST /internal/apm/sourcemaps/migrate_fleet_artifacts\" | \"GET /internal/apm/fleet/has_apm_policies\" | \"GET /internal/apm/fleet/agents\" | \"POST /api/apm/fleet/apm_server_schema 2023-10-31\" | \"GET /internal/apm/fleet/apm_server_schema/unsupported\" | \"GET /internal/apm/fleet/migration_check\" | \"POST /internal/apm/fleet/cloud_apm_package_policy\" | \"GET /internal/apm/fleet/java_agent_versions\" | \"GET /internal/apm/dependencies/top_dependencies\" | \"GET /internal/apm/dependencies/upstream_services\" | \"GET /internal/apm/dependencies/metadata\" | \"GET /internal/apm/dependencies/charts/latency\" | \"GET /internal/apm/dependencies/charts/throughput\" | \"GET /internal/apm/dependencies/charts/error_rate\" | \"GET /internal/apm/dependencies/operations\" | \"GET /internal/apm/dependencies/charts/distribution\" | \"GET /internal/apm/dependencies/operations/spans\" | \"GET /internal/apm/correlations/field_candidates/transactions\" | \"GET /internal/apm/correlations/field_value_stats/transactions\" | \"POST /internal/apm/correlations/field_value_pairs/transactions\" | \"POST /internal/apm/correlations/significant_correlations/transactions\" | \"POST /internal/apm/correlations/p_values/transactions\" | \"GET /internal/apm/fallback_to_transactions\" | \"GET /internal/apm/has_data\" | \"GET /internal/apm/event_metadata/{processorEvent}/{id}\" | \"GET /internal/apm/agent_keys\" | \"GET /internal/apm/agent_keys/privileges\" | \"POST /internal/apm/api_key/invalidate\" | \"POST /api/apm/agent_keys 2023-10-31\" | \"GET /internal/apm/storage_explorer\" | \"GET /internal/apm/services/{serviceName}/storage_details\" | \"GET /internal/apm/storage_chart\" | \"GET /internal/apm/storage_explorer/privileges\" | \"GET /internal/apm/storage_explorer_summary_stats\" | \"GET /internal/apm/storage_explorer/is_cross_cluster_search\" | \"GET /internal/apm/storage_explorer/get_services\" | \"GET /internal/apm/traces/{traceId}/span_links/{spanId}/parents\" | \"GET /internal/apm/traces/{traceId}/span_links/{spanId}/children\" | \"GET /internal/apm/services/{serviceName}/infrastructure_attributes\" | \"GET /internal/apm/debug-telemetry\" | \"GET /internal/apm/time_range_metadata\" | \"GET /internal/apm/settings/labs\" | \"GET /internal/apm/get_agents_per_service\" | \"GET /internal/apm/get_latest_agent_versions\" | \"GET /internal/apm/services/{serviceName}/agent_instances\" | \"GET /internal/apm/mobile-services/{serviceName}/error/http_error_rate\" | \"GET /internal/apm/mobile-services/{serviceName}/errors/groups/main_statistics\" | \"POST /internal/apm/mobile-services/{serviceName}/errors/groups/detailed_statistics\" | \"GET /internal/apm/mobile-services/{serviceName}/error_terms\" | \"POST /internal/apm/mobile-services/{serviceName}/crashes/groups/detailed_statistics\" | \"GET /internal/apm/mobile-services/{serviceName}/crashes/groups/main_statistics\" | \"GET /internal/apm/mobile-services/{serviceName}/crashes/distribution\" | \"GET /internal/apm/services/{serviceName}/mobile/filters\" | \"GET /internal/apm/mobile-services/{serviceName}/most_used_charts\" | \"GET /internal/apm/mobile-services/{serviceName}/transactions/charts/sessions\" | \"GET /internal/apm/mobile-services/{serviceName}/transactions/charts/http_requests\" | \"GET /internal/apm/mobile-services/{serviceName}/stats\" | \"GET /internal/apm/mobile-services/{serviceName}/location/stats\" | \"GET /internal/apm/mobile-services/{serviceName}/terms\" | \"GET /internal/apm/mobile-services/{serviceName}/main_statistics\" | \"GET /internal/apm/mobile-services/{serviceName}/detailed_statistics\" | \"GET /internal/apm/diagnostics\" | \"POST /internal/apm/assistant/get_apm_timeseries\" | \"GET /internal/apm/assistant/get_service_summary\" | \"POST /internal/apm/assistant/get_correlation_values\" | \"GET /internal/apm/assistant/get_downstream_dependencies\" | \"GET /internal/apm/services/{serviceName}/profiling/flamegraph\" | \"GET /internal/apm/profiling/status\" | \"GET /internal/apm/services/{serviceName}/profiling/functions\" | \"POST /internal/apm/custom-dashboard\" | \"DELETE /internal/apm/custom-dashboard\" | \"GET /internal/apm/services/{serviceName}/dashboards\"" ], "path": "x-pack/plugins/apm/server/routes/apm_routes/get_global_apm_server_route_repository.ts", "deprecated": false, @@ -737,46 +737,6 @@ }, "; hostNames: string[]; } | undefined>; } & ", "APMRouteCreateOptions", - "; \"POST /internal/apm/assistant/get_services_list\": { endpoint: \"POST /internal/apm/assistant/get_services_list\"; params?: ", - "TypeC", - "<{ body: ", - "IntersectionC", - "<[", - "TypeC", - "<{ start: ", - "StringC", - "; end: ", - "StringC", - "; }>, ", - "PartialC", - "<{ 'service.environment': ", - "StringC", - "; healthStatus: ", - "ArrayC", - "<", - "UnionC", - "<[", - "LiteralC", - "<", - "ServiceHealthStatus", - ".unknown>, ", - "LiteralC", - "<", - "ServiceHealthStatus", - ".healthy>, ", - "LiteralC", - "<", - "ServiceHealthStatus", - ".warning>, ", - "LiteralC", - "<", - "ServiceHealthStatus", - ".critical>]>>; }>]>; }> | undefined; handler: ({}: ", - "APMRouteHandlerResources", - " & { params: { body: { start: string; end: string; } & { 'service.environment'?: string | undefined; healthStatus?: ", - "ServiceHealthStatus", - "[] | undefined; }; }; }) => Promise<{ content: ApmServicesListContent; }>; } & ", - "APMRouteCreateOptions", "; \"GET /internal/apm/assistant/get_downstream_dependencies\": { endpoint: \"GET /internal/apm/assistant/get_downstream_dependencies\"; params?: ", "TypeC", "<{ query: ", @@ -917,28 +877,6 @@ "CorrelationValue", "[]; }>; } & ", "APMRouteCreateOptions", - "; \"GET /internal/apm/assistant/get_error_document\": { endpoint: \"GET /internal/apm/assistant/get_error_document\"; params?: ", - "TypeC", - "<{ query: ", - "IntersectionC", - "<[", - "TypeC", - "<{ start: ", - "StringC", - "; end: ", - "StringC", - "; }>, ", - "PartialC", - "<{ 'error.grouping_name': ", - "StringC", - "; 'service.name': ", - "StringC", - "; }>]>; }> | undefined; handler: ({}: ", - "APMRouteHandlerResources", - " & { params: { query: { start: string; end: string; } & { 'error.grouping_name'?: string | undefined; 'service.name'?: string | undefined; }; }; }) => Promise<{ content: Partial<", - "APMError", - ">[]; }>; } & ", - "APMRouteCreateOptions", "; \"GET /internal/apm/assistant/get_service_summary\": { endpoint: \"GET /internal/apm/assistant/get_service_summary\"; params?: ", "TypeC", "<{ query: ", diff --git a/api_docs/apm.mdx b/api_docs/apm.mdx index 7421f2fdbe837..15b1a0c3b951b 100644 --- a/api_docs/apm.mdx +++ b/api_docs/apm.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apm title: "apm" image: https://source.unsplash.com/400x175/?github description: API docs for the apm plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'apm'] --- import apmObj from './apm.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/te | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 29 | 0 | 29 | 127 | +| 29 | 0 | 29 | 125 | ## Client diff --git a/api_docs/apm_data_access.mdx b/api_docs/apm_data_access.mdx index 04c54fdbb9bb0..96a3a875b6441 100644 --- a/api_docs/apm_data_access.mdx +++ b/api_docs/apm_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/apmDataAccess title: "apmDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the apmDataAccess plugin -date: 2023-12-06 +date: 2023-12-19 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 163804bf946e7..e4c70128e283d 100644 --- a/api_docs/asset_manager.mdx +++ b/api_docs/asset_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/assetManager title: "assetManager" image: https://source.unsplash.com/400x175/?github description: API docs for the assetManager plugin -date: 2023-12-06 +date: 2023-12-19 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 5261ed8e4f2dd..1f141caf3c9a6 100644 --- a/api_docs/banners.mdx +++ b/api_docs/banners.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/banners title: "banners" image: https://source.unsplash.com/400x175/?github description: API docs for the banners plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'banners'] --- import bannersObj from './banners.devdocs.json'; diff --git a/api_docs/bfetch.devdocs.json b/api_docs/bfetch.devdocs.json index 850bd68c79ed7..3567ec438badf 100644 --- a/api_docs/bfetch.devdocs.json +++ b/api_docs/bfetch.devdocs.json @@ -1,81 +1,7 @@ { "id": "bfetch", "client": { - "classes": [ - { - "parentPluginId": "bfetch", - "id": "def-public.BfetchRequestError", - "type": "Class", - "tags": [], - "label": "BfetchRequestError", - "description": [ - "\nError thrown when xhr request fails" - ], - "signature": [ - { - "pluginId": "bfetch", - "scope": "common", - "docId": "kibBfetchPluginApi", - "section": "def-common.BfetchRequestError", - "text": "BfetchRequestError" - }, - " extends Error" - ], - "path": "src/plugins/bfetch/common/bfetch_error.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "bfetch", - "id": "def-public.BfetchRequestError.Unnamed", - "type": "Function", - "tags": [], - "label": "Constructor", - "description": [ - "\nconstructor" - ], - "signature": [ - "any" - ], - "path": "src/plugins/bfetch/common/bfetch_error.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "bfetch", - "id": "def-public.BfetchRequestError.Unnamed.$1", - "type": "number", - "tags": [], - "label": "code", - "description": [ - "- Xhr error code" - ], - "signature": [ - "number" - ], - "path": "src/plugins/bfetch/common/bfetch_error.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "bfetch", - "id": "def-public.BfetchRequestError.code", - "type": "number", - "tags": [], - "label": "code", - "description": [], - "path": "src/plugins/bfetch/common/bfetch_error.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - } - ], + "classes": [], "functions": [ { "parentPluginId": "bfetch", @@ -609,79 +535,6 @@ }, "common": { "classes": [ - { - "parentPluginId": "bfetch", - "id": "def-common.BfetchRequestError", - "type": "Class", - "tags": [], - "label": "BfetchRequestError", - "description": [ - "\nError thrown when xhr request fails" - ], - "signature": [ - { - "pluginId": "bfetch", - "scope": "common", - "docId": "kibBfetchPluginApi", - "section": "def-common.BfetchRequestError", - "text": "BfetchRequestError" - }, - " extends Error" - ], - "path": "src/plugins/bfetch/common/bfetch_error.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "bfetch", - "id": "def-common.BfetchRequestError.Unnamed", - "type": "Function", - "tags": [], - "label": "Constructor", - "description": [ - "\nconstructor" - ], - "signature": [ - "any" - ], - "path": "src/plugins/bfetch/common/bfetch_error.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "bfetch", - "id": "def-common.BfetchRequestError.Unnamed.$1", - "type": "number", - "tags": [], - "label": "code", - "description": [ - "- Xhr error code" - ], - "signature": [ - "number" - ], - "path": "src/plugins/bfetch/common/bfetch_error.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "bfetch", - "id": "def-common.BfetchRequestError.code", - "type": "number", - "tags": [], - "label": "code", - "description": [], - "path": "src/plugins/bfetch/common/bfetch_error.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - }, { "parentPluginId": "bfetch", "id": "def-common.ItemBuffer", diff --git a/api_docs/bfetch.mdx b/api_docs/bfetch.mdx index 26f41ede6025a..afe008618a7c0 100644 --- a/api_docs/bfetch.mdx +++ b/api_docs/bfetch.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/bfetch title: "bfetch" image: https://source.unsplash.com/400x175/?github description: API docs for the bfetch plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'bfetch'] --- import bfetchObj from './bfetch.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 | |-------------------|-----------|------------------------|-----------------| -| 91 | 1 | 75 | 2 | +| 83 | 1 | 73 | 2 | ## Client @@ -31,9 +31,6 @@ Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sh ### Functions -### Classes - - ### Consts, variables and types diff --git a/api_docs/canvas.mdx b/api_docs/canvas.mdx index 69db21ab19476..8e4f9d8bfb14c 100644 --- a/api_docs/canvas.mdx +++ b/api_docs/canvas.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/canvas title: "canvas" image: https://source.unsplash.com/400x175/?github description: API docs for the canvas plugin -date: 2023-12-06 +date: 2023-12-19 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 afd1d4b2224b4..25c83c9f699ef 100644 --- a/api_docs/cases.mdx +++ b/api_docs/cases.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cases title: "cases" image: https://source.unsplash.com/400x175/?github description: API docs for the cases plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cases'] --- import casesObj from './cases.devdocs.json'; diff --git a/api_docs/charts.devdocs.json b/api_docs/charts.devdocs.json index 613f12198c69b..7a0f646988360 100644 --- a/api_docs/charts.devdocs.json +++ b/api_docs/charts.devdocs.json @@ -2239,19 +2239,9 @@ "label": "theme", "description": [], "signature": [ - "{ readonly chartsDefaultTheme: ", - "RecursivePartial", - "<", - "Theme", - ">; readonly chartsDefaultBaseTheme: ", - "Theme", - "; chartsTheme$: ", - "Observable", - "<", - "RecursivePartial", - "<", + "{ readonly chartsDefaultBaseTheme: ", "Theme", - ">>; chartsBaseTheme$: ", + "; chartsBaseTheme$: ", "Observable", "<", "Theme", @@ -3403,7 +3393,7 @@ "CustomXDomain", " | undefined>; ariaDescription?: string | undefined; ariaDescribedBy?: string | undefined; ariaLabelledBy?: string | undefined; ariaTableCaption?: string | undefined; legendAction?: \"ignore\" | undefined; legendStrategy?: ", "LegendStrategy", - " | undefined; onLegendItemClick?: \"ignore\" | undefined; customLegend?: \"ignore\" | undefined; onLegendItemMinusClick?: \"ignore\" | undefined; onLegendItemOut?: \"ignore\" | undefined; onLegendItemOver?: \"ignore\" | undefined; onLegendItemPlusClick?: \"ignore\" | undefined; debugState?: boolean | undefined; onProjectionClick?: \"ignore\" | undefined; onElementClick?: \"ignore\" | undefined; onElementOver?: \"ignore\" | undefined; onElementOut?: \"ignore\" | undefined; onBrushEnd?: \"ignore\" | undefined; onProjectionAreaChange?: \"ignore\" | undefined; onAnnotationClick?: \"ignore\" | undefined; pointerUpdateDebounce?: number | undefined; roundHistogramBrushValues?: boolean | undefined; noResults?: React.ReactChild | React.ComponentType<{}> | undefined; legendSort?: \"ignore\" | undefined; } | undefined; }" + " | undefined; onLegendItemClick?: \"ignore\" | undefined; customLegend?: \"ignore\" | undefined; onLegendItemMinusClick?: \"ignore\" | undefined; onLegendItemOut?: \"ignore\" | undefined; onLegendItemOver?: \"ignore\" | undefined; onLegendItemPlusClick?: \"ignore\" | undefined; debugState?: boolean | undefined; onProjectionClick?: \"ignore\" | undefined; onElementClick?: \"ignore\" | undefined; onElementOver?: \"ignore\" | undefined; onElementOut?: \"ignore\" | undefined; onBrushEnd?: \"ignore\" | undefined; onResize?: \"ignore\" | undefined; onWillRender?: \"ignore\" | undefined; onProjectionAreaChange?: \"ignore\" | undefined; onAnnotationClick?: \"ignore\" | undefined; pointerUpdateDebounce?: number | undefined; roundHistogramBrushValues?: boolean | undefined; noResults?: React.ReactChild | React.ComponentType<{}> | undefined; legendSort?: \"ignore\" | undefined; } | undefined; }" ], "path": "src/plugins/charts/common/static/overrides/settings.ts", "deprecated": false, @@ -3575,14 +3565,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/charts/common/expressions/palette/types.ts", diff --git a/api_docs/charts.mdx b/api_docs/charts.mdx index b7eb12534cfb7..506c4b022b916 100644 --- a/api_docs/charts.mdx +++ b/api_docs/charts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/charts title: "charts" image: https://source.unsplash.com/400x175/?github description: API docs for the charts plugin -date: 2023-12-06 +date: 2023-12-19 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 cf5fe72bb4704..3b1aedfce5c7c 100644 --- a/api_docs/cloud.mdx +++ b/api_docs/cloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloud title: "cloud" image: https://source.unsplash.com/400x175/?github description: API docs for the cloud plugin -date: 2023-12-06 +date: 2023-12-19 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 94c53ca52b62e..deaabc42e8934 100644 --- a/api_docs/cloud_data_migration.mdx +++ b/api_docs/cloud_data_migration.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDataMigration title: "cloudDataMigration" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDataMigration plugin -date: 2023-12-06 +date: 2023-12-19 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 6d97a919cc3d8..f86194816d447 100644 --- a/api_docs/cloud_defend.mdx +++ b/api_docs/cloud_defend.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudDefend title: "cloudDefend" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudDefend plugin -date: 2023-12-06 +date: 2023-12-19 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 f15c8b4270f47..c869aae3dccab 100644 --- a/api_docs/cloud_experiments.mdx +++ b/api_docs/cloud_experiments.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudExperiments title: "cloudExperiments" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudExperiments plugin -date: 2023-12-06 +date: 2023-12-19 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 265f6f8dac133..75e3ed2771370 100644 --- a/api_docs/cloud_security_posture.mdx +++ b/api_docs/cloud_security_posture.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/cloudSecurityPosture title: "cloudSecurityPosture" image: https://source.unsplash.com/400x175/?github description: API docs for the cloudSecurityPosture plugin -date: 2023-12-06 +date: 2023-12-19 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 2f2cbfcfcd667..391457d5379b0 100644 --- a/api_docs/console.mdx +++ b/api_docs/console.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/console title: "console" image: https://source.unsplash.com/400x175/?github description: API docs for the console plugin -date: 2023-12-06 +date: 2023-12-19 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 45a736a118568..8036d0e2967dc 100644 --- a/api_docs/content_management.mdx +++ b/api_docs/content_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/contentManagement title: "contentManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the contentManagement plugin -date: 2023-12-06 +date: 2023-12-19 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 4e36e5cd0c1f6..aed90aee8ba2b 100644 --- a/api_docs/controls.mdx +++ b/api_docs/controls.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/controls title: "controls" image: https://source.unsplash.com/400x175/?github description: API docs for the controls plugin -date: 2023-12-06 +date: 2023-12-19 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 3335fa73bf47a..4f695b8a38375 100644 --- a/api_docs/custom_integrations.mdx +++ b/api_docs/custom_integrations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/customIntegrations title: "customIntegrations" image: https://source.unsplash.com/400x175/?github description: API docs for the customIntegrations plugin -date: 2023-12-06 +date: 2023-12-19 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 a6f8c9dbf1c6d..39611988cf6b8 100644 --- a/api_docs/dashboard.mdx +++ b/api_docs/dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboard title: "dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboard plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dashboard'] --- import dashboardObj from './dashboard.devdocs.json'; diff --git a/api_docs/dashboard_enhanced.devdocs.json b/api_docs/dashboard_enhanced.devdocs.json index 96cce4041b2bf..56c455e6df7ac 100644 --- a/api_docs/dashboard_enhanced.devdocs.json +++ b/api_docs/dashboard_enhanced.devdocs.json @@ -910,8 +910,8 @@ "pluginId": "uiActionsEnhanced", "scope": "server", "docId": "kibUiActionsEnhancedPluginApi", - "section": "def-server.SetupContract", - "text": "SetupContract" + "section": "def-server.UiActionsEnhancedServerSetup", + "text": "UiActionsEnhancedServerSetup" } ], "path": "x-pack/plugins/dashboard_enhanced/server/plugin.ts", diff --git a/api_docs/dashboard_enhanced.mdx b/api_docs/dashboard_enhanced.mdx index 440b8d60f512f..02869fa53800c 100644 --- a/api_docs/dashboard_enhanced.mdx +++ b/api_docs/dashboard_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dashboardEnhanced title: "dashboardEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the dashboardEnhanced plugin -date: 2023-12-06 +date: 2023-12-19 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 0e999dd6572d4..a1bb897ebee3a 100644 --- a/api_docs/data.devdocs.json +++ b/api_docs/data.devdocs.json @@ -5214,14 +5214,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -5267,14 +5259,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -5320,14 +5304,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -5373,14 +5349,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -5426,14 +5394,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -5479,14 +5439,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -5532,14 +5484,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -5585,14 +5529,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -5638,14 +5574,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -5691,14 +5619,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -5744,14 +5664,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -5797,14 +5709,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -5850,14 +5754,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -5903,14 +5799,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -5956,14 +5844,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -6009,14 +5889,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -6062,14 +5934,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -6115,14 +5979,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -6168,14 +6024,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -6221,14 +6069,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -6274,14 +6114,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -6327,14 +6159,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -6380,14 +6204,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -6433,14 +6249,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -6486,14 +6294,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -6539,14 +6339,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -6592,14 +6384,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -6645,14 +6429,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -6698,14 +6474,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -6751,14 +6519,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -6804,14 +6564,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -6857,14 +6609,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -6910,14 +6654,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -6963,14 +6699,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -7016,14 +6744,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -7069,14 +6789,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -7122,14 +6834,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -7175,14 +6879,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -7228,14 +6924,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -7281,14 +6969,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -9799,14 +9479,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/expressions/esaggs/esaggs_fn.ts", @@ -9853,53 +9525,6 @@ "trackAdoption": false, "initialIsOpen": false }, - { - "parentPluginId": "data", - "id": "def-public.ExecutionContextSearch", - "type": "Type", - "tags": [], - "label": "ExecutionContextSearch", - "description": [], - "signature": [ - "{ filters?: ", - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.Filter", - "text": "Filter" - }, - "[] | undefined; query?: ", - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.Query", - "text": "Query" - }, - " | ", - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.Query", - "text": "Query" - }, - "[] | undefined; timeRange?: ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataQueryPluginApi", - "section": "def-common.TimeRange", - "text": "TimeRange" - }, - " | undefined; disableWarningToasts?: boolean | undefined; }" - ], - "path": "src/plugins/data/common/search/expressions/kibana_context_type.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, { "parentPluginId": "data", "id": "def-public.ExpressionFunctionKibana", @@ -9947,14 +9572,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataSearchPluginApi", - "section": "def-common.ExecutionContextSearch", - "text": "ExecutionContextSearch" - }, ">>" ], "path": "src/plugins/data/common/search/expressions/kibana.ts", @@ -10009,14 +9626,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataSearchPluginApi", - "section": "def-common.ExecutionContextSearch", - "text": "ExecutionContextSearch" - }, ">>" ], "path": "src/plugins/data/common/search/expressions/kibana_context.ts", @@ -10063,14 +9672,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/expressions/kql.ts", @@ -10117,14 +9718,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/expressions/lucene.ts", @@ -10142,9 +9735,9 @@ "signature": [ "{ type: \"kibana_context\"; } & ", { - "pluginId": "data", + "pluginId": "@kbn/es-query", "scope": "common", - "docId": "kibDataSearchPluginApi", + "docId": "kibKbnEsQueryPluginApi", "section": "def-common.ExecutionContextSearch", "text": "ExecutionContextSearch" } @@ -10332,14 +9925,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data_views/common/expressions/load_index_pattern.ts", @@ -10705,9 +10290,9 @@ "signature": [ "{ type: \"kibana_context\"; } & ", { - "pluginId": "data", + "pluginId": "@kbn/es-query", "scope": "common", - "docId": "kibDataSearchPluginApi", + "docId": "kibKbnEsQueryPluginApi", "section": "def-common.ExecutionContextSearch", "text": "ExecutionContextSearch" } @@ -25479,14 +25064,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data_views/common/expressions/load_index_pattern.ts", diff --git a/api_docs/data.mdx b/api_docs/data.mdx index fb9e09b27683a..f51bbd6816385 100644 --- a/api_docs/data.mdx +++ b/api_docs/data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data title: "data" image: https://source.unsplash.com/400x175/?github description: API docs for the data plugin -date: 2023-12-06 +date: 2023-12-19 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 | |-------------------|-----------|------------------------|-----------------| -| 3197 | 32 | 2545 | 22 | +| 3188 | 31 | 2537 | 22 | ## Client diff --git a/api_docs/data_query.mdx b/api_docs/data_query.mdx index dc4f33df821d5..48b82a61a2413 100644 --- a/api_docs/data_query.mdx +++ b/api_docs/data_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-query title: "data.query" image: https://source.unsplash.com/400x175/?github description: API docs for the data.query plugin -date: 2023-12-06 +date: 2023-12-19 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 | |-------------------|-----------|------------------------|-----------------| -| 3197 | 32 | 2545 | 22 | +| 3188 | 31 | 2537 | 22 | ## Client diff --git a/api_docs/data_search.devdocs.json b/api_docs/data_search.devdocs.json index ce39cd72a3b37..632c5c0c65c92 100644 --- a/api_docs/data_search.devdocs.json +++ b/api_docs/data_search.devdocs.json @@ -3,114 +3,6 @@ "client": { "classes": [], "functions": [ - { - "parentPluginId": "data", - "id": "def-public.getSearchErrorOverrideDisplay", - "type": "Function", - "tags": [], - "label": "getSearchErrorOverrideDisplay", - "description": [], - "signature": [ - "({\n error,\n application,\n}: { error: Error; application: ", - { - "pluginId": "@kbn/core-application-browser", - "scope": "common", - "docId": "kibKbnCoreApplicationBrowserPluginApi", - "section": "def-common.ApplicationStart", - "text": "ApplicationStart" - }, - "; }) => { title: string; body: React.ReactNode; actions?: React.ReactNode[] | undefined; } | undefined" - ], - "path": "src/plugins/data/public/search/search_interceptor/utils.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "data", - "id": "def-public.getSearchErrorOverrideDisplay.$1", - "type": "Object", - "tags": [], - "label": "{\n error,\n application,\n}", - "description": [], - "path": "src/plugins/data/public/search/search_interceptor/utils.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "data", - "id": "def-public.getSearchErrorOverrideDisplay.$1.error", - "type": "Object", - "tags": [], - "label": "error", - "description": [], - "signature": [ - "Error" - ], - "path": "src/plugins/data/public/search/search_interceptor/utils.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "data", - "id": "def-public.getSearchErrorOverrideDisplay.$1.application", - "type": "Object", - "tags": [], - "label": "application", - "description": [], - "signature": [ - { - "pluginId": "@kbn/core-application-browser", - "scope": "common", - "docId": "kibKbnCoreApplicationBrowserPluginApi", - "section": "def-common.ApplicationStart", - "text": "ApplicationStart" - } - ], - "path": "src/plugins/data/public/search/search_interceptor/utils.ts", - "deprecated": false, - "trackAdoption": false - } - ] - } - ], - "returnComment": [], - "initialIsOpen": false - }, - { - "parentPluginId": "data", - "id": "def-public.isEsError", - "type": "Function", - "tags": [], - "label": "isEsError", - "description": [ - "\nChecks if a given errors originated from Elasticsearch.\nThose params are assigned to the attributes property of an error.\n" - ], - "signature": [ - "(e: any) => boolean" - ], - "path": "src/plugins/data/public/search/errors/types.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "data", - "id": "def-public.isEsError.$1", - "type": "Any", - "tags": [], - "label": "e", - "description": [], - "signature": [ - "any" - ], - "path": "src/plugins/data/public/search/errors/types.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [], - "initialIsOpen": false - }, { "parentPluginId": "data", "id": "def-public.waitUntilNextSessionCompletes$", @@ -1190,36 +1082,6 @@ } ], "misc": [ - { - "parentPluginId": "data", - "id": "def-public.IEsError", - "type": "Type", - "tags": [], - "label": "IEsError", - "description": [], - "signature": [ - { - "pluginId": "kibanaUtils", - "scope": "common", - "docId": "kibKibanaUtilsPluginApi", - "section": "def-common.KibanaServerError", - "text": "KibanaServerError" - }, - "<", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataSearchPluginApi", - "section": "def-common.IEsErrorAttributes", - "text": "IEsErrorAttributes" - }, - ">" - ], - "path": "src/plugins/data/public/search/errors/types.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, { "parentPluginId": "data", "id": "def-public.ISessionsClient", @@ -13017,14 +12879,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">) => any" ], "path": "src/plugins/data/common/search/expressions/utils/function_wrapper.ts", @@ -17412,14 +17266,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -17465,14 +17311,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -17518,14 +17356,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -17571,14 +17401,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -17624,14 +17446,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -17677,14 +17491,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -17730,13 +17536,50 @@ "section": "def-common.Adapters", "text": "Adapters" }, + ">>" + ], + "path": "src/plugins/data/common/search/aggs/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "data", + "id": "def-common.AggFunctionsMapping.aggHistogram", + "type": "Object", + "tags": [], + "label": "aggHistogram", + "description": [], + "signature": [ + { + "pluginId": "expressions", + "scope": "common", + "docId": "kibExpressionsPluginApi", + "section": "def-common.ExpressionFunctionDefinition", + "text": "ExpressionFunctionDefinition" + }, + "<\"aggHistogram\", any, Arguments, ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataSearchPluginApi", + "section": "def-common.AggExpressionType", + "text": "AggExpressionType" + }, ", ", { - "pluginId": "@kbn/utility-types", + "pluginId": "expressions", "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" + "docId": "kibExpressionsPluginApi", + "section": "def-common.ExecutionContext", + "text": "ExecutionContext" + }, + "<", + { + "pluginId": "inspector", + "scope": "common", + "docId": "kibInspectorPluginApi", + "section": "def-common.Adapters", + "text": "Adapters" }, ">>" ], @@ -17746,10 +17589,10 @@ }, { "parentPluginId": "data", - "id": "def-common.AggFunctionsMapping.aggHistogram", + "id": "def-common.AggFunctionsMapping.aggDateHistogram", "type": "Object", "tags": [], - "label": "aggHistogram", + "label": "aggDateHistogram", "description": [], "signature": [ { @@ -17759,7 +17602,7 @@ "section": "def-common.ExpressionFunctionDefinition", "text": "ExpressionFunctionDefinition" }, - "<\"aggHistogram\", any, Arguments, ", + "<\"aggDateHistogram\", any, Arguments, ", { "pluginId": "data", "scope": "common", @@ -17783,13 +17626,50 @@ "section": "def-common.Adapters", "text": "Adapters" }, + ">>" + ], + "path": "src/plugins/data/common/search/aggs/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "data", + "id": "def-common.AggFunctionsMapping.aggTerms", + "type": "Object", + "tags": [], + "label": "aggTerms", + "description": [], + "signature": [ + { + "pluginId": "expressions", + "scope": "common", + "docId": "kibExpressionsPluginApi", + "section": "def-common.ExpressionFunctionDefinition", + "text": "ExpressionFunctionDefinition" + }, + "<\"aggTerms\", any, Arguments, ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataSearchPluginApi", + "section": "def-common.AggExpressionType", + "text": "AggExpressionType" + }, ", ", { - "pluginId": "@kbn/utility-types", + "pluginId": "expressions", "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" + "docId": "kibExpressionsPluginApi", + "section": "def-common.ExecutionContext", + "text": "ExecutionContext" + }, + "<", + { + "pluginId": "inspector", + "scope": "common", + "docId": "kibInspectorPluginApi", + "section": "def-common.Adapters", + "text": "Adapters" }, ">>" ], @@ -17799,10 +17679,10 @@ }, { "parentPluginId": "data", - "id": "def-common.AggFunctionsMapping.aggDateHistogram", + "id": "def-common.AggFunctionsMapping.aggTimeSeries", "type": "Object", "tags": [], - "label": "aggDateHistogram", + "label": "aggTimeSeries", "description": [], "signature": [ { @@ -17812,7 +17692,7 @@ "section": "def-common.ExpressionFunctionDefinition", "text": "ExpressionFunctionDefinition" }, - "<\"aggDateHistogram\", any, Arguments, ", + "<\"aggTimeSeries\", any, AggArgs, ", { "pluginId": "data", "scope": "common", @@ -17836,120 +17716,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, - ">>" - ], - "path": "src/plugins/data/common/search/aggs/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "data", - "id": "def-common.AggFunctionsMapping.aggTerms", - "type": "Object", - "tags": [], - "label": "aggTerms", - "description": [], - "signature": [ - { - "pluginId": "expressions", - "scope": "common", - "docId": "kibExpressionsPluginApi", - "section": "def-common.ExpressionFunctionDefinition", - "text": "ExpressionFunctionDefinition" - }, - "<\"aggTerms\", any, Arguments, ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataSearchPluginApi", - "section": "def-common.AggExpressionType", - "text": "AggExpressionType" - }, - ", ", - { - "pluginId": "expressions", - "scope": "common", - "docId": "kibExpressionsPluginApi", - "section": "def-common.ExecutionContext", - "text": "ExecutionContext" - }, - "<", - { - "pluginId": "inspector", - "scope": "common", - "docId": "kibInspectorPluginApi", - "section": "def-common.Adapters", - "text": "Adapters" - }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, - ">>" - ], - "path": "src/plugins/data/common/search/aggs/types.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "data", - "id": "def-common.AggFunctionsMapping.aggTimeSeries", - "type": "Object", - "tags": [], - "label": "aggTimeSeries", - "description": [], - "signature": [ - { - "pluginId": "expressions", - "scope": "common", - "docId": "kibExpressionsPluginApi", - "section": "def-common.ExpressionFunctionDefinition", - "text": "ExpressionFunctionDefinition" - }, - "<\"aggTimeSeries\", any, AggArgs, ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataSearchPluginApi", - "section": "def-common.AggExpressionType", - "text": "AggExpressionType" - }, - ", ", - { - "pluginId": "expressions", - "scope": "common", - "docId": "kibExpressionsPluginApi", - "section": "def-common.ExecutionContext", - "text": "ExecutionContext" - }, - "<", - { - "pluginId": "inspector", - "scope": "common", - "docId": "kibInspectorPluginApi", - "section": "def-common.Adapters", - "text": "Adapters" - }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -17995,14 +17761,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -18048,14 +17806,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -18101,14 +17851,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -18154,14 +17896,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -18207,14 +17941,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -18260,14 +17986,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -18313,14 +18031,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -18366,14 +18076,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -18419,14 +18121,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -18472,14 +18166,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -18525,14 +18211,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -18578,14 +18256,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -18631,14 +18301,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -18684,14 +18346,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -18737,14 +18391,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -18790,14 +18436,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -18843,14 +18481,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -18896,14 +18526,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -18949,14 +18571,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -19002,14 +18616,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -19055,14 +18661,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -19108,14 +18706,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -19161,14 +18751,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -19214,14 +18796,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -19267,14 +18841,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -19320,14 +18886,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -19373,14 +18931,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -19426,14 +18976,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -19479,14 +19021,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/aggs/types.ts", @@ -31954,14 +31488,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/expressions/eql.ts", @@ -32090,14 +31616,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/expressions/esaggs/esaggs_fn.ts", @@ -32136,14 +31654,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/expressions/esdsl.ts", @@ -32243,53 +31753,6 @@ "trackAdoption": false, "initialIsOpen": false }, - { - "parentPluginId": "data", - "id": "def-common.ExecutionContextSearch", - "type": "Type", - "tags": [], - "label": "ExecutionContextSearch", - "description": [], - "signature": [ - "{ filters?: ", - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.Filter", - "text": "Filter" - }, - "[] | undefined; query?: ", - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.Query", - "text": "Query" - }, - " | ", - { - "pluginId": "@kbn/es-query", - "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.Query", - "text": "Query" - }, - "[] | undefined; timeRange?: ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataQueryPluginApi", - "section": "def-common.TimeRange", - "text": "TimeRange" - }, - " | undefined; disableWarningToasts?: boolean | undefined; }" - ], - "path": "src/plugins/data/common/search/expressions/kibana_context_type.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, { "parentPluginId": "data", "id": "def-common.ExpressionFunctionCidr", @@ -32337,14 +31800,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/expressions/cidr.ts", @@ -32399,14 +31854,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/expressions/date_range.ts", @@ -32453,14 +31900,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/expressions/exists_filter.ts", @@ -32515,14 +31954,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/expressions/extended_bounds.ts", @@ -32569,14 +32000,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/expressions/field.ts", @@ -32623,14 +32046,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/expressions/geo_bounding_box.ts", @@ -32677,14 +32092,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/expressions/geo_point.ts", @@ -32739,14 +32146,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/expressions/ip_range.ts", @@ -32801,14 +32200,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataSearchPluginApi", - "section": "def-common.ExecutionContextSearch", - "text": "ExecutionContextSearch" - }, ">>" ], "path": "src/plugins/data/common/search/expressions/kibana.ts", @@ -32863,14 +32254,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataSearchPluginApi", - "section": "def-common.ExecutionContextSearch", - "text": "ExecutionContextSearch" - }, ">>" ], "path": "src/plugins/data/common/search/expressions/kibana_context.ts", @@ -32917,14 +32300,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/expressions/kibana_filter.ts", @@ -32979,14 +32354,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/expressions/timerange.ts", @@ -33033,14 +32400,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/expressions/kql.ts", @@ -33087,14 +32446,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/expressions/lucene.ts", @@ -33149,14 +32500,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/expressions/numerical_range.ts", @@ -33203,14 +32546,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/expressions/phrase_filter.ts", @@ -33257,14 +32592,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/expressions/query_filter.ts", @@ -33311,14 +32638,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/expressions/range.ts", @@ -33365,14 +32684,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/expressions/range_filter.ts", @@ -33427,14 +32738,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/expressions/remove_filter.ts", @@ -33489,14 +32792,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data/common/search/expressions/select_filter.ts", @@ -33514,9 +32809,9 @@ "signature": [ "{ type: \"kibana_context\"; } & ", { - "pluginId": "data", + "pluginId": "@kbn/es-query", "scope": "common", - "docId": "kibDataSearchPluginApi", + "docId": "kibKbnEsQueryPluginApi", "section": "def-common.ExecutionContextSearch", "text": "ExecutionContextSearch" } @@ -34512,9 +33807,9 @@ "signature": [ "{ type: \"kibana_context\"; } & ", { - "pluginId": "data", + "pluginId": "@kbn/es-query", "scope": "common", - "docId": "kibDataSearchPluginApi", + "docId": "kibKbnEsQueryPluginApi", "section": "def-common.ExecutionContextSearch", "text": "ExecutionContextSearch" } @@ -37567,14 +36862,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataSearchPluginApi", - "section": "def-common.ExecutionContextSearch", - "text": "ExecutionContextSearch" - }, ">) => ", { "pluginId": "data", @@ -37648,14 +36935,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "data", - "scope": "common", - "docId": "kibDataSearchPluginApi", - "section": "def-common.ExecutionContextSearch", - "text": "ExecutionContextSearch" - }, ">" ], "path": "src/plugins/data/common/search/expressions/kibana.ts", @@ -40577,9 +39856,9 @@ }, "[] | undefined; timeRange?: ", { - "pluginId": "data", + "pluginId": "@kbn/es-query", "scope": "common", - "docId": "kibDataQueryPluginApi", + "docId": "kibKbnEsQueryPluginApi", "section": "def-common.TimeRange", "text": "TimeRange" }, @@ -40920,9 +40199,9 @@ }, "[] | undefined; timeRange?: ", { - "pluginId": "data", + "pluginId": "@kbn/es-query", "scope": "common", - "docId": "kibDataQueryPluginApi", + "docId": "kibKbnEsQueryPluginApi", "section": "def-common.TimeRange", "text": "TimeRange" }, diff --git a/api_docs/data_search.mdx b/api_docs/data_search.mdx index 82a534845b0fa..94b47782937b8 100644 --- a/api_docs/data_search.mdx +++ b/api_docs/data_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/data-search title: "data.search" image: https://source.unsplash.com/400x175/?github description: API docs for the data.search plugin -date: 2023-12-06 +date: 2023-12-19 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 | |-------------------|-----------|------------------------|-----------------| -| 3197 | 32 | 2545 | 22 | +| 3188 | 31 | 2537 | 22 | ## Client diff --git a/api_docs/data_view_editor.mdx b/api_docs/data_view_editor.mdx index b4b56a66a34a5..cd8c70acb8218 100644 --- a/api_docs/data_view_editor.mdx +++ b/api_docs/data_view_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewEditor title: "dataViewEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewEditor plugin -date: 2023-12-06 +date: 2023-12-19 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 f614afa9369e0..a5c2be2aa6634 100644 --- a/api_docs/data_view_field_editor.mdx +++ b/api_docs/data_view_field_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewFieldEditor title: "dataViewFieldEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewFieldEditor plugin -date: 2023-12-06 +date: 2023-12-19 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 62430d70fd068..fc36c8f6b817d 100644 --- a/api_docs/data_view_management.mdx +++ b/api_docs/data_view_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViewManagement title: "dataViewManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViewManagement plugin -date: 2023-12-06 +date: 2023-12-19 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 1f7e5f674a006..d3ccdff224917 100644 --- a/api_docs/data_views.devdocs.json +++ b/api_docs/data_views.devdocs.json @@ -20722,14 +20722,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/data_views/common/expressions/load_index_pattern.ts", diff --git a/api_docs/data_views.mdx b/api_docs/data_views.mdx index 139aff2ca7754..8620b0c7cd236 100644 --- a/api_docs/data_views.mdx +++ b/api_docs/data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataViews title: "dataViews" image: https://source.unsplash.com/400x175/?github description: API docs for the dataViews plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataViews'] --- import dataViewsObj from './data_views.devdocs.json'; diff --git a/api_docs/data_visualizer.mdx b/api_docs/data_visualizer.mdx index 914125c81886b..c98018e6f3792 100644 --- a/api_docs/data_visualizer.mdx +++ b/api_docs/data_visualizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/dataVisualizer title: "dataVisualizer" image: https://source.unsplash.com/400x175/?github description: API docs for the dataVisualizer plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'dataVisualizer'] --- import dataVisualizerObj from './data_visualizer.devdocs.json'; diff --git a/api_docs/dataset_quality.devdocs.json b/api_docs/dataset_quality.devdocs.json index a31a46b97abb8..0d7e9842ff93f 100644 --- a/api_docs/dataset_quality.devdocs.json +++ b/api_docs/dataset_quality.devdocs.json @@ -100,7 +100,7 @@ "label": "APIClientRequestParamsOf", "description": [], "signature": [ - "{ \"GET /internal/dataset_quality/data_streams/malformed_docs\": { endpoint: \"GET /internal/dataset_quality/data_streams/malformed_docs\"; params?: ", + "{ \"GET /internal/dataset_quality/data_streams/degraded_docs\": { endpoint: \"GET /internal/dataset_quality/data_streams/degraded_docs\"; params?: ", "TypeC", "<{ query: ", "IntersectionC", @@ -113,24 +113,14 @@ "; }>, ", "PartialC", "<{ type: ", - "UnionC", - "<[", - "LiteralC", - "<\"logs\">, ", - "LiteralC", - "<\"metrics\">, ", - "LiteralC", - "<\"traces\">, ", - "LiteralC", - "<\"synthetics\">, ", - "LiteralC", - "<\"profiling\">]>; }>, ", + "KeyofC", + "<{ logs: null; metrics: null; traces: null; synthetics: null; profiling: null; }>; }>, ", "PartialC", "<{ datasetQuery: ", "StringC", "; }>]>; }> | undefined; handler: ({}: ", "DatasetQualityRouteHandlerResources", - " & { params: { query: { start: number; end: number; } & { type?: \"metrics\" | \"synthetics\" | \"profiling\" | \"traces\" | \"logs\" | undefined; } & { datasetQuery?: string | undefined; }; }; }) => Promise<{ malformedDocs: { dataset: string; percentage: number; }[]; }>; } & ", + " & { params: { query: { start: number; end: number; } & { type?: \"metrics\" | \"synthetics\" | \"traces\" | \"logs\" | \"profiling\" | undefined; } & { datasetQuery?: string | undefined; }; }; }) => Promise<{ degradedDocs: { dataset: string; percentage: number; }[]; }>; } & ", "DatasetQualityRouteCreateOptions", "; \"GET /internal/dataset_quality/data_streams/stats\": { endpoint: \"GET /internal/dataset_quality/data_streams/stats\"; params?: ", "TypeC", @@ -139,28 +129,14 @@ "<[", "PartialC", "<{ type: ", - "UnionC", - "<[", - "LiteralC", - "<\"logs\">, ", - "LiteralC", - "<\"metrics\">, ", - "LiteralC", - "<\"traces\">, ", - "LiteralC", - "<\"synthetics\">, ", - "LiteralC", - "<\"profiling\">]>; }>, ", + "KeyofC", + "<{ logs: null; metrics: null; traces: null; synthetics: null; profiling: null; }>; }>, ", "PartialC", "<{ datasetQuery: ", "StringC", "; }>]>; }> | undefined; handler: ({}: ", "DatasetQualityRouteHandlerResources", - " & { params: { query: { type?: \"metrics\" | \"synthetics\" | \"profiling\" | \"traces\" | \"logs\" | undefined; } & { datasetQuery?: string | undefined; }; }; }) => Promise<{ dataStreamsStats: ", - "DataStreamStat", - "[]; integrations: ", - "Integration", - "[]; }>; } & ", + " & { params: { query: { type?: \"metrics\" | \"synthetics\" | \"traces\" | \"logs\" | \"profiling\" | undefined; } & { datasetQuery?: string | undefined; }; }; }) => Promise<{ dataStreamsStats: ({ name: string; } & { size?: string | undefined; sizeBytes?: number | undefined; lastActivity?: number | undefined; integration?: string | undefined; })[]; integrations: ({ name: string; } & { title?: string | undefined; version?: string | undefined; icons?: ({ path: string; src: string; } & { title?: string | undefined; size?: string | undefined; type?: string | undefined; })[] | undefined; datasets?: { [x: string]: string; } | undefined; })[]; }>; } & ", "DatasetQualityRouteCreateOptions", "; }[TEndpoint] extends { endpoint: any; params?: infer TRouteParamsRT | undefined; handler: ({}: any) => Promise; } & ", "ServerRouteCreateOptions", @@ -187,7 +163,7 @@ "label": "APIReturnType", "description": [], "signature": [ - "{ \"GET /internal/dataset_quality/data_streams/malformed_docs\": { endpoint: \"GET /internal/dataset_quality/data_streams/malformed_docs\"; params?: ", + "{ \"GET /internal/dataset_quality/data_streams/degraded_docs\": { endpoint: \"GET /internal/dataset_quality/data_streams/degraded_docs\"; params?: ", "TypeC", "<{ query: ", "IntersectionC", @@ -200,24 +176,14 @@ "; }>, ", "PartialC", "<{ type: ", - "UnionC", - "<[", - "LiteralC", - "<\"logs\">, ", - "LiteralC", - "<\"metrics\">, ", - "LiteralC", - "<\"traces\">, ", - "LiteralC", - "<\"synthetics\">, ", - "LiteralC", - "<\"profiling\">]>; }>, ", + "KeyofC", + "<{ logs: null; metrics: null; traces: null; synthetics: null; profiling: null; }>; }>, ", "PartialC", "<{ datasetQuery: ", "StringC", "; }>]>; }> | undefined; handler: ({}: ", "DatasetQualityRouteHandlerResources", - " & { params: { query: { start: number; end: number; } & { type?: \"metrics\" | \"synthetics\" | \"profiling\" | \"traces\" | \"logs\" | undefined; } & { datasetQuery?: string | undefined; }; }; }) => Promise<{ malformedDocs: { dataset: string; percentage: number; }[]; }>; } & ", + " & { params: { query: { start: number; end: number; } & { type?: \"metrics\" | \"synthetics\" | \"traces\" | \"logs\" | \"profiling\" | undefined; } & { datasetQuery?: string | undefined; }; }; }) => Promise<{ degradedDocs: { dataset: string; percentage: number; }[]; }>; } & ", "DatasetQualityRouteCreateOptions", "; \"GET /internal/dataset_quality/data_streams/stats\": { endpoint: \"GET /internal/dataset_quality/data_streams/stats\"; params?: ", "TypeC", @@ -226,28 +192,14 @@ "<[", "PartialC", "<{ type: ", - "UnionC", - "<[", - "LiteralC", - "<\"logs\">, ", - "LiteralC", - "<\"metrics\">, ", - "LiteralC", - "<\"traces\">, ", - "LiteralC", - "<\"synthetics\">, ", - "LiteralC", - "<\"profiling\">]>; }>, ", + "KeyofC", + "<{ logs: null; metrics: null; traces: null; synthetics: null; profiling: null; }>; }>, ", "PartialC", "<{ datasetQuery: ", "StringC", "; }>]>; }> | undefined; handler: ({}: ", "DatasetQualityRouteHandlerResources", - " & { params: { query: { type?: \"metrics\" | \"synthetics\" | \"profiling\" | \"traces\" | \"logs\" | undefined; } & { datasetQuery?: string | undefined; }; }; }) => Promise<{ dataStreamsStats: ", - "DataStreamStat", - "[]; integrations: ", - "Integration", - "[]; }>; } & ", + " & { params: { query: { type?: \"metrics\" | \"synthetics\" | \"traces\" | \"logs\" | \"profiling\" | undefined; } & { datasetQuery?: string | undefined; }; }; }) => Promise<{ dataStreamsStats: ({ name: string; } & { size?: string | undefined; sizeBytes?: number | undefined; lastActivity?: number | undefined; integration?: string | undefined; })[]; integrations: ({ name: string; } & { title?: string | undefined; version?: string | undefined; icons?: ({ path: string; src: string; } & { title?: string | undefined; size?: string | undefined; type?: string | undefined; })[] | undefined; datasets?: { [x: string]: string; } | undefined; })[]; }>; } & ", "DatasetQualityRouteCreateOptions", "; }[TEndpoint] extends { endpoint: any; params?: any; handler: ({}: any) => Promise; } & ", "ServerRouteCreateOptions", diff --git a/api_docs/dataset_quality.mdx b/api_docs/dataset_quality.mdx index 071b9494793ce..c90839b3f515a 100644 --- a/api_docs/dataset_quality.mdx +++ b/api_docs/dataset_quality.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/datasetQuality title: "datasetQuality" image: https://source.unsplash.com/400x175/?github description: API docs for the datasetQuality plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'datasetQuality'] --- import datasetQualityObj from './dataset_quality.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 8 | 0 | 8 | 4 | +| 8 | 0 | 8 | 2 | ## Client diff --git a/api_docs/deprecations_by_api.mdx b/api_docs/deprecations_by_api.mdx index af1947c646d7f..6149473692b9f 100644 --- a/api_docs/deprecations_by_api.mdx +++ b/api_docs/deprecations_by_api.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByApi slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-api title: Deprecated API usage by API description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -18,7 +18,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | ---------------|-----------|-----------| | | ml, stackAlerts | - | | | ruleRegistry, observability, ml, infra, monitoring, securitySolution, stackAlerts, synthetics, transform, uptime | - | -| | share, uiActions, guidedOnboarding, home, serverless, management, spaces, savedObjects, indexManagement, visualizations, controls, dashboard, savedObjectsTagging, expressionXY, lens, expressionMetricVis, expressionGauge, security, alerting, triggersActionsUi, cases, aiops, advancedSettings, exploratoryView, fleet, metricsDataAccess, licenseManagement, maps, dataVisualizer, ml, profiling, apm, expressionImage, expressionMetric, expressionError, expressionRevealImage, expressionRepeatImage, expressionShape, crossClusterReplication, enterpriseSearch, globalSearchBar, graph, grokdebugger, indexLifecycleManagement, infra, ingestPipelines, logstash, monitoring, observabilityOnboarding, osquery, devTools, painlessLab, remoteClusters, rollup, searchprofiler, newsfeed, securitySolution, snapshotRestore, synthetics, transform, upgradeAssistant, uptime, ux, watcher, cloudDataMigration, console, filesManagement, kibanaOverview, visDefaultEditor, expressionHeatmap, expressionLegacyMetricVis, expressionPartitionVis, expressionTagcloud, visTypeTable, visTypeTimelion, visTypeTimeseries, visTypeVega, visTypeVislib | - | +| | share, uiActions, guidedOnboarding, home, serverless, management, spaces, savedObjects, indexManagement, visualizations, dashboard, savedObjectsTagging, expressionXY, lens, expressionMetricVis, expressionGauge, security, alerting, triggersActionsUi, cases, aiops, advancedSettings, exploratoryView, fleet, metricsDataAccess, licenseManagement, maps, dataVisualizer, ml, osquery, profiling, apm, expressionImage, expressionMetric, expressionError, expressionRevealImage, expressionRepeatImage, expressionShape, crossClusterReplication, globalSearchBar, graph, grokdebugger, indexLifecycleManagement, infra, ingestPipelines, logstash, monitoring, observabilityOnboarding, devTools, painlessLab, remoteClusters, rollup, searchprofiler, newsfeed, securitySolution, snapshotRestore, synthetics, transform, upgradeAssistant, uptime, ux, watcher, cloudDataMigration, console, filesManagement, kibanaOverview, visDefaultEditor, expressionHeatmap, expressionLegacyMetricVis, expressionPartitionVis, expressionTagcloud, visTypeTable, visTypeTimelion, visTypeTimeseries, visTypeVega, visTypeVislib | - | | | encryptedSavedObjects, actions, data, ml, logstash, securitySolution, cloudChat | - | | | actions, ml, savedObjectsTagging, enterpriseSearch | - | | | @kbn/core-saved-objects-browser-internal, @kbn/core, savedObjects, visualizations, aiops, ml, dataVisualizer, dashboardEnhanced, graph, lens, securitySolution, eventAnnotation, @kbn/core-saved-objects-browser-mocks | - | @@ -28,7 +28,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | dashboard, dataVisualizer, stackAlerts, expressionPartitionVis | - | | | stackAlerts, alerting, securitySolution, inputControlVis | - | | | triggersActionsUi | - | -| | inspector, data, savedObjects, runtimeFields, indexManagement, dataViewEditor, unifiedSearch, embeddable, visualizations, controls, dashboard, licensing, savedObjectsTagging, dataViewFieldEditor, lens, security, triggersActionsUi, cases, observabilityShared, advancedSettings, exploratoryView, fleet, telemetry, maps, banners, reporting, timelines, cloudSecurityPosture, dashboardEnhanced, imageEmbeddable, graph, monitoring, securitySolution, synthetics, uptime, cloudLinks, console, dataViewManagement, eventAnnotationListing, filesManagement, uiActions, visTypeVislib | - | +| | inspector, data, savedObjects, runtimeFields, indexManagement, dataViewEditor, unifiedSearch, embeddable, visualizations, dashboard, licensing, savedObjectsTagging, dataViewFieldEditor, lens, security, triggersActionsUi, cases, observabilityShared, advancedSettings, exploratoryView, fleet, telemetry, maps, timelines, banners, reporting, cloudSecurityPosture, dashboardEnhanced, imageEmbeddable, graph, monitoring, securitySolution, synthetics, uptime, cloudLinks, console, dataViewManagement, eventAnnotationListing, filesManagement, uiActions, visTypeVislib | - | | | @kbn/core, visualizations, triggersActionsUi, advancedSettings | - | | | observability, @kbn/securitysolution-data-table, securitySolution | - | | | monitoring | - | @@ -57,7 +57,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | securitySolution | - | | | securitySolution | - | | | securitySolution | - | -| | exploratoryView, fleet, dataVisualizer, cloudSecurityPosture, discoverEnhanced, osquery, synthetics | - | +| | exploratoryView, fleet, dataVisualizer, osquery, cloudSecurityPosture, discoverEnhanced, synthetics | - | | | @kbn/core-plugins-browser-internal, @kbn/core-root-browser-internal, home, savedObjects, unifiedSearch, visualizations, fileUpload, dashboardEnhanced, transform, discover, dataVisualizer | - | | | @kbn/core-lifecycle-browser, @kbn/core-saved-objects-browser-internal, @kbn/core, visualizations, exploratoryView, transform, @kbn/core-saved-objects-browser-mocks | - | | | actions, alerting | - | @@ -128,8 +128,8 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | @kbn/reporting-export-types-pdf, reporting | - | | | @kbn/core-elasticsearch-server-internal, @kbn/core-plugins-server-internal, observabilityOnboarding, console | - | | | @kbn/content-management-table-list-view, filesManagement | - | -| | enterpriseSearch | - | | | @kbn/react-kibana-context-styled, kibanaReact | - | +| | enterpriseSearch | - | | | encryptedSavedObjects | - | | | @kbn/core | - | | | @kbn/core | - | diff --git a/api_docs/deprecations_by_plugin.mdx b/api_docs/deprecations_by_plugin.mdx index 70feaef0dd13c..aea6a04dfd7a6 100644 --- a/api_docs/deprecations_by_plugin.mdx +++ b/api_docs/deprecations_by_plugin.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsByPlugin slug: /kibana-dev-docs/api-meta/deprecated-api-list-by-plugin title: Deprecated API usage by plugin description: A list of deprecated APIs, which plugins are still referencing them, and when they need to be removed by. -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- @@ -145,9 +145,9 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | ---------------|-----------|-----------| | | [internal_utils.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/utils/internal_utils.ts#:~:text=migrationVersion) | - | | | [internal_utils.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/utils/internal_utils.ts#:~:text=migrationVersion), [internal_utils.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/utils/internal_utils.ts#:~:text=migrationVersion), [internal_utils.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/utils/internal_utils.ts#:~:text=migrationVersion), [internal_utils.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/utils/internal_utils.ts#:~:text=migrationVersion) | - | -| | [increment_counter_internal.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/internals/increment_counter_internal.ts#:~:text=migrationVersion), [repository.test.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.test.ts#:~:text=migrationVersion) | - | +| | [increment_counter_internal.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/internals/increment_counter_internal.ts#:~:text=migrationVersion), [increment_counter.test.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/increment_counter.test.ts#:~:text=migrationVersion) | - | | | [bulk_create.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_create.ts#:~:text=migrationVersion), [repository.test.common.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server-internal/src/test_helpers/repository.test.common.ts#:~:text=migrationVersion) | - | -| | [create.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/create.ts#:~:text=migrationVersion), [repository.test.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.test.ts#:~:text=migrationVersion), [repository.test.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.test.ts#:~:text=migrationVersion), [repository.test.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.test.ts#:~:text=migrationVersion) | - | +| | [create.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/create.ts#:~:text=migrationVersion), [create.test.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/create.test.ts#:~:text=migrationVersion), [create.test.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/create.test.ts#:~:text=migrationVersion), [create.test.ts](https://github.com/elastic/kibana/tree/main/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/create.test.ts#:~:text=migrationVersion) | - | @@ -484,8 +484,6 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| -| | [open_add_data_control_flyout.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/controls/public/control_group/editor/open_add_data_control_flyout.tsx#:~:text=toMountPoint), [open_add_data_control_flyout.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/controls/public/control_group/editor/open_add_data_control_flyout.tsx#:~:text=toMountPoint), [open_edit_control_group_flyout.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/controls/public/control_group/editor/open_edit_control_group_flyout.tsx#:~:text=toMountPoint), [open_edit_control_group_flyout.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/controls/public/control_group/editor/open_edit_control_group_flyout.tsx#:~:text=toMountPoint), [edit_control_action.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/controls/public/control_group/actions/edit_control_action.tsx#:~:text=toMountPoint), [edit_control_action.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/controls/public/control_group/actions/edit_control_action.tsx#:~:text=toMountPoint) | - | -| | [options_list_embeddable.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/controls/public/options_list/embeddable/options_list_embeddable.tsx#:~:text=KibanaThemeProvider), [options_list_embeddable.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/controls/public/options_list/embeddable/options_list_embeddable.tsx#:~:text=KibanaThemeProvider), [options_list_embeddable.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/controls/public/options_list/embeddable/options_list_embeddable.tsx#:~:text=KibanaThemeProvider), [range_slider_embeddable.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.tsx#:~:text=KibanaThemeProvider), [range_slider_embeddable.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.tsx#:~:text=KibanaThemeProvider), [range_slider_embeddable.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.tsx#:~:text=KibanaThemeProvider), [control_group_container.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/controls/public/control_group/embeddable/control_group_container.tsx#:~:text=KibanaThemeProvider), [control_group_container.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/controls/public/control_group/embeddable/control_group_container.tsx#:~:text=KibanaThemeProvider), [control_group_container.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/controls/public/control_group/embeddable/control_group_container.tsx#:~:text=KibanaThemeProvider), [time_slider_embeddable.tsx](https://github.com/elastic/kibana/tree/main/src/plugins/controls/public/time_slider/embeddable/time_slider_embeddable.tsx#:~:text=KibanaThemeProvider)+ 2 more | - | | | [options_list_persistable_state.ts](https://github.com/elastic/kibana/tree/main/src/plugins/controls/common/options_list/options_list_persistable_state.ts#:~:text=SavedObjectReference), [options_list_persistable_state.ts](https://github.com/elastic/kibana/tree/main/src/plugins/controls/common/options_list/options_list_persistable_state.ts#:~:text=SavedObjectReference), [options_list_persistable_state.ts](https://github.com/elastic/kibana/tree/main/src/plugins/controls/common/options_list/options_list_persistable_state.ts#:~:text=SavedObjectReference), [range_slider_persistable_state.ts](https://github.com/elastic/kibana/tree/main/src/plugins/controls/common/range_slider/range_slider_persistable_state.ts#:~:text=SavedObjectReference), [range_slider_persistable_state.ts](https://github.com/elastic/kibana/tree/main/src/plugins/controls/common/range_slider/range_slider_persistable_state.ts#:~:text=SavedObjectReference), [range_slider_persistable_state.ts](https://github.com/elastic/kibana/tree/main/src/plugins/controls/common/range_slider/range_slider_persistable_state.ts#:~:text=SavedObjectReference), [time_slider_persistable_state.ts](https://github.com/elastic/kibana/tree/main/src/plugins/controls/common/time_slider/time_slider_persistable_state.ts#:~:text=SavedObjectReference), [time_slider_persistable_state.ts](https://github.com/elastic/kibana/tree/main/src/plugins/controls/common/time_slider/time_slider_persistable_state.ts#:~:text=SavedObjectReference), [time_slider_persistable_state.ts](https://github.com/elastic/kibana/tree/main/src/plugins/controls/common/time_slider/time_slider_persistable_state.ts#:~:text=SavedObjectReference), [control_group_persistable_state.ts](https://github.com/elastic/kibana/tree/main/src/plugins/controls/common/control_group/control_group_persistable_state.ts#:~:text=SavedObjectReference)+ 2 more | - | @@ -653,7 +651,6 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| -| | [index.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/enterprise_search/public/applications/index.tsx#:~:text=KibanaThemeProvider), [index.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/enterprise_search/public/applications/index.tsx#:~:text=KibanaThemeProvider), [index.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/enterprise_search/public/applications/index.tsx#:~:text=KibanaThemeProvider) | - | | | [account_settings.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/account_settings/account_settings.tsx#:~:text=uiApi), [account_settings.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/account_settings/account_settings.tsx#:~:text=uiApi), [account_settings.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/account_settings/account_settings.tsx#:~:text=uiApi), [account_settings.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/account_settings/account_settings.tsx#:~:text=uiApi) | - | | | [check_access.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/enterprise_search/server/lib/check_access.ts#:~:text=authz), [check_access.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/enterprise_search/server/lib/check_access.ts#:~:text=authz), [check_access.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/enterprise_search/server/lib/check_access.ts#:~:text=authz) | - | | | [telemetry.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/enterprise_search/server/collectors/lib/telemetry.ts#:~:text=SavedObjectAttributes), [telemetry.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/enterprise_search/server/collectors/lib/telemetry.ts#:~:text=SavedObjectAttributes), [telemetry.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/enterprise_search/server/collectors/lib/telemetry.ts#:~:text=SavedObjectAttributes), [telemetry.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/enterprise_search/server/collectors/lib/telemetry.ts#:~:text=SavedObjectAttributes), [telemetry.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/enterprise_search/server/collectors/lib/telemetry.ts#:~:text=SavedObjectAttributes), [telemetry.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/enterprise_search/server/collectors/lib/telemetry.ts#:~:text=SavedObjectAttributes) | - | @@ -1136,7 +1133,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | Deprecated API | Reference location(s) | Remove By | | ---------------|-----------|-----------| -| | [custom_threshold_executor.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability/server/lib/rules/custom_threshold/custom_threshold_executor.ts#:~:text=alertFactory), [executor.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability/server/lib/rules/slo_burn_rate/executor.ts#:~:text=alertFactory), [executor.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability/server/lib/rules/slo_burn_rate/executor.test.ts#:~:text=alertFactory) | - | +| | [custom_threshold_executor.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability/server/lib/rules/custom_threshold/custom_threshold_executor.ts#:~:text=alertFactory), [custom_threshold_executor.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability/server/lib/rules/custom_threshold/custom_threshold_executor.ts#:~:text=alertFactory), [executor.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability/server/lib/rules/slo_burn_rate/executor.ts#:~:text=alertFactory), [executor.test.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability/server/lib/rules/slo_burn_rate/executor.test.ts#:~:text=alertFactory) | - | | | [plugin.ts](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability/public/plugin.ts#:~:text=license%24) | 8.8.0 | | | [render_cell_value.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability/public/components/alerts_table/slo/render_cell_value.tsx#:~:text=DeprecatedCellValueElementProps), [render_cell_value.tsx](https://github.com/elastic/kibana/tree/main/x-pack/plugins/observability/public/components/alerts_table/slo/render_cell_value.tsx#:~:text=DeprecatedCellValueElementProps) | - | diff --git a/api_docs/deprecations_by_team.mdx b/api_docs/deprecations_by_team.mdx index 1034f7d7a7913..11a116cd2c798 100644 --- a/api_docs/deprecations_by_team.mdx +++ b/api_docs/deprecations_by_team.mdx @@ -7,7 +7,7 @@ id: kibDevDocsDeprecationsDueByTeam slug: /kibana-dev-docs/api-meta/deprecations-due-by-team title: Deprecated APIs due to be removed, by team description: Lists the teams that are referencing deprecated APIs with a remove by date. -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana'] --- diff --git a/api_docs/dev_tools.mdx b/api_docs/dev_tools.mdx index 09a928cf60480..3f980b57ee13c 100644 --- a/api_docs/dev_tools.mdx +++ b/api_docs/dev_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/devTools title: "devTools" image: https://source.unsplash.com/400x175/?github description: API docs for the devTools plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'devTools'] --- import devToolsObj from './dev_tools.devdocs.json'; diff --git a/api_docs/discover.devdocs.json b/api_docs/discover.devdocs.json index 5b01be8304df7..222ba19a62be0 100644 --- a/api_docs/discover.devdocs.json +++ b/api_docs/discover.devdocs.json @@ -21,6 +21,54 @@ "children": [], "returnComment": [], "initialIsOpen": false + }, + { + "parentPluginId": "discover", + "id": "def-public.LogExplorerTabs", + "type": "Function", + "tags": [], + "label": "LogExplorerTabs", + "description": [], + "signature": [ + "React.ForwardRefExoticComponent<", + { + "pluginId": "discover", + "scope": "public", + "docId": "kibDiscoverPluginApi", + "section": "def-public.LogExplorerTabsProps", + "text": "LogExplorerTabsProps" + }, + " & ", + { + "pluginId": "@kbn/shared-ux-utility", + "scope": "common", + "docId": "kibKbnSharedUxUtilityPluginApi", + "section": "def-common.WithSuspenseExtendedDeps", + "text": "WithSuspenseExtendedDeps" + }, + " & React.RefAttributes<{}>>" + ], + "path": "src/plugins/discover/public/components/log_explorer_tabs/index.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "discover", + "id": "def-public.LogExplorerTabs.$1", + "type": "Uncategorized", + "tags": [], + "label": "props", + "description": [], + "signature": [ + "P" + ], + "path": "node_modules/@types/react/index.d.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false } ], "interfaces": [ @@ -710,6 +758,22 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "discover", + "id": "def-public.DiscoverStateContainer.customizationContext", + "type": "Object", + "tags": [], + "label": "customizationContext", + "description": [ + "\nContext object for customization related properties" + ], + "signature": [ + "DiscoverCustomizationContext" + ], + "path": "src/plugins/discover/public/application/main/services/discover_state.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "discover", "id": "def-public.DiscoverStateContainer.actions", @@ -802,7 +866,15 @@ "section": "def-public.SavedSearch", "text": "SavedSearch" }, - ">; updateAdHocDataViewId: () => void; }" + ">; updateAdHocDataViewId: () => Promise<", + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataView", + "text": "DataView" + }, + " | undefined>; }" ], "path": "src/plugins/discover/public/application/main/services/discover_state.ts", "deprecated": false, @@ -811,99 +883,6 @@ ], "initialIsOpen": false }, - { - "parentPluginId": "discover", - "id": "def-public.FlyoutContentActions", - "type": "Interface", - "tags": [], - "label": "FlyoutContentActions", - "description": [], - "path": "src/plugins/discover/public/customizations/customization_types/flyout_customization.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "discover", - "id": "def-public.FlyoutContentActions.addFilter", - "type": "Function", - "tags": [], - "label": "addFilter", - "description": [], - "signature": [ - "DocViewFilterFn", - " | undefined" - ], - "path": "src/plugins/discover/public/customizations/customization_types/flyout_customization.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "discover", - "id": "def-public.FlyoutContentActions.addColumn", - "type": "Function", - "tags": [], - "label": "addColumn", - "description": [], - "signature": [ - "(column: string) => void" - ], - "path": "src/plugins/discover/public/customizations/customization_types/flyout_customization.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "discover", - "id": "def-public.FlyoutContentActions.addColumn.$1", - "type": "string", - "tags": [], - "label": "column", - "description": [], - "signature": [ - "string" - ], - "path": "src/plugins/discover/public/customizations/customization_types/flyout_customization.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] - }, - { - "parentPluginId": "discover", - "id": "def-public.FlyoutContentActions.removeColumn", - "type": "Function", - "tags": [], - "label": "removeColumn", - "description": [], - "signature": [ - "(column: string) => void" - ], - "path": "src/plugins/discover/public/customizations/customization_types/flyout_customization.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "discover", - "id": "def-public.FlyoutContentActions.removeColumn.$1", - "type": "string", - "tags": [], - "label": "column", - "description": [], - "signature": [ - "string" - ], - "path": "src/plugins/discover/public/customizations/customization_types/flyout_customization.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] - } - ], - "initialIsOpen": false - }, { "parentPluginId": "discover", "id": "def-public.FlyoutContentProps", @@ -923,13 +902,9 @@ "label": "actions", "description": [], "signature": [ - { - "pluginId": "discover", - "scope": "public", - "docId": "kibDiscoverPluginApi", - "section": "def-public.FlyoutContentActions", - "text": "FlyoutContentActions" - } + "{ filter?: ", + "DocViewFilterFn", + " | undefined; onAddColumn?: ((columnName: string) => void) | undefined; onRemoveColumn?: ((columnName: string) => void) | undefined; }" ], "path": "src/plugins/discover/public/customizations/customization_types/flyout_customization.ts", "deprecated": false, @@ -1061,6 +1036,43 @@ "path": "src/plugins/discover/public/customizations/customization_types/flyout_customization.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "discover", + "id": "def-public.FlyoutCustomization.docViewsRegistry", + "type": "CompoundType", + "tags": [], + "label": "docViewsRegistry", + "description": [], + "signature": [ + { + "pluginId": "@kbn/unified-doc-viewer", + "scope": "common", + "docId": "kibKbnUnifiedDocViewerPluginApi", + "section": "def-common.DocViewsRegistry", + "text": "DocViewsRegistry" + }, + " | ((prevRegistry: ", + { + "pluginId": "@kbn/unified-doc-viewer", + "scope": "common", + "docId": "kibKbnUnifiedDocViewerPluginApi", + "section": "def-common.DocViewsRegistry", + "text": "DocViewsRegistry" + }, + ") => ", + { + "pluginId": "@kbn/unified-doc-viewer", + "scope": "common", + "docId": "kibKbnUnifiedDocViewerPluginApi", + "section": "def-common.DocViewsRegistry", + "text": "DocViewsRegistry" + }, + ") | undefined" + ], + "path": "src/plugins/discover/public/customizations/customization_types/flyout_customization.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -1147,6 +1159,56 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "discover", + "id": "def-public.LogExplorerTabsProps", + "type": "Interface", + "tags": [], + "label": "LogExplorerTabsProps", + "description": [], + "path": "src/plugins/discover/public/components/log_explorer_tabs/log_explorer_tabs.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "discover", + "id": "def-public.LogExplorerTabsProps.services", + "type": "Object", + "tags": [], + "label": "services", + "description": [], + "signature": [ + "{ share?: ", + { + "pluginId": "share", + "scope": "public", + "docId": "kibSharePluginApi", + "section": "def-public.SharePublicStart", + "text": "SharePublicStart" + }, + " | undefined; }" + ], + "path": "src/plugins/discover/public/components/log_explorer_tabs/log_explorer_tabs.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "discover", + "id": "def-public.LogExplorerTabsProps.selectedTab", + "type": "CompoundType", + "tags": [], + "label": "selectedTab", + "description": [], + "signature": [ + "\"discover\" | \"log-explorer\"" + ], + "path": "src/plugins/discover/public/components/log_explorer_tabs/log_explorer_tabs.tsx", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "discover", "id": "def-public.SearchBarCustomization", @@ -1404,7 +1466,15 @@ "section": "def-public.CustomizationCallback", "text": "CustomizationCallback" }, - "[]; }" + "[]; stateStorageContainer?: ", + { + "pluginId": "kibanaUtils", + "scope": "public", + "docId": "kibKibanaUtilsPluginApi", + "section": "def-public.IKbnUrlStateStorage", + "text": "IKbnUrlStateStorage" + }, + " | undefined; }" ], "path": "src/plugins/discover/public/components/discover_container/index.ts", "deprecated": false, @@ -1645,6 +1715,22 @@ "path": "src/plugins/discover/public/plugin.tsx", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "discover", + "id": "def-public.DiscoverSetup.showLogExplorerTabs", + "type": "Function", + "tags": [], + "label": "showLogExplorerTabs", + "description": [], + "signature": [ + "() => void" + ], + "path": "src/plugins/discover/public/plugin.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] } ], "lifecycle": "setup", @@ -2312,18 +2398,6 @@ "plugin": "dataVisualizer", "path": "x-pack/plugins/data_visualizer/public/application/index_data_visualizer/components/actions_panel/actions_panel.tsx" }, - { - "plugin": "cloudSecurityPosture", - "path": "x-pack/plugins/cloud_security_posture/public/pages/configurations/findings_flyout/overview_tab.tsx" - }, - { - "plugin": "discoverEnhanced", - "path": "x-pack/plugins/discover_enhanced/public/actions/explore_data/explore_data_context_menu_action.ts" - }, - { - "plugin": "discoverEnhanced", - "path": "x-pack/plugins/discover_enhanced/public/actions/explore_data/explore_data_chart_action.ts" - }, { "plugin": "osquery", "path": "x-pack/plugins/osquery/public/packs/pack_queries_status_table.tsx" @@ -2336,6 +2410,18 @@ "plugin": "osquery", "path": "x-pack/plugins/osquery/public/common/hooks/use_discover_link.tsx" }, + { + "plugin": "cloudSecurityPosture", + "path": "x-pack/plugins/cloud_security_posture/public/pages/configurations/findings_flyout/overview_tab.tsx" + }, + { + "plugin": "discoverEnhanced", + "path": "x-pack/plugins/discover_enhanced/public/actions/explore_data/explore_data_context_menu_action.ts" + }, + { + "plugin": "discoverEnhanced", + "path": "x-pack/plugins/discover_enhanced/public/actions/explore_data/explore_data_chart_action.ts" + }, { "plugin": "synthetics", "path": "x-pack/plugins/synthetics/public/apps/synthetics/components/common/components/stderr_logs.tsx" diff --git a/api_docs/discover.mdx b/api_docs/discover.mdx index 82b7eaaa113b5..2cc3945fba056 100644 --- a/api_docs/discover.mdx +++ b/api_docs/discover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discover title: "discover" image: https://source.unsplash.com/400x175/?github description: API docs for the discover plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'discover'] --- import discoverObj from './discover.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 134 | 0 | 91 | 20 | +| 136 | 0 | 91 | 21 | ## Client diff --git a/api_docs/discover_enhanced.devdocs.json b/api_docs/discover_enhanced.devdocs.json index f0194c72301a8..080bcfd52756d 100644 --- a/api_docs/discover_enhanced.devdocs.json +++ b/api_docs/discover_enhanced.devdocs.json @@ -622,8 +622,8 @@ "pluginId": "share", "scope": "public", "docId": "kibSharePluginApi", - "section": "def-public.SharePluginSetup", - "text": "SharePluginSetup" + "section": "def-public.SharePublicSetup", + "text": "SharePublicSetup" }, " | undefined" ], @@ -743,8 +743,8 @@ "pluginId": "share", "scope": "public", "docId": "kibSharePluginApi", - "section": "def-public.SharePluginStart", - "text": "SharePluginStart" + "section": "def-public.SharePublicStart", + "text": "SharePublicStart" }, " | undefined" ], diff --git a/api_docs/discover_enhanced.mdx b/api_docs/discover_enhanced.mdx index db477e06bc2ca..16b1c3d808b2d 100644 --- a/api_docs/discover_enhanced.mdx +++ b/api_docs/discover_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/discoverEnhanced title: "discoverEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the discoverEnhanced plugin -date: 2023-12-06 +date: 2023-12-19 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 165de38235513..7af190927d645 100644 --- a/api_docs/ecs_data_quality_dashboard.mdx +++ b/api_docs/ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ecsDataQualityDashboard title: "ecsDataQualityDashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the ecsDataQualityDashboard plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'ecsDataQualityDashboard'] --- import ecsDataQualityDashboardObj from './ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/elastic_assistant.devdocs.json b/api_docs/elastic_assistant.devdocs.json index 9a3dd4987b4a5..0f18338ae23fe 100644 --- a/api_docs/elastic_assistant.devdocs.json +++ b/api_docs/elastic_assistant.devdocs.json @@ -11,7 +11,1677 @@ "server": { "classes": [], "functions": [], - "interfaces": [], + "interfaces": [ + { + "parentPluginId": "elasticAssistant", + "id": "def-server.AssistantTool", + "type": "Interface", + "tags": [], + "label": "AssistantTool", + "description": [ + "\nInterfaces for registering tools to be used by the elastic assistant" + ], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "elasticAssistant", + "id": "def-server.AssistantTool.id", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "elasticAssistant", + "id": "def-server.AssistantTool.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "elasticAssistant", + "id": "def-server.AssistantTool.description", + "type": "string", + "tags": [], + "label": "description", + "description": [], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "elasticAssistant", + "id": "def-server.AssistantTool.sourceRegister", + "type": "string", + "tags": [], + "label": "sourceRegister", + "description": [], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "elasticAssistant", + "id": "def-server.AssistantTool.isSupported", + "type": "Function", + "tags": [], + "label": "isSupported", + "description": [], + "signature": [ + "(params: ", + { + "pluginId": "elasticAssistant", + "scope": "server", + "docId": "kibElasticAssistantPluginApi", + "section": "def-server.AssistantToolParams", + "text": "AssistantToolParams" + }, + ") => boolean" + ], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "elasticAssistant", + "id": "def-server.AssistantTool.isSupported.$1", + "type": "Object", + "tags": [], + "label": "params", + "description": [], + "signature": [ + { + "pluginId": "elasticAssistant", + "scope": "server", + "docId": "kibElasticAssistantPluginApi", + "section": "def-server.AssistantToolParams", + "text": "AssistantToolParams" + } + ], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "elasticAssistant", + "id": "def-server.AssistantTool.getTool", + "type": "Function", + "tags": [], + "label": "getTool", + "description": [], + "signature": [ + "(params: ", + { + "pluginId": "elasticAssistant", + "scope": "server", + "docId": "kibElasticAssistantPluginApi", + "section": "def-server.AssistantToolParams", + "text": "AssistantToolParams" + }, + ") => ", + "Tool", + " | null" + ], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "elasticAssistant", + "id": "def-server.AssistantTool.getTool.$1", + "type": "Object", + "tags": [], + "label": "params", + "description": [], + "signature": [ + { + "pluginId": "elasticAssistant", + "scope": "server", + "docId": "kibElasticAssistantPluginApi", + "section": "def-server.AssistantToolParams", + "text": "AssistantToolParams" + } + ], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "elasticAssistant", + "id": "def-server.AssistantToolParams", + "type": "Interface", + "tags": [], + "label": "AssistantToolParams", + "description": [], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "elasticAssistant", + "id": "def-server.AssistantToolParams.alertsIndexPattern", + "type": "string", + "tags": [], + "label": "alertsIndexPattern", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "elasticAssistant", + "id": "def-server.AssistantToolParams.allow", + "type": "Array", + "tags": [], + "label": "allow", + "description": [], + "signature": [ + "string[] | undefined" + ], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "elasticAssistant", + "id": "def-server.AssistantToolParams.allowReplacement", + "type": "Array", + "tags": [], + "label": "allowReplacement", + "description": [], + "signature": [ + "string[] | undefined" + ], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "elasticAssistant", + "id": "def-server.AssistantToolParams.assistantLangChain", + "type": "boolean", + "tags": [], + "label": "assistantLangChain", + "description": [], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "elasticAssistant", + "id": "def-server.AssistantToolParams.chain", + "type": "Object", + "tags": [], + "label": "chain", + "description": [], + "signature": [ + "RetrievalQAChain" + ], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "elasticAssistant", + "id": "def-server.AssistantToolParams.esClient", + "type": "Object", + "tags": [], + "label": "esClient", + "description": [], + "signature": [ + "{ create: { (this: That, params: ", + "CreateRequest", + " | ", + "CreateRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "WriteResponseBase", + ">; (this: That, params: ", + "CreateRequest", + " | ", + "CreateRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "WriteResponseBase", + ", unknown>>; (this: That, params: ", + "CreateRequest", + " | ", + "CreateRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "WriteResponseBase", + ">; }; update: { (this: That, params: ", + "UpdateRequest", + " | ", + "UpdateRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "UpdateResponse", + ">; (this: That, params: ", + "UpdateRequest", + " | ", + "UpdateRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "UpdateResponse", + ", unknown>>; (this: That, params: ", + "UpdateRequest", + " | ", + "UpdateRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "UpdateResponse", + ">; }; get: { (this: That, params: ", + "GetRequest", + " | ", + "GetRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "GetResponse", + ">; (this: That, params: ", + "GetRequest", + " | ", + "GetRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "GetResponse", + ", unknown>>; (this: That, params: ", + "GetRequest", + " | ", + "GetRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "GetResponse", + ">; }; delete: { (this: That, params: ", + "DeleteRequest", + " | ", + "DeleteRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "WriteResponseBase", + ">; (this: That, params: ", + "DeleteRequest", + " | ", + "DeleteRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "WriteResponseBase", + ", unknown>>; (this: That, params: ", + "DeleteRequest", + " | ", + "DeleteRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "WriteResponseBase", + ">; }; search: { >(this: That, params?: ", + "SearchRequest", + " | ", + "SearchRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "SearchResponse", + ">; >(this: That, params?: ", + "SearchRequest", + " | ", + "SearchRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "SearchResponse", + ", unknown>>; >(this: That, params?: ", + "SearchRequest", + " | ", + "SearchRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "SearchResponse", + ">; }; helpers: ", + "default", + "; name: string | symbol; [kAsyncSearch]: symbol | null; [kAutoscaling]: symbol | null; [kCat]: symbol | null; [kCcr]: symbol | null; [kCluster]: symbol | null; [kDanglingIndices]: symbol | null; [kEnrich]: symbol | null; [kEql]: symbol | null; [kFeatures]: symbol | null; [kFleet]: symbol | null; [kGraph]: symbol | null; [kIlm]: symbol | null; [kIndices]: symbol | null; [kIngest]: symbol | null; [kLicense]: symbol | null; [kLogstash]: symbol | null; [kMigration]: symbol | null; [kMl]: symbol | null; [kMonitoring]: symbol | null; [kNodes]: symbol | null; [kRollup]: symbol | null; [kSearchApplication]: symbol | null; [kSearchableSnapshots]: symbol | null; [kSecurity]: symbol | null; [kShutdown]: symbol | null; [kSlm]: symbol | null; [kSnapshot]: symbol | null; [kSql]: symbol | null; [kSsl]: symbol | null; [kSynonyms]: symbol | null; [kTasks]: symbol | null; [kTextStructure]: symbol | null; [kTransform]: symbol | null; [kWatcher]: symbol | null; [kXpack]: symbol | null; transport: ", + "default", + "; child: (opts: ", + "ClientOptions", + ") => ", + "default", + "; asyncSearch: ", + "default", + "; autoscaling: ", + "default", + "; bulk: { (this: That, params: ", + "BulkRequest", + " | ", + "BulkRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "BulkResponse", + ">; (this: That, params: ", + "BulkRequest", + " | ", + "BulkRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "BulkResponse", + ", unknown>>; (this: That, params: ", + "BulkRequest", + " | ", + "BulkRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "BulkResponse", + ">; }; cat: ", + "default", + "; ccr: ", + "default", + "; clearScroll: { (this: That, params?: ", + "ClearScrollRequest", + " | ", + "ClearScrollRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ClearScrollResponse", + ">; (this: That, params?: ", + "ClearScrollRequest", + " | ", + "ClearScrollRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ClearScrollResponse", + ", unknown>>; (this: That, params?: ", + "ClearScrollRequest", + " | ", + "ClearScrollRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ClearScrollResponse", + ">; }; closePointInTime: { (this: That, params: ", + "ClosePointInTimeRequest", + " | ", + "ClosePointInTimeRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ClosePointInTimeResponse", + ">; (this: That, params: ", + "ClosePointInTimeRequest", + " | ", + "ClosePointInTimeRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ClosePointInTimeResponse", + ", unknown>>; (this: That, params: ", + "ClosePointInTimeRequest", + " | ", + "ClosePointInTimeRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ClosePointInTimeResponse", + ">; }; cluster: ", + "default", + "; count: { (this: That, params?: ", + "CountRequest", + " | ", + "CountRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "CountResponse", + ">; (this: That, params?: ", + "CountRequest", + " | ", + "CountRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "CountResponse", + ", unknown>>; (this: That, params?: ", + "CountRequest", + " | ", + "CountRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "CountResponse", + ">; }; danglingIndices: ", + "default", + "; deleteByQuery: { (this: That, params: ", + "DeleteByQueryRequest", + " | ", + "DeleteByQueryRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "DeleteByQueryResponse", + ">; (this: That, params: ", + "DeleteByQueryRequest", + " | ", + "DeleteByQueryRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "DeleteByQueryResponse", + ", unknown>>; (this: That, params: ", + "DeleteByQueryRequest", + " | ", + "DeleteByQueryRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "DeleteByQueryResponse", + ">; }; deleteByQueryRethrottle: { (this: That, params: ", + "DeleteByQueryRethrottleRequest", + " | ", + "DeleteByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "TasksTaskListResponseBase", + ">; (this: That, params: ", + "DeleteByQueryRethrottleRequest", + " | ", + "DeleteByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "TasksTaskListResponseBase", + ", unknown>>; (this: That, params: ", + "DeleteByQueryRethrottleRequest", + " | ", + "DeleteByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "TasksTaskListResponseBase", + ">; }; deleteScript: { (this: That, params: ", + "DeleteScriptRequest", + " | ", + "DeleteScriptRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "AcknowledgedResponseBase", + ">; (this: That, params: ", + "DeleteScriptRequest", + " | ", + "DeleteScriptRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "AcknowledgedResponseBase", + ", unknown>>; (this: That, params: ", + "DeleteScriptRequest", + " | ", + "DeleteScriptRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "AcknowledgedResponseBase", + ">; }; enrich: ", + "default", + "; eql: ", + "default", + "; exists: { (this: That, params: ", + "ExistsRequest", + " | ", + "ExistsRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise; (this: That, params: ", + "ExistsRequest", + " | ", + "ExistsRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + ">; (this: That, params: ", + "ExistsRequest", + " | ", + "ExistsRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise; }; existsSource: { (this: That, params: ", + "ExistsSourceRequest", + " | ", + "ExistsSourceRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise; (this: That, params: ", + "ExistsSourceRequest", + " | ", + "ExistsSourceRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + ">; (this: That, params: ", + "ExistsSourceRequest", + " | ", + "ExistsSourceRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise; }; explain: { (this: That, params: ", + "ExplainRequest", + " | ", + "ExplainRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ExplainResponse", + ">; (this: That, params: ", + "ExplainRequest", + " | ", + "ExplainRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ExplainResponse", + ", unknown>>; (this: That, params: ", + "ExplainRequest", + " | ", + "ExplainRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ExplainResponse", + ">; }; features: ", + "default", + "; fieldCaps: { (this: That, params?: ", + "FieldCapsRequest", + " | ", + "FieldCapsRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "FieldCapsResponse", + ">; (this: That, params?: ", + "FieldCapsRequest", + " | ", + "FieldCapsRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "FieldCapsResponse", + ", unknown>>; (this: That, params?: ", + "FieldCapsRequest", + " | ", + "FieldCapsRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "FieldCapsResponse", + ">; }; fleet: ", + "default", + "; getScript: { (this: That, params: ", + "GetScriptRequest", + " | ", + "GetScriptRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "GetScriptResponse", + ">; (this: That, params: ", + "GetScriptRequest", + " | ", + "GetScriptRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "GetScriptResponse", + ", unknown>>; (this: That, params: ", + "GetScriptRequest", + " | ", + "GetScriptRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "GetScriptResponse", + ">; }; getScriptContext: { (this: That, params?: ", + "GetScriptContextRequest", + " | ", + "GetScriptContextRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "GetScriptContextResponse", + ">; (this: That, params?: ", + "GetScriptContextRequest", + " | ", + "GetScriptContextRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "GetScriptContextResponse", + ", unknown>>; (this: That, params?: ", + "GetScriptContextRequest", + " | ", + "GetScriptContextRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "GetScriptContextResponse", + ">; }; getScriptLanguages: { (this: That, params?: ", + "GetScriptLanguagesRequest", + " | ", + "GetScriptLanguagesRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "GetScriptLanguagesResponse", + ">; (this: That, params?: ", + "GetScriptLanguagesRequest", + " | ", + "GetScriptLanguagesRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "GetScriptLanguagesResponse", + ", unknown>>; (this: That, params?: ", + "GetScriptLanguagesRequest", + " | ", + "GetScriptLanguagesRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "GetScriptLanguagesResponse", + ">; }; getSource: { (this: That, params: ", + "GetSourceRequest", + " | ", + "GetSourceRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise; (this: That, params: ", + "GetSourceRequest", + " | ", + "GetSourceRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + ">; (this: That, params: ", + "GetSourceRequest", + " | ", + "GetSourceRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise; }; graph: ", + "default", + "; healthReport: { (this: That, params?: ", + "HealthReportRequest", + " | ", + "HealthReportRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "HealthReportResponse", + ">; (this: That, params?: ", + "HealthReportRequest", + " | ", + "HealthReportRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "HealthReportResponse", + ", unknown>>; (this: That, params?: ", + "HealthReportRequest", + " | ", + "HealthReportRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "HealthReportResponse", + ">; }; ilm: ", + "default", + "; index: { (this: That, params: ", + "IndexRequest", + " | ", + "IndexRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "WriteResponseBase", + ">; (this: That, params: ", + "IndexRequest", + " | ", + "IndexRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "WriteResponseBase", + ", unknown>>; (this: That, params: ", + "IndexRequest", + " | ", + "IndexRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "WriteResponseBase", + ">; }; indices: ", + "default", + "; info: { (this: That, params?: ", + "InfoRequest", + " | ", + "InfoRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "InfoResponse", + ">; (this: That, params?: ", + "InfoRequest", + " | ", + "InfoRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "InfoResponse", + ", unknown>>; (this: That, params?: ", + "InfoRequest", + " | ", + "InfoRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "InfoResponse", + ">; }; ingest: ", + "default", + "; knnSearch: { (this: That, params: ", + "KnnSearchRequest", + " | ", + "KnnSearchRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "KnnSearchResponse", + ">; (this: That, params: ", + "KnnSearchRequest", + " | ", + "KnnSearchRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "KnnSearchResponse", + ", unknown>>; (this: That, params: ", + "KnnSearchRequest", + " | ", + "KnnSearchRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "KnnSearchResponse", + ">; }; license: ", + "default", + "; logstash: ", + "default", + "; mget: { (this: That, params?: ", + "MgetRequest", + " | ", + "MgetRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "MgetResponse", + ">; (this: That, params?: ", + "MgetRequest", + " | ", + "MgetRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "MgetResponse", + ", unknown>>; (this: That, params?: ", + "MgetRequest", + " | ", + "MgetRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "MgetResponse", + ">; }; migration: ", + "default", + "; ml: ", + "default", + "; monitoring: ", + "default", + "; msearch: { >(this: That, params: ", + "MsearchRequest", + " | ", + "MsearchRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "MsearchResponse", + ">; >(this: That, params: ", + "MsearchRequest", + " | ", + "MsearchRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "MsearchResponse", + ", unknown>>; >(this: That, params: ", + "MsearchRequest", + " | ", + "MsearchRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "MsearchResponse", + ">; }; msearchTemplate: { >(this: That, params: ", + "MsearchTemplateRequest", + " | ", + "MsearchTemplateRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "MsearchTemplateResponse", + ">; >(this: That, params: ", + "MsearchTemplateRequest", + " | ", + "MsearchTemplateRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "MsearchTemplateResponse", + ", unknown>>; >(this: That, params: ", + "MsearchTemplateRequest", + " | ", + "MsearchTemplateRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "MsearchTemplateResponse", + ">; }; mtermvectors: { (this: That, params?: ", + "MtermvectorsRequest", + " | ", + "MtermvectorsRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "MtermvectorsResponse", + ">; (this: That, params?: ", + "MtermvectorsRequest", + " | ", + "MtermvectorsRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "MtermvectorsResponse", + ", unknown>>; (this: That, params?: ", + "MtermvectorsRequest", + " | ", + "MtermvectorsRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "MtermvectorsResponse", + ">; }; nodes: ", + "default", + "; openPointInTime: { (this: That, params: ", + "OpenPointInTimeRequest", + " | ", + "OpenPointInTimeRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "OpenPointInTimeResponse", + ">; (this: That, params: ", + "OpenPointInTimeRequest", + " | ", + "OpenPointInTimeRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "OpenPointInTimeResponse", + ", unknown>>; (this: That, params: ", + "OpenPointInTimeRequest", + " | ", + "OpenPointInTimeRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "OpenPointInTimeResponse", + ">; }; ping: { (this: That, params?: ", + "PingRequest", + " | ", + "PingRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise; (this: That, params?: ", + "PingRequest", + " | ", + "PingRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + ">; (this: That, params?: ", + "PingRequest", + " | ", + "PingRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise; }; putScript: { (this: That, params: ", + "PutScriptRequest", + " | ", + "PutScriptRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "AcknowledgedResponseBase", + ">; (this: That, params: ", + "PutScriptRequest", + " | ", + "PutScriptRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "AcknowledgedResponseBase", + ", unknown>>; (this: That, params: ", + "PutScriptRequest", + " | ", + "PutScriptRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "AcknowledgedResponseBase", + ">; }; rankEval: { (this: That, params: ", + "RankEvalRequest", + " | ", + "RankEvalRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "RankEvalResponse", + ">; (this: That, params: ", + "RankEvalRequest", + " | ", + "RankEvalRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "RankEvalResponse", + ", unknown>>; (this: That, params: ", + "RankEvalRequest", + " | ", + "RankEvalRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "RankEvalResponse", + ">; }; reindex: { (this: That, params: ", + "ReindexRequest", + " | ", + "ReindexRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ReindexResponse", + ">; (this: That, params: ", + "ReindexRequest", + " | ", + "ReindexRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ReindexResponse", + ", unknown>>; (this: That, params: ", + "ReindexRequest", + " | ", + "ReindexRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ReindexResponse", + ">; }; reindexRethrottle: { (this: That, params: ", + "ReindexRethrottleRequest", + " | ", + "ReindexRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ReindexRethrottleResponse", + ">; (this: That, params: ", + "ReindexRethrottleRequest", + " | ", + "ReindexRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ReindexRethrottleResponse", + ", unknown>>; (this: That, params: ", + "ReindexRethrottleRequest", + " | ", + "ReindexRethrottleRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ReindexRethrottleResponse", + ">; }; renderSearchTemplate: { (this: That, params?: ", + "RenderSearchTemplateRequest", + " | ", + "RenderSearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "RenderSearchTemplateResponse", + ">; (this: That, params?: ", + "RenderSearchTemplateRequest", + " | ", + "RenderSearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "RenderSearchTemplateResponse", + ", unknown>>; (this: That, params?: ", + "RenderSearchTemplateRequest", + " | ", + "RenderSearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "RenderSearchTemplateResponse", + ">; }; rollup: ", + "default", + "; scriptsPainlessExecute: { (this: That, params?: ", + "ScriptsPainlessExecuteRequest", + " | ", + "ScriptsPainlessExecuteRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ScriptsPainlessExecuteResponse", + ">; (this: That, params?: ", + "ScriptsPainlessExecuteRequest", + " | ", + "ScriptsPainlessExecuteRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ScriptsPainlessExecuteResponse", + ", unknown>>; (this: That, params?: ", + "ScriptsPainlessExecuteRequest", + " | ", + "ScriptsPainlessExecuteRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ScriptsPainlessExecuteResponse", + ">; }; scroll: { >(this: That, params: ", + "ScrollRequest", + " | ", + "ScrollRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "ScrollResponse", + ">; >(this: That, params: ", + "ScrollRequest", + " | ", + "ScrollRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "ScrollResponse", + ", unknown>>; >(this: That, params: ", + "ScrollRequest", + " | ", + "ScrollRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "ScrollResponse", + ">; }; searchApplication: ", + "default", + "; searchMvt: { (this: That, params: ", + "SearchMvtRequest", + " | ", + "SearchMvtRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise; (this: That, params: ", + "SearchMvtRequest", + " | ", + "SearchMvtRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + ">; (this: That, params: ", + "SearchMvtRequest", + " | ", + "SearchMvtRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise; }; searchShards: { (this: That, params?: ", + "SearchShardsRequest", + " | ", + "SearchShardsRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "SearchShardsResponse", + ">; (this: That, params?: ", + "SearchShardsRequest", + " | ", + "SearchShardsRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "SearchShardsResponse", + ", unknown>>; (this: That, params?: ", + "SearchShardsRequest", + " | ", + "SearchShardsRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "SearchShardsResponse", + ">; }; searchTemplate: { (this: That, params?: ", + "SearchTemplateRequest", + " | ", + "SearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "SearchTemplateResponse", + ">; (this: That, params?: ", + "SearchTemplateRequest", + " | ", + "SearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "SearchTemplateResponse", + ", unknown>>; (this: That, params?: ", + "SearchTemplateRequest", + " | ", + "SearchTemplateRequest", + " | undefined, options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "SearchTemplateResponse", + ">; }; searchableSnapshots: ", + "default", + "; security: ", + "default", + "; shutdown: ", + "default", + "; slm: ", + "default", + "; snapshot: ", + "default", + "; sql: ", + "default", + "; ssl: ", + "default", + "; synonyms: ", + "default", + "; tasks: ", + "default", + "; termsEnum: { (this: That, params: ", + "TermsEnumRequest", + " | ", + "TermsEnumRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "TermsEnumResponse", + ">; (this: That, params: ", + "TermsEnumRequest", + " | ", + "TermsEnumRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "TermsEnumResponse", + ", unknown>>; (this: That, params: ", + "TermsEnumRequest", + " | ", + "TermsEnumRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "TermsEnumResponse", + ">; }; termvectors: { (this: That, params: ", + "TermvectorsRequest", + " | ", + "TermvectorsRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "TermvectorsResponse", + ">; (this: That, params: ", + "TermvectorsRequest", + " | ", + "TermvectorsRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "TermvectorsResponse", + ", unknown>>; (this: That, params: ", + "TermvectorsRequest", + " | ", + "TermvectorsRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "TermvectorsResponse", + ">; }; textStructure: ", + "default", + "; transform: ", + "default", + "; updateByQuery: { (this: That, params: ", + "UpdateByQueryRequest", + " | ", + "UpdateByQueryRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "UpdateByQueryResponse", + ">; (this: That, params: ", + "UpdateByQueryRequest", + " | ", + "UpdateByQueryRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "UpdateByQueryResponse", + ", unknown>>; (this: That, params: ", + "UpdateByQueryRequest", + " | ", + "UpdateByQueryRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "UpdateByQueryResponse", + ">; }; updateByQueryRethrottle: { (this: That, params: ", + "UpdateByQueryRethrottleRequest", + " | ", + "UpdateByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithOutMeta", + " | undefined): Promise<", + "UpdateByQueryRethrottleResponse", + ">; (this: That, params: ", + "UpdateByQueryRethrottleRequest", + " | ", + "UpdateByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptionsWithMeta", + " | undefined): Promise<", + "TransportResult", + "<", + "UpdateByQueryRethrottleResponse", + ", unknown>>; (this: That, params: ", + "UpdateByQueryRethrottleRequest", + " | ", + "UpdateByQueryRethrottleRequest", + ", options?: ", + "TransportRequestOptions", + " | undefined): Promise<", + "UpdateByQueryRethrottleResponse", + ">; }; watcher: ", + "default", + "; xpack: ", + "default", + "; }" + ], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "elasticAssistant", + "id": "def-server.AssistantToolParams.modelExists", + "type": "boolean", + "tags": [], + "label": "modelExists", + "description": [], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "elasticAssistant", + "id": "def-server.AssistantToolParams.onNewReplacements", + "type": "Function", + "tags": [], + "label": "onNewReplacements", + "description": [], + "signature": [ + "((newReplacements: Record) => void) | undefined" + ], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "elasticAssistant", + "id": "def-server.AssistantToolParams.onNewReplacements.$1", + "type": "Object", + "tags": [], + "label": "newReplacements", + "description": [], + "signature": [ + "Record" + ], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "elasticAssistant", + "id": "def-server.AssistantToolParams.replacements", + "type": "Object", + "tags": [], + "label": "replacements", + "description": [], + "signature": [ + "Record | undefined" + ], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "elasticAssistant", + "id": "def-server.AssistantToolParams.request", + "type": "Object", + "tags": [], + "label": "request", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + "" + ], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "elasticAssistant", + "id": "def-server.AssistantToolParams.size", + "type": "number", + "tags": [], + "label": "size", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "elasticAssistant", + "id": "def-server.ElasticAssistantPluginSetupDependencies", + "type": "Interface", + "tags": [], + "label": "ElasticAssistantPluginSetupDependencies", + "description": [], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "elasticAssistant", + "id": "def-server.ElasticAssistantPluginSetupDependencies.actions", + "type": "Object", + "tags": [], + "label": "actions", + "description": [], + "signature": [ + { + "pluginId": "actions", + "scope": "server", + "docId": "kibActionsPluginApi", + "section": "def-server.PluginSetupContract", + "text": "PluginSetupContract" + } + ], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "elasticAssistant", + "id": "def-server.ElasticAssistantPluginSetupDependencies.ml", + "type": "CompoundType", + "tags": [], + "label": "ml", + "description": [], + "signature": [ + "JobServiceProvider", + " & ", + "AnomalyDetectorsProvider", + " & ", + "MlSystemProvider", + " & ", + "ModulesProvider", + " & ", + "ResultsServiceProvider", + " & { alertingServiceProvider(savedObjectsClient: ", + { + "pluginId": "@kbn/core-saved-objects-api-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsApiServerPluginApi", + "section": "def-common.SavedObjectsClientContract", + "text": "SavedObjectsClientContract" + }, + ", request: ", + { + "pluginId": "@kbn/core-http-server", + "scope": "common", + "docId": "kibKbnCoreHttpServerPluginApi", + "section": "def-common.KibanaRequest", + "text": "KibanaRequest" + }, + "): { preview: (args_0: Readonly<{} & { timeRange: string; alertParams: Readonly<{} & { severity: number; jobSelection: Readonly<{} & { groupIds: string[]; jobIds: string[]; }>; resultType: \"bucket\" | \"record\" | \"influencer\"; includeInterim: boolean; lookbackInterval: string | null; topNBuckets: number | null; }>; sampleSize: number; }>) => Promise; execute: (params: Readonly<{} & { severity: number; jobSelection: Readonly<{} & { groupIds: string[]; jobIds: string[]; }>; resultType: \"bucket\" | \"record\" | \"influencer\"; includeInterim: boolean; lookbackInterval: string | null; topNBuckets: number | null; }>, spaceId: string) => Promise<{ payload: ", + "AnomalyDetectionAlertPayload", + "; context: ", + "AnomalyDetectionAlertContext", + "; name: string; isHealthy: boolean; } | undefined>; }; } & ", + "TrainedModelsProvider" + ], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "elasticAssistant", + "id": "def-server.ElasticAssistantPluginStartDependencies", + "type": "Interface", + "tags": [], + "label": "ElasticAssistantPluginStartDependencies", + "description": [], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "elasticAssistant", + "id": "def-server.ElasticAssistantPluginStartDependencies.actions", + "type": "Object", + "tags": [], + "label": "actions", + "description": [], + "signature": [ + { + "pluginId": "actions", + "scope": "server", + "docId": "kibActionsPluginApi", + "section": "def-server.PluginStartContract", + "text": "PluginStartContract" + } + ], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ], "enums": [], "misc": [], "objects": [], @@ -84,6 +1754,114 @@ "path": "x-pack/plugins/elastic_assistant/server/types.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "elasticAssistant", + "id": "def-server.ElasticAssistantPluginStart.registerTools", + "type": "Function", + "tags": [], + "label": "registerTools", + "description": [ + "\nRegister tools to be used by the elastic assistant" + ], + "signature": [ + "(pluginName: string, tools: ", + { + "pluginId": "elasticAssistant", + "scope": "server", + "docId": "kibElasticAssistantPluginApi", + "section": "def-server.AssistantTool", + "text": "AssistantTool" + }, + "[]) => void" + ], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "elasticAssistant", + "id": "def-server.ElasticAssistantPluginStart.registerTools.$1", + "type": "string", + "tags": [], + "label": "pluginName", + "description": [ + "Name of the plugin the tool should be registered to" + ], + "signature": [ + "string" + ], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "elasticAssistant", + "id": "def-server.ElasticAssistantPluginStart.registerTools.$2", + "type": "Array", + "tags": [], + "label": "tools", + "description": [ + "AssistantTools to be registered with for the given plugin" + ], + "signature": [ + { + "pluginId": "elasticAssistant", + "scope": "server", + "docId": "kibElasticAssistantPluginApi", + "section": "def-server.AssistantTool", + "text": "AssistantTool" + }, + "[]" + ], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "elasticAssistant", + "id": "def-server.ElasticAssistantPluginStart.getRegisteredTools", + "type": "Function", + "tags": [], + "label": "getRegisteredTools", + "description": [ + "\nGet the registered tools" + ], + "signature": [ + "(pluginName: string) => ", + { + "pluginId": "elasticAssistant", + "scope": "server", + "docId": "kibElasticAssistantPluginApi", + "section": "def-server.AssistantTool", + "text": "AssistantTool" + }, + "[]" + ], + "path": "x-pack/plugins/elastic_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "elasticAssistant", + "id": "def-server.ElasticAssistantPluginStart.getRegisteredTools.$1", + "type": "string", + "tags": [], + "label": "pluginName", + "description": [ + "Name of the plugin to get the tools for" + ], + "path": "x-pack/plugins/elastic_assistant/server/services/app_context.ts", + "deprecated": false, + "trackAdoption": false + } + ] } ], "lifecycle": "start", diff --git a/api_docs/elastic_assistant.mdx b/api_docs/elastic_assistant.mdx index fa5e7b37b54cd..cd65a2e37d323 100644 --- a/api_docs/elastic_assistant.mdx +++ b/api_docs/elastic_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/elasticAssistant title: "elasticAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the elasticAssistant plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'elasticAssistant'] --- import elasticAssistantObj from './elastic_assistant.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/security-solution](https://github.com/orgs/elastic/teams/secur | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 4 | 0 | 2 | 0 | +| 36 | 0 | 28 | 0 | ## Server @@ -31,3 +31,6 @@ Contact [@elastic/security-solution](https://github.com/orgs/elastic/teams/secur ### Start +### Interfaces + + diff --git a/api_docs/embeddable.mdx b/api_docs/embeddable.mdx index 9cd4ba6b861bf..057014b818dfc 100644 --- a/api_docs/embeddable.mdx +++ b/api_docs/embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddable title: "embeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddable plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddable'] --- import embeddableObj from './embeddable.devdocs.json'; diff --git a/api_docs/embeddable_enhanced.mdx b/api_docs/embeddable_enhanced.mdx index 920d942760938..22bbf44ebcf0f 100644 --- a/api_docs/embeddable_enhanced.mdx +++ b/api_docs/embeddable_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/embeddableEnhanced title: "embeddableEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the embeddableEnhanced plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'embeddableEnhanced'] --- import embeddableEnhancedObj from './embeddable_enhanced.devdocs.json'; diff --git a/api_docs/encrypted_saved_objects.devdocs.json b/api_docs/encrypted_saved_objects.devdocs.json index 9cef856f653c5..a4ed7a2dc8ff1 100644 --- a/api_docs/encrypted_saved_objects.devdocs.json +++ b/api_docs/encrypted_saved_objects.devdocs.json @@ -315,7 +315,14 @@ "label": "doc", "description": [], "signature": [ - "SavedObjectDoc & { references?: ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectDoc", + "text": "SavedObjectDoc" + }, + " & { references?: ", { "pluginId": "@kbn/core-saved-objects-common", "scope": "common", @@ -997,6 +1004,46 @@ "trackAdoption": false } ] + }, + { + "parentPluginId": "encryptedSavedObjects", + "id": "def-server.EncryptedSavedObjectsPluginSetup.createModelVersion", + "type": "Function", + "tags": [], + "label": "createModelVersion", + "description": [], + "signature": [ + "(opts: ", + "CreateEsoModelVersionFnOpts", + ") => ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectsModelVersion", + "text": "SavedObjectsModelVersion" + } + ], + "path": "x-pack/plugins/encrypted_saved_objects/server/plugin.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "encryptedSavedObjects", + "id": "def-server.EncryptedSavedObjectsPluginSetup.createModelVersion.$1", + "type": "Object", + "tags": [], + "label": "opts", + "description": [], + "signature": [ + "CreateEsoModelVersionFnOpts" + ], + "path": "x-pack/plugins/encrypted_saved_objects/server/create_model_version.ts", + "deprecated": false, + "trackAdoption": false + } + ] } ], "lifecycle": "setup", diff --git a/api_docs/encrypted_saved_objects.mdx b/api_docs/encrypted_saved_objects.mdx index 267a10395a001..fb06097287248 100644 --- a/api_docs/encrypted_saved_objects.mdx +++ b/api_docs/encrypted_saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/encryptedSavedObjects title: "encryptedSavedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the encryptedSavedObjects plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'encryptedSavedObjects'] --- import encryptedSavedObjectsObj from './encrypted_saved_objects.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana- | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 51 | 0 | 44 | 0 | +| 53 | 0 | 46 | 1 | ## Server diff --git a/api_docs/enterprise_search.mdx b/api_docs/enterprise_search.mdx index aa6d18edb850e..cf844ada379ce 100644 --- a/api_docs/enterprise_search.mdx +++ b/api_docs/enterprise_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/enterpriseSearch title: "enterpriseSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the enterpriseSearch plugin -date: 2023-12-06 +date: 2023-12-19 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 c574ee0bd4b23..90845ad4b2ed2 100644 --- a/api_docs/es_ui_shared.mdx +++ b/api_docs/es_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/esUiShared title: "esUiShared" image: https://source.unsplash.com/400x175/?github description: API docs for the esUiShared plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'esUiShared'] --- import esUiSharedObj from './es_ui_shared.devdocs.json'; diff --git a/api_docs/event_annotation.devdocs.json b/api_docs/event_annotation.devdocs.json index c4f90f98b9a1a..1f5334176836c 100644 --- a/api_docs/event_annotation.devdocs.json +++ b/api_docs/event_annotation.devdocs.json @@ -804,14 +804,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/event_annotation/common/event_annotation_group/index.ts", diff --git a/api_docs/event_annotation.mdx b/api_docs/event_annotation.mdx index a1cdfae4cc8b8..ae4a01bdc817c 100644 --- a/api_docs/event_annotation.mdx +++ b/api_docs/event_annotation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventAnnotation title: "eventAnnotation" image: https://source.unsplash.com/400x175/?github description: API docs for the eventAnnotation plugin -date: 2023-12-06 +date: 2023-12-19 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 400cf4f60baa5..88c5fa1cdbe06 100644 --- a/api_docs/event_annotation_listing.mdx +++ b/api_docs/event_annotation_listing.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventAnnotationListing title: "eventAnnotationListing" image: https://source.unsplash.com/400x175/?github description: API docs for the eventAnnotationListing plugin -date: 2023-12-06 +date: 2023-12-19 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 cad5c55189d3a..e2539a556c286 100644 --- a/api_docs/event_log.mdx +++ b/api_docs/event_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/eventLog title: "eventLog" image: https://source.unsplash.com/400x175/?github description: API docs for the eventLog plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'eventLog'] --- import eventLogObj from './event_log.devdocs.json'; diff --git a/api_docs/exploratory_view.devdocs.json b/api_docs/exploratory_view.devdocs.json index 0e55c27726762..5e286318a0344 100644 --- a/api_docs/exploratory_view.devdocs.json +++ b/api_docs/exploratory_view.devdocs.json @@ -460,10 +460,10 @@ "pluginId": "exploratoryView", "scope": "public", "docId": "kibExploratoryViewPluginApi", - "section": "def-public.AllSeries", - "text": "AllSeries" + "section": "def-public.SeriesUrl", + "text": "SeriesUrl" }, - " | undefined" + "[]" ], "path": "x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/embeddable/embeddable.tsx", "deprecated": false, @@ -525,20 +525,6 @@ "deprecated": false, "trackAdoption": false }, - { - "parentPluginId": "exploratoryView", - "id": "def-public.ExploratoryEmbeddableProps.customLensAttrs", - "type": "Any", - "tags": [], - "label": "customLensAttrs", - "description": [], - "signature": [ - "any" - ], - "path": "x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/embeddable/embeddable.tsx", - "deprecated": false, - "trackAdoption": false - }, { "parentPluginId": "exploratoryView", "id": "def-public.ExploratoryEmbeddableProps.customTimeRange", @@ -1215,7 +1201,7 @@ "label": "observabilityShared", "description": [], "signature": [ - "{ navigation: { PageTemplate: (pageTemplateProps: ", + "{ locators: ObservabilitySharedLocators; navigation: { PageTemplate: (pageTemplateProps: ", "WrappedPageTemplateProps", ") => JSX.Element; registerSections: (sections$: ", "Observable", diff --git a/api_docs/exploratory_view.mdx b/api_docs/exploratory_view.mdx index 4ff160e9bac43..dce248fcf2062 100644 --- a/api_docs/exploratory_view.mdx +++ b/api_docs/exploratory_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/exploratoryView title: "exploratoryView" image: https://source.unsplash.com/400x175/?github description: API docs for the exploratoryView plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'exploratoryView'] --- import exploratoryViewObj from './exploratory_view.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/te | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 131 | 1 | 131 | 14 | +| 130 | 0 | 130 | 14 | ## Client diff --git a/api_docs/expression_error.mdx b/api_docs/expression_error.mdx index cfcc84223e7d2..e33e91622e2e9 100644 --- a/api_docs/expression_error.mdx +++ b/api_docs/expression_error.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionError title: "expressionError" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionError plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionError'] --- import expressionErrorObj from './expression_error.devdocs.json'; diff --git a/api_docs/expression_gauge.devdocs.json b/api_docs/expression_gauge.devdocs.json index 4ffb246b88599..b38c5c31347ff 100644 --- a/api_docs/expression_gauge.devdocs.json +++ b/api_docs/expression_gauge.devdocs.json @@ -1132,14 +1132,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/chart_expressions/expression_gauge/common/types/expression_functions.ts", @@ -1265,7 +1257,7 @@ "CustomXDomain", " | undefined>; ariaDescription?: string | undefined; ariaDescribedBy?: string | undefined; ariaLabelledBy?: string | undefined; ariaTableCaption?: string | undefined; legendAction?: \"ignore\" | undefined; legendStrategy?: ", "LegendStrategy", - " | undefined; onLegendItemClick?: \"ignore\" | undefined; customLegend?: \"ignore\" | undefined; onLegendItemMinusClick?: \"ignore\" | undefined; onLegendItemOut?: \"ignore\" | undefined; onLegendItemOver?: \"ignore\" | undefined; onLegendItemPlusClick?: \"ignore\" | undefined; debugState?: boolean | undefined; onProjectionClick?: \"ignore\" | undefined; onElementClick?: \"ignore\" | undefined; onElementOver?: \"ignore\" | undefined; onElementOut?: \"ignore\" | undefined; onBrushEnd?: \"ignore\" | undefined; onProjectionAreaChange?: \"ignore\" | undefined; onAnnotationClick?: \"ignore\" | undefined; pointerUpdateDebounce?: number | undefined; roundHistogramBrushValues?: boolean | undefined; noResults?: React.ReactChild | React.ComponentType<{}> | undefined; legendSort?: \"ignore\" | undefined; }>> & Partial>) | undefined; }" + " | undefined; onLegendItemClick?: \"ignore\" | undefined; customLegend?: \"ignore\" | undefined; onLegendItemMinusClick?: \"ignore\" | undefined; onLegendItemOut?: \"ignore\" | undefined; onLegendItemOver?: \"ignore\" | undefined; onLegendItemPlusClick?: \"ignore\" | undefined; debugState?: boolean | undefined; onProjectionClick?: \"ignore\" | undefined; onElementClick?: \"ignore\" | undefined; onElementOver?: \"ignore\" | undefined; onElementOut?: \"ignore\" | undefined; onBrushEnd?: \"ignore\" | undefined; onResize?: \"ignore\" | undefined; onWillRender?: \"ignore\" | undefined; onProjectionAreaChange?: \"ignore\" | undefined; onAnnotationClick?: \"ignore\" | undefined; pointerUpdateDebounce?: number | undefined; roundHistogramBrushValues?: boolean | undefined; noResults?: React.ReactChild | React.ComponentType<{}> | undefined; legendSort?: \"ignore\" | undefined; }>> & Partial>) | undefined; }" ], "path": "src/plugins/chart_expressions/expression_gauge/common/types/expression_renderers.ts", "deprecated": false, diff --git a/api_docs/expression_gauge.mdx b/api_docs/expression_gauge.mdx index 5645468112e68..09e404d5f40a3 100644 --- a/api_docs/expression_gauge.mdx +++ b/api_docs/expression_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionGauge title: "expressionGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionGauge plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionGauge'] --- import expressionGaugeObj from './expression_gauge.devdocs.json'; diff --git a/api_docs/expression_heatmap.devdocs.json b/api_docs/expression_heatmap.devdocs.json index d131c0eec50a3..90835598df7ca 100644 --- a/api_docs/expression_heatmap.devdocs.json +++ b/api_docs/expression_heatmap.devdocs.json @@ -527,7 +527,7 @@ "CustomXDomain", " | undefined>; ariaDescription?: string | undefined; ariaDescribedBy?: string | undefined; ariaLabelledBy?: string | undefined; ariaTableCaption?: string | undefined; legendAction?: \"ignore\" | undefined; legendStrategy?: ", "LegendStrategy", - " | undefined; onLegendItemClick?: \"ignore\" | undefined; customLegend?: \"ignore\" | undefined; onLegendItemMinusClick?: \"ignore\" | undefined; onLegendItemOut?: \"ignore\" | undefined; onLegendItemOver?: \"ignore\" | undefined; onLegendItemPlusClick?: \"ignore\" | undefined; debugState?: boolean | undefined; onProjectionClick?: \"ignore\" | undefined; onElementClick?: \"ignore\" | undefined; onElementOver?: \"ignore\" | undefined; onElementOut?: \"ignore\" | undefined; onBrushEnd?: \"ignore\" | undefined; onProjectionAreaChange?: \"ignore\" | undefined; onAnnotationClick?: \"ignore\" | undefined; pointerUpdateDebounce?: number | undefined; roundHistogramBrushValues?: boolean | undefined; noResults?: React.ReactChild | React.ComponentType<{}> | undefined; legendSort?: \"ignore\" | undefined; }>> & Partial>) | undefined" + " | undefined; onLegendItemClick?: \"ignore\" | undefined; customLegend?: \"ignore\" | undefined; onLegendItemMinusClick?: \"ignore\" | undefined; onLegendItemOut?: \"ignore\" | undefined; onLegendItemOver?: \"ignore\" | undefined; onLegendItemPlusClick?: \"ignore\" | undefined; debugState?: boolean | undefined; onProjectionClick?: \"ignore\" | undefined; onElementClick?: \"ignore\" | undefined; onElementOver?: \"ignore\" | undefined; onElementOut?: \"ignore\" | undefined; onBrushEnd?: \"ignore\" | undefined; onResize?: \"ignore\" | undefined; onWillRender?: \"ignore\" | undefined; onProjectionAreaChange?: \"ignore\" | undefined; onAnnotationClick?: \"ignore\" | undefined; pointerUpdateDebounce?: number | undefined; roundHistogramBrushValues?: boolean | undefined; noResults?: React.ReactChild | React.ComponentType<{}> | undefined; legendSort?: \"ignore\" | undefined; }>> & Partial>) | undefined" ], "path": "src/plugins/chart_expressions/expression_heatmap/common/types/expression_functions.ts", "deprecated": false, @@ -687,14 +687,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/chart_expressions/expression_heatmap/common/types/expression_functions.ts", @@ -759,14 +751,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/chart_expressions/expression_heatmap/common/types/expression_functions.ts", @@ -831,14 +815,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/chart_expressions/expression_heatmap/common/types/expression_functions.ts", diff --git a/api_docs/expression_heatmap.mdx b/api_docs/expression_heatmap.mdx index 9a356e1c44ae8..3ad875c6bfc50 100644 --- a/api_docs/expression_heatmap.mdx +++ b/api_docs/expression_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionHeatmap title: "expressionHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionHeatmap plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionHeatmap'] --- import expressionHeatmapObj from './expression_heatmap.devdocs.json'; diff --git a/api_docs/expression_image.devdocs.json b/api_docs/expression_image.devdocs.json index 9d01073c26d1f..e51a1a08b9d44 100644 --- a/api_docs/expression_image.devdocs.json +++ b/api_docs/expression_image.devdocs.json @@ -406,14 +406,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expression_image/common/types/expression_functions.ts", diff --git a/api_docs/expression_image.mdx b/api_docs/expression_image.mdx index cb2404b25d172..4ef4283f8461c 100644 --- a/api_docs/expression_image.mdx +++ b/api_docs/expression_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionImage title: "expressionImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionImage plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionImage'] --- import expressionImageObj from './expression_image.devdocs.json'; diff --git a/api_docs/expression_legacy_metric_vis.devdocs.json b/api_docs/expression_legacy_metric_vis.devdocs.json index e75f85673b794..59c68ce444a54 100644 --- a/api_docs/expression_legacy_metric_vis.devdocs.json +++ b/api_docs/expression_legacy_metric_vis.devdocs.json @@ -872,14 +872,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/chart_expressions/expression_legacy_metric/common/types/expression_functions.ts", diff --git a/api_docs/expression_legacy_metric_vis.mdx b/api_docs/expression_legacy_metric_vis.mdx index dfabf1c7600be..196afb1a723dd 100644 --- a/api_docs/expression_legacy_metric_vis.mdx +++ b/api_docs/expression_legacy_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionLegacyMetricVis title: "expressionLegacyMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionLegacyMetricVis plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionLegacyMetricVis'] --- import expressionLegacyMetricVisObj from './expression_legacy_metric_vis.devdocs.json'; diff --git a/api_docs/expression_metric.devdocs.json b/api_docs/expression_metric.devdocs.json index 5c1c8610709fa..674dd738fe29d 100644 --- a/api_docs/expression_metric.devdocs.json +++ b/api_docs/expression_metric.devdocs.json @@ -529,14 +529,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expression_metric/common/types/expression_functions.ts", diff --git a/api_docs/expression_metric.mdx b/api_docs/expression_metric.mdx index 48bae08fb0dac..0d650df9ebfb6 100644 --- a/api_docs/expression_metric.mdx +++ b/api_docs/expression_metric.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetric title: "expressionMetric" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetric plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetric'] --- import expressionMetricObj from './expression_metric.devdocs.json'; diff --git a/api_docs/expression_metric_vis.devdocs.json b/api_docs/expression_metric_vis.devdocs.json index 02dbf6469ed48..37d520de6b448 100644 --- a/api_docs/expression_metric_vis.devdocs.json +++ b/api_docs/expression_metric_vis.devdocs.json @@ -904,7 +904,7 @@ "CustomXDomain", " | undefined>; ariaDescription?: string | undefined; ariaDescribedBy?: string | undefined; ariaLabelledBy?: string | undefined; ariaTableCaption?: string | undefined; legendAction?: \"ignore\" | undefined; legendStrategy?: ", "LegendStrategy", - " | undefined; onLegendItemClick?: \"ignore\" | undefined; customLegend?: \"ignore\" | undefined; onLegendItemMinusClick?: \"ignore\" | undefined; onLegendItemOut?: \"ignore\" | undefined; onLegendItemOver?: \"ignore\" | undefined; onLegendItemPlusClick?: \"ignore\" | undefined; debugState?: boolean | undefined; onProjectionClick?: \"ignore\" | undefined; onElementClick?: \"ignore\" | undefined; onElementOver?: \"ignore\" | undefined; onElementOut?: \"ignore\" | undefined; onBrushEnd?: \"ignore\" | undefined; onProjectionAreaChange?: \"ignore\" | undefined; onAnnotationClick?: \"ignore\" | undefined; pointerUpdateDebounce?: number | undefined; roundHistogramBrushValues?: boolean | undefined; noResults?: React.ReactChild | React.ComponentType<{}> | undefined; legendSort?: \"ignore\" | undefined; }>> & Partial>) | undefined" + " | undefined; onLegendItemClick?: \"ignore\" | undefined; customLegend?: \"ignore\" | undefined; onLegendItemMinusClick?: \"ignore\" | undefined; onLegendItemOut?: \"ignore\" | undefined; onLegendItemOver?: \"ignore\" | undefined; onLegendItemPlusClick?: \"ignore\" | undefined; debugState?: boolean | undefined; onProjectionClick?: \"ignore\" | undefined; onElementClick?: \"ignore\" | undefined; onElementOver?: \"ignore\" | undefined; onElementOut?: \"ignore\" | undefined; onBrushEnd?: \"ignore\" | undefined; onResize?: \"ignore\" | undefined; onWillRender?: \"ignore\" | undefined; onProjectionAreaChange?: \"ignore\" | undefined; onAnnotationClick?: \"ignore\" | undefined; pointerUpdateDebounce?: number | undefined; roundHistogramBrushValues?: boolean | undefined; noResults?: React.ReactChild | React.ComponentType<{}> | undefined; legendSort?: \"ignore\" | undefined; }>> & Partial>) | undefined" ], "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts", "deprecated": false, @@ -1135,14 +1135,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts", @@ -1223,14 +1215,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/chart_expressions/expression_metric/common/types/expression_functions.ts", diff --git a/api_docs/expression_metric_vis.mdx b/api_docs/expression_metric_vis.mdx index 86443fa86b713..709fcff3d9d6f 100644 --- a/api_docs/expression_metric_vis.mdx +++ b/api_docs/expression_metric_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionMetricVis title: "expressionMetricVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionMetricVis plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionMetricVis'] --- import expressionMetricVisObj from './expression_metric_vis.devdocs.json'; diff --git a/api_docs/expression_partition_vis.devdocs.json b/api_docs/expression_partition_vis.devdocs.json index 908097245f11f..7079babb4610f 100644 --- a/api_docs/expression_partition_vis.devdocs.json +++ b/api_docs/expression_partition_vis.devdocs.json @@ -140,14 +140,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/chart_expressions/expression_partition_vis/common/expression_functions/partition_labels_function.ts", @@ -1196,14 +1188,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/chart_expressions/expression_partition_vis/common/types/expression_functions.ts", @@ -1290,14 +1274,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/chart_expressions/expression_partition_vis/common/types/expression_functions.ts", @@ -1377,14 +1353,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/chart_expressions/expression_partition_vis/common/types/expression_functions.ts", @@ -1494,14 +1462,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/chart_expressions/expression_partition_vis/common/types/expression_functions.ts", @@ -1581,14 +1541,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/chart_expressions/expression_partition_vis/common/types/expression_functions.ts", diff --git a/api_docs/expression_partition_vis.mdx b/api_docs/expression_partition_vis.mdx index 77c66a111e8c9..20adbc2af5fa0 100644 --- a/api_docs/expression_partition_vis.mdx +++ b/api_docs/expression_partition_vis.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionPartitionVis title: "expressionPartitionVis" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionPartitionVis plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionPartitionVis'] --- import expressionPartitionVisObj from './expression_partition_vis.devdocs.json'; diff --git a/api_docs/expression_repeat_image.devdocs.json b/api_docs/expression_repeat_image.devdocs.json index dcd0d54c1296c..4cab6fe3a0829 100644 --- a/api_docs/expression_repeat_image.devdocs.json +++ b/api_docs/expression_repeat_image.devdocs.json @@ -453,14 +453,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expression_repeat_image/common/types/expression_functions.ts", diff --git a/api_docs/expression_repeat_image.mdx b/api_docs/expression_repeat_image.mdx index 985a84c89e9aa..fcf581dc2c59f 100644 --- a/api_docs/expression_repeat_image.mdx +++ b/api_docs/expression_repeat_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRepeatImage title: "expressionRepeatImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRepeatImage plugin -date: 2023-12-06 +date: 2023-12-19 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 f17f44938cd61..26676f8e0aa79 100644 --- a/api_docs/expression_reveal_image.mdx +++ b/api_docs/expression_reveal_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionRevealImage title: "expressionRevealImage" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionRevealImage plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionRevealImage'] --- import expressionRevealImageObj from './expression_reveal_image.devdocs.json'; diff --git a/api_docs/expression_shape.devdocs.json b/api_docs/expression_shape.devdocs.json index 3c6d97d7ce0f4..63d5a0c2ce2f2 100644 --- a/api_docs/expression_shape.devdocs.json +++ b/api_docs/expression_shape.devdocs.json @@ -1517,14 +1517,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expression_shape/common/types/expression_functions.ts", @@ -1574,14 +1566,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expression_shape/common/types/expression_functions.ts", @@ -2521,14 +2505,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expression_shape/common/types/expression_functions.ts", @@ -2578,14 +2554,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expression_shape/common/types/expression_functions.ts", diff --git a/api_docs/expression_shape.mdx b/api_docs/expression_shape.mdx index 89006a1fc5e71..d6869a4d3e337 100644 --- a/api_docs/expression_shape.mdx +++ b/api_docs/expression_shape.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionShape title: "expressionShape" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionShape plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionShape'] --- import expressionShapeObj from './expression_shape.devdocs.json'; diff --git a/api_docs/expression_tagcloud.devdocs.json b/api_docs/expression_tagcloud.devdocs.json index 0c7cd3c404232..8c6b4a77e81c1 100644 --- a/api_docs/expression_tagcloud.devdocs.json +++ b/api_docs/expression_tagcloud.devdocs.json @@ -104,14 +104,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/chart_expressions/expression_tagcloud/common/types/expression_functions.ts", diff --git a/api_docs/expression_tagcloud.mdx b/api_docs/expression_tagcloud.mdx index d152fd3d33e51..717f1e531aeda 100644 --- a/api_docs/expression_tagcloud.mdx +++ b/api_docs/expression_tagcloud.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionTagcloud title: "expressionTagcloud" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionTagcloud plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionTagcloud'] --- import expressionTagcloudObj from './expression_tagcloud.devdocs.json'; diff --git a/api_docs/expression_x_y.devdocs.json b/api_docs/expression_x_y.devdocs.json index ef1d580422e20..a1289bd45ad5c 100644 --- a/api_docs/expression_x_y.devdocs.json +++ b/api_docs/expression_x_y.devdocs.json @@ -1978,7 +1978,7 @@ "CustomXDomain", " | undefined>; ariaDescription?: string | undefined; ariaDescribedBy?: string | undefined; ariaLabelledBy?: string | undefined; ariaTableCaption?: string | undefined; legendAction?: \"ignore\" | undefined; legendStrategy?: ", "LegendStrategy", - " | undefined; onLegendItemClick?: \"ignore\" | undefined; customLegend?: \"ignore\" | undefined; onLegendItemMinusClick?: \"ignore\" | undefined; onLegendItemOut?: \"ignore\" | undefined; onLegendItemOver?: \"ignore\" | undefined; onLegendItemPlusClick?: \"ignore\" | undefined; debugState?: boolean | undefined; onProjectionClick?: \"ignore\" | undefined; onElementClick?: \"ignore\" | undefined; onElementOver?: \"ignore\" | undefined; onElementOut?: \"ignore\" | undefined; onBrushEnd?: \"ignore\" | undefined; onProjectionAreaChange?: \"ignore\" | undefined; onAnnotationClick?: \"ignore\" | undefined; pointerUpdateDebounce?: number | undefined; roundHistogramBrushValues?: boolean | undefined; noResults?: React.ReactChild | React.ComponentType<{}> | undefined; legendSort?: \"ignore\" | undefined; }>> & Partial>) | undefined" + " | undefined; onLegendItemClick?: \"ignore\" | undefined; customLegend?: \"ignore\" | undefined; onLegendItemMinusClick?: \"ignore\" | undefined; onLegendItemOut?: \"ignore\" | undefined; onLegendItemOver?: \"ignore\" | undefined; onLegendItemPlusClick?: \"ignore\" | undefined; debugState?: boolean | undefined; onProjectionClick?: \"ignore\" | undefined; onElementClick?: \"ignore\" | undefined; onElementOver?: \"ignore\" | undefined; onElementOut?: \"ignore\" | undefined; onBrushEnd?: \"ignore\" | undefined; onResize?: \"ignore\" | undefined; onWillRender?: \"ignore\" | undefined; onProjectionAreaChange?: \"ignore\" | undefined; onAnnotationClick?: \"ignore\" | undefined; pointerUpdateDebounce?: number | undefined; roundHistogramBrushValues?: boolean | undefined; noResults?: React.ReactChild | React.ComponentType<{}> | undefined; legendSort?: \"ignore\" | undefined; }>> & Partial>) | undefined" ], "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts", "deprecated": false, @@ -2522,14 +2522,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts", @@ -2688,14 +2680,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts", @@ -2766,14 +2750,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts", @@ -2863,14 +2839,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts", @@ -2972,14 +2940,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts", @@ -3049,14 +3009,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts", @@ -3178,14 +3130,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts", @@ -3331,14 +3275,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts", @@ -3423,14 +3359,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts", @@ -3599,14 +3527,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_functions.ts", diff --git a/api_docs/expression_x_y.mdx b/api_docs/expression_x_y.mdx index f08db602f77a7..b3da4ddabb6f6 100644 --- a/api_docs/expression_x_y.mdx +++ b/api_docs/expression_x_y.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressionXY title: "expressionXY" image: https://source.unsplash.com/400x175/?github description: API docs for the expressionXY plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'expressionXY'] --- import expressionXYObj from './expression_x_y.devdocs.json'; diff --git a/api_docs/expressions.devdocs.json b/api_docs/expressions.devdocs.json index 147e417c0ce6d..d3440d35400cd 100644 --- a/api_docs/expressions.devdocs.json +++ b/api_docs/expressions.devdocs.json @@ -127,15 +127,7 @@ "section": "def-common.ExecutionContext", "text": "ExecutionContext" }, - "" + "" ], "path": "src/plugins/expressions/common/execution/execution.ts", "deprecated": false, @@ -7068,7 +7060,7 @@ "section": "def-common.ExecutionContext", "text": "ExecutionContext" }, - "" + "" ], "path": "src/plugins/expressions/common/execution/types.ts", "deprecated": false, @@ -7084,7 +7076,14 @@ "\nGet search context of the expression." ], "signature": [ - "() => ExecutionContextSearch" + "() => ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.ExecutionContextSearch", + "text": "ExecutionContextSearch" + } ], "path": "src/plugins/expressions/common/execution/types.ts", "deprecated": false, @@ -8608,14 +8607,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/types.ts", @@ -8669,14 +8660,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/types.ts", @@ -8714,14 +8697,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/types.ts", @@ -8759,14 +8734,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/types.ts", @@ -8804,14 +8771,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/types.ts", @@ -8873,14 +8832,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/types.ts", @@ -8942,14 +8893,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/types.ts", @@ -9011,14 +8954,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/types.ts", @@ -9080,14 +9015,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/types.ts", @@ -10655,11 +10582,11 @@ "description": [], "signature": [ { - "pluginId": "@kbn/utility-types", + "pluginId": "@kbn/es-query", "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.ExecutionContextSearch", + "text": "ExecutionContextSearch" }, " | undefined" ], @@ -11861,14 +11788,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/types.ts", @@ -13647,15 +13566,7 @@ "section": "def-common.ExecutionContext", "text": "ExecutionContext" }, - "" + "" ], "path": "src/plugins/expressions/common/execution/execution.ts", "deprecated": false, @@ -18475,7 +18386,7 @@ "section": "def-common.ExecutionContext", "text": "ExecutionContext" }, - "" + "" ], "path": "src/plugins/expressions/common/execution/types.ts", "deprecated": false, @@ -18491,7 +18402,14 @@ "\nGet search context of the expression." ], "signature": [ - "() => ExecutionContextSearch" + "() => ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.ExecutionContextSearch", + "text": "ExecutionContextSearch" + } ], "path": "src/plugins/expressions/common/execution/types.ts", "deprecated": false, @@ -19984,14 +19902,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/types.ts", @@ -20045,14 +19955,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/types.ts", @@ -20090,14 +19992,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/types.ts", @@ -20135,14 +20029,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/types.ts", @@ -20180,14 +20066,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/types.ts", @@ -20249,14 +20127,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/types.ts", @@ -20318,14 +20188,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/types.ts", @@ -20387,14 +20249,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/types.ts", @@ -20456,14 +20310,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/types.ts", @@ -21769,14 +21615,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/types.ts", @@ -22814,15 +22652,7 @@ "section": "def-common.ExecutionContext", "text": "ExecutionContext" }, - "" + "" ], "path": "src/plugins/expressions/common/execution/execution.ts", "deprecated": false, @@ -28967,14 +28797,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">" ], "path": "src/plugins/expressions/common/util/test_utils.ts", @@ -30542,7 +30364,7 @@ "section": "def-common.ExecutionContext", "text": "ExecutionContext" }, - "" + "" ], "path": "src/plugins/expressions/common/execution/types.ts", "deprecated": false, @@ -30558,7 +30380,14 @@ "\nGet search context of the expression." ], "signature": [ - "() => ExecutionContextSearch" + "() => ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.ExecutionContextSearch", + "text": "ExecutionContextSearch" + } ], "path": "src/plugins/expressions/common/execution/types.ts", "deprecated": false, @@ -32269,11 +32098,11 @@ "description": [], "signature": [ { - "pluginId": "@kbn/utility-types", + "pluginId": "@kbn/es-query", "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.ExecutionContextSearch", + "text": "ExecutionContextSearch" }, " | undefined" ], @@ -32872,14 +32701,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/types.ts", @@ -32933,14 +32754,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/types.ts", @@ -32978,14 +32791,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/types.ts", @@ -33023,14 +32828,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/types.ts", @@ -33068,14 +32865,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/types.ts", @@ -33137,14 +32926,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/types.ts", @@ -33206,14 +32987,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/types.ts", @@ -33275,14 +33048,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/types.ts", @@ -33344,14 +33109,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/types.ts", @@ -36454,14 +36211,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/types.ts", @@ -36853,14 +36602,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/specs/clog.ts", @@ -36923,14 +36664,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/specs/cumulative_sum.ts", @@ -36993,14 +36726,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/specs/derivative.ts", @@ -37055,14 +36780,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/specs/font.ts", @@ -37125,14 +36842,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/specs/moving_average.ts", @@ -37195,14 +36904,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/specs/overall_metric.ts", @@ -37241,14 +36942,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/specs/theme.ts", @@ -37303,14 +36996,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/specs/ui_setting.ts", @@ -37349,14 +37034,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/specs/var.ts", @@ -37395,14 +37072,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/expressions/common/expression_functions/specs/var_set.ts", @@ -42551,14 +42220,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">) => Promise<", { "pluginId": "expressions", @@ -42638,14 +42299,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">" ], "path": "src/plugins/expressions/common/expression_functions/specs/math_column.ts", @@ -45399,14 +45052,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">) => any" ], "path": "src/plugins/expressions/common/expression_functions/specs/theme.ts", @@ -45466,14 +45111,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">" ], "path": "src/plugins/expressions/common/expression_functions/specs/theme.ts", @@ -45912,14 +45549,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">) => unknown" ], "path": "src/plugins/expressions/common/expression_functions/specs/var.ts", @@ -45979,14 +45608,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">" ], "path": "src/plugins/expressions/common/expression_functions/specs/var.ts", @@ -46205,14 +45826,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">) => unknown" ], "path": "src/plugins/expressions/common/expression_functions/specs/var_set.ts", @@ -46272,14 +45885,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">" ], "path": "src/plugins/expressions/common/expression_functions/specs/var_set.ts", diff --git a/api_docs/expressions.mdx b/api_docs/expressions.mdx index f005e710d9047..8430995eb24c2 100644 --- a/api_docs/expressions.mdx +++ b/api_docs/expressions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/expressions title: "expressions" image: https://source.unsplash.com/400x175/?github description: API docs for the expressions plugin -date: 2023-12-06 +date: 2023-12-19 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 2cf8a8dcc9e62..7ae1f4334dcfc 100644 --- a/api_docs/features.mdx +++ b/api_docs/features.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/features title: "features" image: https://source.unsplash.com/400x175/?github description: API docs for the features plugin -date: 2023-12-06 +date: 2023-12-19 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 aa2c080a0101d..fc723632424c7 100644 --- a/api_docs/field_formats.mdx +++ b/api_docs/field_formats.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fieldFormats title: "fieldFormats" image: https://source.unsplash.com/400x175/?github description: API docs for the fieldFormats plugin -date: 2023-12-06 +date: 2023-12-19 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 f07e0ba16937c..be3c119ee0054 100644 --- a/api_docs/file_upload.mdx +++ b/api_docs/file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fileUpload title: "fileUpload" image: https://source.unsplash.com/400x175/?github description: API docs for the fileUpload plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'fileUpload'] --- import fileUploadObj from './file_upload.devdocs.json'; diff --git a/api_docs/files.devdocs.json b/api_docs/files.devdocs.json index 12fa5d46818a4..691ce183b4985 100644 --- a/api_docs/files.devdocs.json +++ b/api_docs/files.devdocs.json @@ -524,10 +524,10 @@ "objects": [], "setup": { "parentPluginId": "files", - "id": "def-public.FilesSetup", + "id": "def-public.FilesPublicSetup", "type": "Interface", "tags": [], - "label": "FilesSetup", + "label": "FilesPublicSetup", "description": [ "\nPublic setup-phase contract" ], @@ -537,7 +537,7 @@ "children": [ { "parentPluginId": "files", - "id": "def-public.FilesSetup.filesClientFactory", + "id": "def-public.FilesPublicSetup.filesClientFactory", "type": "Object", "tags": [ "track-adoption" @@ -597,7 +597,7 @@ }, { "parentPluginId": "files", - "id": "def-public.FilesSetup.registerFileKind", + "id": "def-public.FilesPublicSetup.registerFileKind", "type": "Function", "tags": [], "label": "registerFileKind", @@ -621,7 +621,7 @@ "children": [ { "parentPluginId": "files", - "id": "def-public.FilesSetup.registerFileKind.$1", + "id": "def-public.FilesPublicSetup.registerFileKind.$1", "type": "Object", "tags": [], "label": "fileKind", @@ -651,10 +651,10 @@ }, "start": { "parentPluginId": "files", - "id": "def-public.FilesStart", + "id": "def-public.FilesPublicStart", "type": "Type", "tags": [], - "label": "FilesStart", + "label": "FilesPublicStart", "description": [], "signature": [ "Pick<", @@ -662,8 +662,8 @@ "pluginId": "files", "scope": "public", "docId": "kibFilesPluginApi", - "section": "def-public.FilesSetup", - "text": "FilesSetup" + "section": "def-public.FilesPublicSetup", + "text": "FilesPublicSetup" }, ", \"filesClientFactory\"> & { getFileKindDefinition: (id: string) => ", { @@ -5317,10 +5317,10 @@ "objects": [], "setup": { "parentPluginId": "files", - "id": "def-server.FilesSetup", + "id": "def-server.FilesServerSetup", "type": "Interface", "tags": [], - "label": "FilesSetup", + "label": "FilesServerSetup", "description": [ "\nFiles plugin setup contract" ], @@ -5330,7 +5330,7 @@ "children": [ { "parentPluginId": "files", - "id": "def-server.FilesSetup.registerFileKind", + "id": "def-server.FilesServerSetup.registerFileKind", "type": "Function", "tags": [ "track-adoption" @@ -5398,7 +5398,7 @@ "children": [ { "parentPluginId": "files", - "id": "def-server.FilesSetup.registerFileKind.$1", + "id": "def-server.FilesServerSetup.registerFileKind.$1", "type": "Object", "tags": [], "label": "fileKind", @@ -5428,10 +5428,10 @@ }, "start": { "parentPluginId": "files", - "id": "def-server.FilesStart", + "id": "def-server.FilesServerStart", "type": "Interface", "tags": [], - "label": "FilesStart", + "label": "FilesServerStart", "description": [ "\nFiles plugin start contract" ], @@ -5441,7 +5441,7 @@ "children": [ { "parentPluginId": "files", - "id": "def-server.FilesStart.fileServiceFactory", + "id": "def-server.FilesServerStart.fileServiceFactory", "type": "Object", "tags": [ "track-adoption" diff --git a/api_docs/files.mdx b/api_docs/files.mdx index 477297b1e424a..4e18bd193ef2f 100644 --- a/api_docs/files.mdx +++ b/api_docs/files.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/files title: "files" image: https://source.unsplash.com/400x175/?github description: API docs for the files plugin -date: 2023-12-06 +date: 2023-12-19 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 fd3593edfabdd..2f787e46e8292 100644 --- a/api_docs/files_management.mdx +++ b/api_docs/files_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/filesManagement title: "filesManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the filesManagement plugin -date: 2023-12-06 +date: 2023-12-19 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 a57d03da5d59f..fc7721c76091e 100644 --- a/api_docs/fleet.devdocs.json +++ b/api_docs/fleet.devdocs.json @@ -2371,6 +2371,18 @@ "deprecated": false, "trackAdoption": false, "initialIsOpen": false + }, + { + "parentPluginId": "fleet", + "id": "def-public.SetupTechnology", + "type": "Enum", + "tags": [], + "label": "SetupTechnology", + "description": [], + "path": "x-pack/plugins/fleet/common/types/models/setup_technology.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false } ], "misc": [ @@ -4515,7 +4527,7 @@ "text": "AgentNotFoundError" }, " extends ", - "FleetError" + "FleetNotFoundError" ], "path": "x-pack/plugins/fleet/server/errors/index.ts", "deprecated": false, @@ -6705,7 +6717,7 @@ "label": "ensureInstalledPackage", "description": [], "signature": [ - "(options: { pkgName: string; pkgVersion?: string | undefined; spaceId?: string | undefined; }) => Promise<", + "(options: { pkgName: string; pkgVersion?: string | undefined; spaceId?: string | undefined; force?: boolean | undefined; }) => Promise<", { "pluginId": "fleet", "scope": "common", @@ -6768,6 +6780,111 @@ "path": "x-pack/plugins/fleet/server/services/epm/package_service.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "fleet", + "id": "def-server.PackageClient.ensureInstalledPackage.$1.force", + "type": "CompoundType", + "tags": [], + "label": "force", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/plugins/fleet/server/services/epm/package_service.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "returnComment": [] + }, + { + "parentPluginId": "fleet", + "id": "def-server.PackageClient.installPackage", + "type": "Function", + "tags": [], + "label": "installPackage", + "description": [], + "signature": [ + "(options: { pkgName: string; pkgVersion?: string | undefined; spaceId?: string | undefined; force?: boolean | undefined; }) => Promise<", + { + "pluginId": "fleet", + "scope": "common", + "docId": "kibFleetPluginApi", + "section": "def-common.InstallResult", + "text": "InstallResult" + }, + " | undefined>" + ], + "path": "x-pack/plugins/fleet/server/services/epm/package_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "fleet", + "id": "def-server.PackageClient.installPackage.$1", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "path": "x-pack/plugins/fleet/server/services/epm/package_service.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "fleet", + "id": "def-server.PackageClient.installPackage.$1.pkgName", + "type": "string", + "tags": [], + "label": "pkgName", + "description": [], + "path": "x-pack/plugins/fleet/server/services/epm/package_service.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "fleet", + "id": "def-server.PackageClient.installPackage.$1.pkgVersion", + "type": "string", + "tags": [], + "label": "pkgVersion", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "x-pack/plugins/fleet/server/services/epm/package_service.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "fleet", + "id": "def-server.PackageClient.installPackage.$1.spaceId", + "type": "string", + "tags": [], + "label": "spaceId", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "x-pack/plugins/fleet/server/services/epm/package_service.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "fleet", + "id": "def-server.PackageClient.installPackage.$1.force", + "type": "CompoundType", + "tags": [], + "label": "force", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "x-pack/plugins/fleet/server/services/epm/package_service.ts", + "deprecated": false, + "trackAdoption": false } ] } @@ -18175,17 +18292,19 @@ }, { "parentPluginId": "fleet", - "id": "def-common.BundledPackage.buffer", - "type": "Object", + "id": "def-common.BundledPackage.getBuffer", + "type": "Function", "tags": [], - "label": "buffer", + "label": "getBuffer", "description": [], "signature": [ - "Buffer" + "() => Promise" ], "path": "x-pack/plugins/fleet/common/types/models/epm.ts", "deprecated": false, - "trackAdoption": false + "trackAdoption": false, + "children": [], + "returnComment": [] } ], "initialIsOpen": false diff --git a/api_docs/fleet.mdx b/api_docs/fleet.mdx index 655dc0fc6fb41..a2529488fea50 100644 --- a/api_docs/fleet.mdx +++ b/api_docs/fleet.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/fleet title: "fleet" image: https://source.unsplash.com/400x175/?github description: API docs for the fleet plugin -date: 2023-12-06 +date: 2023-12-19 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 | |-------------------|-----------|------------------------|-----------------| -| 1213 | 3 | 1095 | 47 | +| 1221 | 3 | 1103 | 48 | ## Client diff --git a/api_docs/global_search.mdx b/api_docs/global_search.mdx index 6c78f3b021631..cfe38afe6f1e7 100644 --- a/api_docs/global_search.mdx +++ b/api_docs/global_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/globalSearch title: "globalSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the globalSearch plugin -date: 2023-12-06 +date: 2023-12-19 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 132f0a719b81b..dd3d5c34517c0 100644 --- a/api_docs/guided_onboarding.mdx +++ b/api_docs/guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/guidedOnboarding title: "guidedOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the guidedOnboarding plugin -date: 2023-12-06 +date: 2023-12-19 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 f7a7cb9902051..371246b624db7 100644 --- a/api_docs/home.mdx +++ b/api_docs/home.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/home title: "home" image: https://source.unsplash.com/400x175/?github description: API docs for the home plugin -date: 2023-12-06 +date: 2023-12-19 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 89690b050449e..8c2cfc0e6478f 100644 --- a/api_docs/image_embeddable.mdx +++ b/api_docs/image_embeddable.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/imageEmbeddable title: "imageEmbeddable" image: https://source.unsplash.com/400x175/?github description: API docs for the imageEmbeddable plugin -date: 2023-12-06 +date: 2023-12-19 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 1d44292f093c8..df8a5fef4b6ff 100644 --- a/api_docs/index_lifecycle_management.mdx +++ b/api_docs/index_lifecycle_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexLifecycleManagement title: "indexLifecycleManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexLifecycleManagement plugin -date: 2023-12-06 +date: 2023-12-19 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 50160805079ef..8ce83386a5629 100644 --- a/api_docs/index_management.mdx +++ b/api_docs/index_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/indexManagement title: "indexManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the indexManagement plugin -date: 2023-12-06 +date: 2023-12-19 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 e5518f72e6b60..81ff97012c029 100644 --- a/api_docs/infra.mdx +++ b/api_docs/infra.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/infra title: "infra" image: https://source.unsplash.com/400x175/?github description: API docs for the infra plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'infra'] --- import infraObj from './infra.devdocs.json'; diff --git a/api_docs/inspector.mdx b/api_docs/inspector.mdx index 0ee4cd9ca8007..e2c5b4b9d23ca 100644 --- a/api_docs/inspector.mdx +++ b/api_docs/inspector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/inspector title: "inspector" image: https://source.unsplash.com/400x175/?github description: API docs for the inspector plugin -date: 2023-12-06 +date: 2023-12-19 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 282c9446b8891..252e160b616d4 100644 --- a/api_docs/interactive_setup.mdx +++ b/api_docs/interactive_setup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/interactiveSetup title: "interactiveSetup" image: https://source.unsplash.com/400x175/?github description: API docs for the interactiveSetup plugin -date: 2023-12-06 +date: 2023-12-19 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 05607b208d333..c756601989eeb 100644 --- a/api_docs/kbn_ace.mdx +++ b/api_docs/kbn_ace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ace title: "@kbn/ace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ace plugin -date: 2023-12-06 +date: 2023-12-19 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 21262556ffd87..8fe3b21f248a5 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: 2023-12-06 +date: 2023-12-19 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 debe7b20aa1aa..ed0a6db4ec0b7 100644 --- a/api_docs/kbn_aiops_components.mdx +++ b/api_docs/kbn_aiops_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-components title: "@kbn/aiops-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-components plugin -date: 2023-12-06 +date: 2023-12-19 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 b1028674a5561..fd698569ca494 100644 --- a/api_docs/kbn_aiops_utils.mdx +++ b/api_docs/kbn_aiops_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-aiops-utils title: "@kbn/aiops-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/aiops-utils plugin -date: 2023-12-06 +date: 2023-12-19 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 9137920b999a3..97a48ce9208ca 100644 --- a/api_docs/kbn_alerting_api_integration_helpers.mdx +++ b/api_docs/kbn_alerting_api_integration_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-api-integration-helpers title: "@kbn/alerting-api-integration-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-api-integration-helpers plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-api-integration-helpers'] --- import kbnAlertingApiIntegrationHelpersObj from './kbn_alerting_api_integration_helpers.devdocs.json'; diff --git a/api_docs/kbn_alerting_state_types.mdx b/api_docs/kbn_alerting_state_types.mdx index 73ae31061984d..4c422b3bc080a 100644 --- a/api_docs/kbn_alerting_state_types.mdx +++ b/api_docs/kbn_alerting_state_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerting-state-types title: "@kbn/alerting-state-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerting-state-types plugin -date: 2023-12-06 +date: 2023-12-19 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 8858014b04c5f..d8bdba5c8b581 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: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/alerting-types'] --- import kbnAlertingTypesObj from './kbn_alerting_types.devdocs.json'; diff --git a/api_docs/kbn_alerts_as_data_utils.devdocs.json b/api_docs/kbn_alerts_as_data_utils.devdocs.json index fd6deadf657c8..0431988537292 100644 --- a/api_docs/kbn_alerts_as_data_utils.devdocs.json +++ b/api_docs/kbn_alerts_as_data_utils.devdocs.json @@ -420,7 +420,7 @@ "label": "StackAlert", "description": [], "signature": [ - "{} & { 'kibana.alert.evaluation.conditions'?: string | undefined; 'kibana.alert.evaluation.value'?: string | undefined; 'kibana.alert.title'?: string | undefined; } & { '@timestamp': string | number; 'kibana.alert.instance.id': string; 'kibana.alert.rule.category': string; 'kibana.alert.rule.consumer': string; 'kibana.alert.rule.name': string; 'kibana.alert.rule.producer': string; 'kibana.alert.rule.revision': string | number; 'kibana.alert.rule.rule_type_id': string; 'kibana.alert.rule.uuid': string; 'kibana.alert.status': string; 'kibana.alert.uuid': string; 'kibana.space_ids': string[]; } & { 'event.action'?: string | undefined; 'event.kind'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.duration.us'?: string | number | undefined; 'kibana.alert.end'?: string | number | undefined; 'kibana.alert.flapping'?: boolean | undefined; 'kibana.alert.flapping_history'?: boolean[] | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.start'?: string | number | undefined; 'kibana.alert.time_range'?: { gte?: string | number | undefined; lte?: string | number | undefined; } | undefined; 'kibana.alert.url'?: string | undefined; 'kibana.alert.workflow_assignee_ids'?: string[] | undefined; 'kibana.alert.workflow_status'?: string | undefined; 'kibana.alert.workflow_tags'?: string[] | undefined; 'kibana.version'?: string | undefined; tags?: string[] | undefined; }" + "{} & { 'kibana.alert.evaluation.conditions'?: string | undefined; 'kibana.alert.evaluation.threshold'?: string | number | undefined; 'kibana.alert.evaluation.value'?: string | undefined; 'kibana.alert.title'?: string | undefined; } & { '@timestamp': string | number; 'kibana.alert.instance.id': string; 'kibana.alert.rule.category': string; 'kibana.alert.rule.consumer': string; 'kibana.alert.rule.name': string; 'kibana.alert.rule.producer': string; 'kibana.alert.rule.revision': string | number; 'kibana.alert.rule.rule_type_id': string; 'kibana.alert.rule.uuid': string; 'kibana.alert.status': string; 'kibana.alert.uuid': string; 'kibana.space_ids': string[]; } & { 'event.action'?: string | undefined; 'event.kind'?: string | undefined; 'kibana.alert.action_group'?: string | undefined; 'kibana.alert.case_ids'?: string[] | undefined; 'kibana.alert.duration.us'?: string | number | undefined; 'kibana.alert.end'?: string | number | undefined; 'kibana.alert.flapping'?: boolean | undefined; 'kibana.alert.flapping_history'?: boolean[] | undefined; 'kibana.alert.last_detected'?: string | number | undefined; 'kibana.alert.maintenance_window_ids'?: string[] | undefined; 'kibana.alert.reason'?: string | undefined; 'kibana.alert.rule.execution.uuid'?: string | undefined; 'kibana.alert.rule.parameters'?: unknown; 'kibana.alert.rule.tags'?: string[] | undefined; 'kibana.alert.start'?: string | number | undefined; 'kibana.alert.time_range'?: { gte?: string | number | undefined; lte?: string | number | undefined; } | undefined; 'kibana.alert.url'?: string | undefined; 'kibana.alert.workflow_assignee_ids'?: string[] | undefined; 'kibana.alert.workflow_status'?: string | undefined; 'kibana.alert.workflow_tags'?: string[] | undefined; 'kibana.version'?: string | undefined; tags?: string[] | undefined; }" ], "path": "packages/kbn-alerts-as-data-utils/src/schemas/generated/stack_schema.ts", "deprecated": false, diff --git a/api_docs/kbn_alerts_as_data_utils.mdx b/api_docs/kbn_alerts_as_data_utils.mdx index 3101ea6e7c868..c6c2e697f4043 100644 --- a/api_docs/kbn_alerts_as_data_utils.mdx +++ b/api_docs/kbn_alerts_as_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-as-data-utils title: "@kbn/alerts-as-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-as-data-utils plugin -date: 2023-12-06 +date: 2023-12-19 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 0f7475abc1fb0..24fa8ff68db43 100644 --- a/api_docs/kbn_alerts_ui_shared.mdx +++ b/api_docs/kbn_alerts_ui_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-alerts-ui-shared title: "@kbn/alerts-ui-shared" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/alerts-ui-shared plugin -date: 2023-12-06 +date: 2023-12-19 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 b3751189f89e2..cbbcfa455aa75 100644 --- a/api_docs/kbn_analytics.mdx +++ b/api_docs/kbn_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics title: "@kbn/analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics'] --- import kbnAnalyticsObj from './kbn_analytics.devdocs.json'; diff --git a/api_docs/kbn_analytics_client.devdocs.json b/api_docs/kbn_analytics_client.devdocs.json index ff3773ba777b1..a940c2673439c 100644 --- a/api_docs/kbn_analytics_client.devdocs.json +++ b/api_docs/kbn_analytics_client.devdocs.json @@ -731,16 +731,16 @@ "path": "x-pack/plugins/fleet/server/services/telemetry/fleet_usage_sender.ts" }, { - "plugin": "observabilityAIAssistant", - "path": "x-pack/plugins/observability_ai_assistant/public/components/chat/chat_body.tsx" + "plugin": "fleet", + "path": "x-pack/plugins/fleet/server/services/telemetry/fleet_usage_sender.ts" }, { "plugin": "observabilityAIAssistant", - "path": "x-pack/plugins/observability_ai_assistant/public/utils/storybook_decorator.tsx" + "path": "x-pack/plugins/observability_ai_assistant/public/analytics/index.ts" }, { "plugin": "observabilityAIAssistant", - "path": "x-pack/plugins/observability_ai_assistant/public/components/insight/insight.tsx" + "path": "x-pack/plugins/observability_ai_assistant/public/utils/storybook_decorator.tsx" }, { "plugin": "apm", diff --git a/api_docs/kbn_analytics_client.mdx b/api_docs/kbn_analytics_client.mdx index 5379de10291c0..416181f49c311 100644 --- a/api_docs/kbn_analytics_client.mdx +++ b/api_docs/kbn_analytics_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-client title: "@kbn/analytics-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-client plugin -date: 2023-12-06 +date: 2023-12-19 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 370405df86446..dba58985f7e87 100644 --- a/api_docs/kbn_analytics_collection_utils.mdx +++ b/api_docs/kbn_analytics_collection_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-collection-utils title: "@kbn/analytics-collection-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-collection-utils plugin -date: 2023-12-06 +date: 2023-12-19 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 c053c77554d70..24a7528cb02b3 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-browser title: "@kbn/analytics-shippers-elastic-v3-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-browser plugin -date: 2023-12-06 +date: 2023-12-19 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 54e3c6c9c539c..e2d2380e865cf 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-common title: "@kbn/analytics-shippers-elastic-v3-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-common plugin -date: 2023-12-06 +date: 2023-12-19 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 1b71efe4cce60..298857a4647f1 100644 --- a/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx +++ b/api_docs/kbn_analytics_shippers_elastic_v3_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-elastic-v3-server title: "@kbn/analytics-shippers-elastic-v3-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-elastic-v3-server plugin -date: 2023-12-06 +date: 2023-12-19 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 4cd88cec4af67..b1e2330a7995e 100644 --- a/api_docs/kbn_analytics_shippers_fullstory.mdx +++ b/api_docs/kbn_analytics_shippers_fullstory.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-fullstory title: "@kbn/analytics-shippers-fullstory" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-fullstory plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-fullstory'] --- import kbnAnalyticsShippersFullstoryObj from './kbn_analytics_shippers_fullstory.devdocs.json'; diff --git a/api_docs/kbn_analytics_shippers_gainsight.mdx b/api_docs/kbn_analytics_shippers_gainsight.mdx index 9ab1f3cdef257..0f41d84f92480 100644 --- a/api_docs/kbn_analytics_shippers_gainsight.mdx +++ b/api_docs/kbn_analytics_shippers_gainsight.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-analytics-shippers-gainsight title: "@kbn/analytics-shippers-gainsight" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/analytics-shippers-gainsight plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/analytics-shippers-gainsight'] --- import kbnAnalyticsShippersGainsightObj from './kbn_analytics_shippers_gainsight.devdocs.json'; diff --git a/api_docs/kbn_apm_config_loader.mdx b/api_docs/kbn_apm_config_loader.mdx index 46bd4b704b386..11a93dcd52d3b 100644 --- a/api_docs/kbn_apm_config_loader.mdx +++ b/api_docs/kbn_apm_config_loader.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-config-loader title: "@kbn/apm-config-loader" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-config-loader plugin -date: 2023-12-06 +date: 2023-12-19 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 5a2fe2f2fefcd..f83bb9f9b3466 100644 --- a/api_docs/kbn_apm_synthtrace.mdx +++ b/api_docs/kbn_apm_synthtrace.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace title: "@kbn/apm-synthtrace" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace'] --- import kbnApmSynthtraceObj from './kbn_apm_synthtrace.devdocs.json'; diff --git a/api_docs/kbn_apm_synthtrace_client.devdocs.json b/api_docs/kbn_apm_synthtrace_client.devdocs.json index c3d0a4f306d85..ed09a5e431a2f 100644 --- a/api_docs/kbn_apm_synthtrace_client.devdocs.json +++ b/api_docs/kbn_apm_synthtrace_client.devdocs.json @@ -2510,9 +2510,11 @@ "signature": [ "{ '@timestamp'?: number | undefined; } & Partial<{ meta: { 'metricset.id': string; }; }> & Partial<{ 'timestamp.us'?: number | undefined; 'agent.name': string; 'agent.version': string; 'client.geo.city_name': string; 'client.geo.continent_name': string; 'client.geo.country_iso_code': string; 'client.geo.country_name': string; 'client.geo.location': ", "GeoLocation", - "; 'client.geo.region_iso_code': string; 'client.geo.region_name': string; 'client.ip': string; 'cloud.account.id': string; 'cloud.account.name': string; 'cloud.availability_zone': string; 'cloud.machine.type': string; 'cloud.project.id': string; 'cloud.project.name': string; 'cloud.provider': string; 'cloud.region': string; 'cloud.service.name': string; 'container.id': string; 'destination.address': string; 'destination.port': number; 'device.id': string; 'device.manufacturer': string; 'device.model.identifier': string; 'device.model.name': string; 'ecs.version': string; 'error.exception': ", + "; 'client.geo.region_iso_code': string; 'client.geo.region_name': string; 'client.ip': string; 'cloud.account.id': string; 'cloud.account.name': string; 'cloud.availability_zone': string; 'cloud.machine.type': string; 'cloud.project.id': string; 'cloud.project.name': string; 'cloud.provider': string; 'cloud.region': string; 'cloud.service.name': string; 'code.stacktrace': string; 'container.id': string; 'destination.address': string; 'destination.port': number; 'device.id': string; 'device.manufacturer': string; 'device.model.identifier': string; 'device.model.name': string; 'ecs.version': string; 'error.exception': ", "ApmException", - "[]; 'error.grouping_key': string; 'error.grouping_name': string; 'error.id': string; 'error.type': string; 'event.ingested': number; 'event.name': string; 'event.action': string; 'event.outcome': string; 'event.outcome_numeric': number | { sum: number; value_count: number; }; 'faas.coldstart': boolean; 'faas.execution': string; 'faas.id': string; 'faas.name': string; 'faas.trigger.type': string; 'faas.version': string; 'host.architecture': string; 'host.hostname': string; 'host.name': string; 'host.os.full': string; 'host.os.name': string; 'host.os.platform': string; 'host.os.type': string; 'host.os.version': string; 'http.request.method': string; 'http.response.status_code': number; 'kubernetes.pod.name': string; 'kubernetes.pod.uid': string; 'labels.name': string; 'labels.telemetry_auto_version': string; 'labels.lifecycle_state': string; 'metricset.name': string; 'network.carrier.icc': string; 'network.carrier.mcc': string; 'network.carrier.mnc': string; 'network.carrier.name': string; 'network.connection.subtype': string; 'network.connection.type': string; 'observer.type': string; 'observer.version_major': number; 'observer.version': string; 'parent.id': string; 'processor.event': string; 'processor.name': string; 'session.id': string; 'trace.id': string; 'transaction.aggregation.overflow_count': number; 'transaction.duration.us': number; 'transaction.id': string; 'transaction.name': string; 'transaction.type': string; 'transaction.duration.histogram': { values: number[]; counts: number[]; }; 'transaction.result': string; 'transaction.sampled': boolean; 'service.environment': string; 'service.framework.name': string; 'service.framework.version': string; 'service.language.name': string; 'service.language.version': string; 'service.name': string; 'service.node.name': string; 'service.runtime.name': string; 'service.runtime.version': string; 'service.target.name': string; 'service.target.type': string; 'service.version': string; 'span.action': string; 'span.destination.service.resource': string; 'span.destination.service.response_time.count': number; 'span.destination.service.response_time.sum.us': number; 'span.duration.us': number; 'span.id': string; 'span.name': string; 'span.self_time.count': number; 'span.self_time.sum.us': number; 'span.subtype': string; 'span.type': string; 'span.links': { trace: { id: string; }; span: { id: string; }; }[]; 'url.original': string; }> & Partial<{ 'system.process.memory.size': number; 'system.memory.actual.free': number; 'system.memory.total': number; 'system.process.cgroup.memory.mem.limit.bytes': number; 'system.process.cgroup.memory.mem.usage.bytes': number; 'system.cpu.total.norm.pct': number; 'system.process.memory.rss.bytes': number; 'system.process.cpu.total.norm.pct': number; 'jvm.memory.heap.used': number; 'jvm.memory.non_heap.used': number; 'jvm.thread.count': number; 'faas.billed_duration': number; 'faas.timeout': number; 'faas.coldstart_duration': number; 'faas.duration': number; 'application.launch.time': number; }> & Partial<{ 'metricset.interval': string; 'transaction.duration.summary': string; }>" + "[]; 'error.grouping_key': string; 'error.grouping_name': string; 'error.id': string; 'error.type': string; 'event.ingested': number; 'event.name': string; 'event.action': string; 'event.outcome': string; 'event.outcome_numeric': number | { sum: number; value_count: number; }; 'faas.coldstart': boolean; 'faas.execution': string; 'faas.id': string; 'faas.name': string; 'faas.trigger.type': string; 'faas.version': string; 'host.architecture': string; 'host.hostname': string; 'host.name': string; 'host.os.full': string; 'host.os.name': string; 'host.os.platform': string; 'host.os.type': string; 'host.os.version': string; 'http.request.method': string; 'http.response.status_code': number; 'kubernetes.pod.name': string; 'kubernetes.pod.uid': string; 'labels.name': string; 'labels.telemetry_auto_version': string; 'labels.lifecycle_state': string; 'metricset.name': string; 'network.carrier.icc': string; 'network.carrier.mcc': string; 'network.carrier.mnc': string; 'network.carrier.name': string; 'network.connection.subtype': string; 'network.connection.type': string; 'observer.type': string; 'observer.version_major': number; 'observer.version': string; 'parent.id': string; 'processor.event': string; 'processor.name': string; 'session.id': string; 'trace.id': string; 'transaction.aggregation.overflow_count': number; 'transaction.duration.us': number; 'transaction.id': string; 'transaction.name': string; 'transaction.type': string; 'transaction.duration.histogram': { values: number[]; counts: number[]; }; 'transaction.result': string; 'transaction.sampled': boolean; 'service.environment': string; 'service.framework.name': string; 'service.framework.version': string; 'service.language.name': string; 'service.language.version': string; 'service.name': string; 'service.node.name': string; 'service.runtime.name': string; 'service.runtime.version': string; 'service.target.name': string; 'service.target.type': string; 'service.version': string; 'span.action': string; 'span.destination.service.resource': string; 'span.destination.service.response_time.count': number; 'span.destination.service.response_time.sum.us': number; 'span.duration.us': number; 'span.id': string; 'span.name': string; 'span.stacktrace': ", + "APMStacktrace", + "[]; 'span.self_time.count': number; 'span.self_time.sum.us': number; 'span.subtype': string; 'span.type': string; 'span.links': { trace: { id: string; }; span: { id: string; }; }[]; 'url.original': string; }> & Partial<{ 'system.process.memory.size': number; 'system.memory.actual.free': number; 'system.memory.total': number; 'system.process.cgroup.memory.mem.limit.bytes': number; 'system.process.cgroup.memory.mem.usage.bytes': number; 'system.cpu.total.norm.pct': number; 'system.process.memory.rss.bytes': number; 'system.process.cpu.total.norm.pct': number; 'jvm.memory.heap.used': number; 'jvm.memory.non_heap.used': number; 'jvm.thread.count': number; 'faas.billed_duration': number; 'faas.timeout': number; 'faas.coldstart_duration': number; 'faas.duration': number; 'application.launch.time': number; }> & Partial<{ 'metricset.interval': string; 'transaction.duration.summary': string; }>" ], "path": "packages/kbn-apm-synthtrace-client/src/lib/apm/apm_fields.ts", "deprecated": false, diff --git a/api_docs/kbn_apm_synthtrace_client.mdx b/api_docs/kbn_apm_synthtrace_client.mdx index 7aef74b4d15ec..2a391fff63155 100644 --- a/api_docs/kbn_apm_synthtrace_client.mdx +++ b/api_docs/kbn_apm_synthtrace_client.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-synthtrace-client title: "@kbn/apm-synthtrace-client" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-synthtrace-client plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/apm-synthtrace-client'] --- import kbnApmSynthtraceClientObj from './kbn_apm_synthtrace_client.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/te | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 188 | 0 | 188 | 27 | +| 188 | 0 | 188 | 28 | ## Common diff --git a/api_docs/kbn_apm_utils.mdx b/api_docs/kbn_apm_utils.mdx index 096137b9f5ce4..33ce87aa5ab24 100644 --- a/api_docs/kbn_apm_utils.mdx +++ b/api_docs/kbn_apm_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-apm-utils title: "@kbn/apm-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/apm-utils plugin -date: 2023-12-06 +date: 2023-12-19 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 ab48174ca30d0..64d29795c1b8e 100644 --- a/api_docs/kbn_axe_config.mdx +++ b/api_docs/kbn_axe_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-axe-config title: "@kbn/axe-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/axe-config plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/axe-config'] --- import kbnAxeConfigObj from './kbn_axe_config.devdocs.json'; diff --git a/api_docs/kbn_bfetch_error.devdocs.json b/api_docs/kbn_bfetch_error.devdocs.json new file mode 100644 index 0000000000000..425adaaaf5275 --- /dev/null +++ b/api_docs/kbn_bfetch_error.devdocs.json @@ -0,0 +1,101 @@ +{ + "id": "@kbn/bfetch-error", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [ + { + "parentPluginId": "@kbn/bfetch-error", + "id": "def-common.BfetchRequestError", + "type": "Class", + "tags": [], + "label": "BfetchRequestError", + "description": [ + "\nError thrown when xhr request fails" + ], + "signature": [ + { + "pluginId": "@kbn/bfetch-error", + "scope": "common", + "docId": "kibKbnBfetchErrorPluginApi", + "section": "def-common.BfetchRequestError", + "text": "BfetchRequestError" + }, + " extends Error" + ], + "path": "packages/kbn-bfetch-error/src/bfetch_error.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/bfetch-error", + "id": "def-common.BfetchRequestError.Unnamed", + "type": "Function", + "tags": [], + "label": "Constructor", + "description": [ + "\nconstructor" + ], + "signature": [ + "any" + ], + "path": "packages/kbn-bfetch-error/src/bfetch_error.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/bfetch-error", + "id": "def-common.BfetchRequestError.Unnamed.$1", + "type": "number", + "tags": [], + "label": "code", + "description": [ + "- Xhr error code" + ], + "signature": [ + "number" + ], + "path": "packages/kbn-bfetch-error/src/bfetch_error.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/bfetch-error", + "id": "def-common.BfetchRequestError.code", + "type": "number", + "tags": [], + "label": "code", + "description": [], + "path": "packages/kbn-bfetch-error/src/bfetch_error.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_bfetch_error.mdx b/api_docs/kbn_bfetch_error.mdx new file mode 100644 index 0000000000000..8031118be4078 --- /dev/null +++ b/api_docs/kbn_bfetch_error.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: kibKbnBfetchErrorPluginApi +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: 2023-12-19 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/bfetch-error'] +--- +import kbnBfetchErrorObj from './kbn_bfetch_error.devdocs.json'; + + + +Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 4 | 0 | 1 | 0 | + +## Common + +### Classes + + diff --git a/api_docs/kbn_calculate_auto.mdx b/api_docs/kbn_calculate_auto.mdx index 4ff354dd1e20f..4c905f367cdef 100644 --- a/api_docs/kbn_calculate_auto.mdx +++ b/api_docs/kbn_calculate_auto.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-calculate-auto title: "@kbn/calculate-auto" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/calculate-auto plugin -date: 2023-12-06 +date: 2023-12-19 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 cbabc1173c3a0..fdc76dd7fa26f 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: 2023-12-06 +date: 2023-12-19 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 b74b8f94b53a9..6a59d149f5983 100644 --- a/api_docs/kbn_cases_components.mdx +++ b/api_docs/kbn_cases_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cases-components title: "@kbn/cases-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cases-components plugin -date: 2023-12-06 +date: 2023-12-19 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 9e85ce298e587..c792dc3ef7526 100644 --- a/api_docs/kbn_cell_actions.mdx +++ b/api_docs/kbn_cell_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cell-actions title: "@kbn/cell-actions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cell-actions plugin -date: 2023-12-06 +date: 2023-12-19 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 41cdf516f4fd4..48046c35959a4 100644 --- a/api_docs/kbn_chart_expressions_common.mdx +++ b/api_docs/kbn_chart_expressions_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-expressions-common title: "@kbn/chart-expressions-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-expressions-common plugin -date: 2023-12-06 +date: 2023-12-19 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 8c23ffb69af13..e782250378ad5 100644 --- a/api_docs/kbn_chart_icons.mdx +++ b/api_docs/kbn_chart_icons.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-chart-icons title: "@kbn/chart-icons" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/chart-icons plugin -date: 2023-12-06 +date: 2023-12-19 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 3eb427b361b09..2e79dbdedfd6f 100644 --- a/api_docs/kbn_ci_stats_core.mdx +++ b/api_docs/kbn_ci_stats_core.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-core title: "@kbn/ci-stats-core" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-core plugin -date: 2023-12-06 +date: 2023-12-19 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 22c462a6c2018..39b5cc2616cb6 100644 --- a/api_docs/kbn_ci_stats_performance_metrics.mdx +++ b/api_docs/kbn_ci_stats_performance_metrics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-performance-metrics title: "@kbn/ci-stats-performance-metrics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-performance-metrics plugin -date: 2023-12-06 +date: 2023-12-19 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 e65678bfbd35f..8864be86e08ac 100644 --- a/api_docs/kbn_ci_stats_reporter.mdx +++ b/api_docs/kbn_ci_stats_reporter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ci-stats-reporter title: "@kbn/ci-stats-reporter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ci-stats-reporter plugin -date: 2023-12-06 +date: 2023-12-19 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 90e87e16d7d30..f3a65737a8370 100644 --- a/api_docs/kbn_cli_dev_mode.mdx +++ b/api_docs/kbn_cli_dev_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cli-dev-mode title: "@kbn/cli-dev-mode" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cli-dev-mode plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cli-dev-mode'] --- import kbnCliDevModeObj from './kbn_cli_dev_mode.devdocs.json'; diff --git a/api_docs/kbn_code_editor.mdx b/api_docs/kbn_code_editor.mdx index eb972cc57df2e..75a478ca85892 100644 --- a/api_docs/kbn_code_editor.mdx +++ b/api_docs/kbn_code_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-code-editor title: "@kbn/code-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/code-editor plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-editor'] --- import kbnCodeEditorObj from './kbn_code_editor.devdocs.json'; diff --git a/api_docs/kbn_code_owners.devdocs.json b/api_docs/kbn_code_owners.devdocs.json new file mode 100644 index 0000000000000..f36c551502821 --- /dev/null +++ b/api_docs/kbn_code_owners.devdocs.json @@ -0,0 +1,175 @@ +{ + "id": "@kbn/code-owners", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/code-owners", + "id": "def-common.getCodeOwnersForFile", + "type": "Function", + "tags": [], + "label": "getCodeOwnersForFile", + "description": [ + "\nGet the GitHub CODEOWNERS for a file in the repository" + ], + "signature": [ + "(filePath: string, reversedCodeowners: ", + { + "pluginId": "@kbn/code-owners", + "scope": "common", + "docId": "kibKbnCodeOwnersPluginApi", + "section": "def-common.PathWithOwners", + "text": "PathWithOwners" + }, + "[] | undefined) => string | undefined" + ], + "path": "packages/kbn-code-owners/src/file_code_owner.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/code-owners", + "id": "def-common.getCodeOwnersForFile.$1", + "type": "string", + "tags": [], + "label": "filePath", + "description": [ + "the file to get code owners for" + ], + "signature": [ + "string" + ], + "path": "packages/kbn-code-owners/src/file_code_owner.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/code-owners", + "id": "def-common.getCodeOwnersForFile.$2", + "type": "Array", + "tags": [], + "label": "reversedCodeowners", + "description": [ + "a cached reversed code owners list, use to speed up multiple requests" + ], + "signature": [ + { + "pluginId": "@kbn/code-owners", + "scope": "common", + "docId": "kibKbnCodeOwnersPluginApi", + "section": "def-common.PathWithOwners", + "text": "PathWithOwners" + }, + "[] | undefined" + ], + "path": "packages/kbn-code-owners/src/file_code_owner.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/code-owners", + "id": "def-common.getPathsWithOwnersReversed", + "type": "Function", + "tags": [], + "label": "getPathsWithOwnersReversed", + "description": [ + "\nGet the .github/CODEOWNERS entries, prepared for path matching.\nThe last matching CODEOWNERS entry has highest precedence:\nhttps://help.github.com/articles/about-codeowners/\nso entries are returned in reversed order to later search for the first match." + ], + "signature": [ + "() => ", + { + "pluginId": "@kbn/code-owners", + "scope": "common", + "docId": "kibKbnCodeOwnersPluginApi", + "section": "def-common.PathWithOwners", + "text": "PathWithOwners" + }, + "[]" + ], + "path": "packages/kbn-code-owners/src/file_code_owner.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [ + { + "parentPluginId": "@kbn/code-owners", + "id": "def-common.PathWithOwners", + "type": "Interface", + "tags": [], + "label": "PathWithOwners", + "description": [], + "path": "packages/kbn-code-owners/src/file_code_owner.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/code-owners", + "id": "def-common.PathWithOwners.path", + "type": "string", + "tags": [], + "label": "path", + "description": [], + "path": "packages/kbn-code-owners/src/file_code_owner.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/code-owners", + "id": "def-common.PathWithOwners.teams", + "type": "string", + "tags": [], + "label": "teams", + "description": [], + "path": "packages/kbn-code-owners/src/file_code_owner.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/code-owners", + "id": "def-common.PathWithOwners.ignorePattern", + "type": "Object", + "tags": [], + "label": "ignorePattern", + "description": [], + "signature": [ + "Ignore" + ], + "path": "packages/kbn-code-owners/src/file_code_owner.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_code_owners.mdx b/api_docs/kbn_code_owners.mdx new file mode 100644 index 0000000000000..f7fa10cf12779 --- /dev/null +++ b/api_docs/kbn_code_owners.mdx @@ -0,0 +1,33 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnCodeOwnersPluginApi +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: 2023-12-19 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/code-owners'] +--- +import kbnCodeOwnersObj from './kbn_code_owners.devdocs.json'; + + + +Contact [@elastic/appex-qa](https://github.com/orgs/elastic/teams/appex-qa) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 8 | 0 | 4 | 0 | + +## Common + +### Functions + + +### Interfaces + + diff --git a/api_docs/kbn_coloring.mdx b/api_docs/kbn_coloring.mdx index e940dd0667d29..80f298a4632d1 100644 --- a/api_docs/kbn_coloring.mdx +++ b/api_docs/kbn_coloring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-coloring title: "@kbn/coloring" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/coloring plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/coloring'] --- import kbnColoringObj from './kbn_coloring.devdocs.json'; diff --git a/api_docs/kbn_config.devdocs.json b/api_docs/kbn_config.devdocs.json index 20f8007793e7e..1e255c294bc37 100644 --- a/api_docs/kbn_config.devdocs.json +++ b/api_docs/kbn_config.devdocs.json @@ -40,7 +40,13 @@ ], "signature": [ "{ readonly version: string; readonly branch: string; readonly buildNum: number; readonly buildSha: string; readonly buildDate: Date; readonly buildFlavor: ", - "BuildFlavor", + { + "pluginId": "@kbn/config", + "scope": "common", + "docId": "kibKbnConfigPluginApi", + "section": "def-common.BuildFlavor", + "text": "BuildFlavor" + }, "; readonly dist: boolean; }" ], "path": "packages/kbn-config/src/env.ts", @@ -1051,6 +1057,21 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/config", + "id": "def-common.BuildFlavor", + "type": "Type", + "tags": [], + "label": "BuildFlavor", + "description": [], + "signature": [ + "\"serverless\" | \"traditional\"" + ], + "path": "packages/kbn-config/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/config", "id": "def-common.ConfigDeprecation", diff --git a/api_docs/kbn_config.mdx b/api_docs/kbn_config.mdx index 0267f57d22b9f..a0c3690b8183a 100644 --- a/api_docs/kbn_config.mdx +++ b/api_docs/kbn_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config title: "@kbn/config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config plugin -date: 2023-12-06 +date: 2023-12-19 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 | |-------------------|-----------|------------------------|-----------------| -| 75 | 0 | 46 | 10 | +| 76 | 0 | 47 | 9 | ## Common diff --git a/api_docs/kbn_config_mocks.mdx b/api_docs/kbn_config_mocks.mdx index 86a81e2b71960..f9cbc02ab134b 100644 --- a/api_docs/kbn_config_mocks.mdx +++ b/api_docs/kbn_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-mocks title: "@kbn/config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 e6b40f8507189..67682b5517ef8 100644 --- a/api_docs/kbn_config_schema.mdx +++ b/api_docs/kbn_config_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-config-schema title: "@kbn/config-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/config-schema plugin -date: 2023-12-06 +date: 2023-12-19 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 97ff80736e880..a8a3928ed2857 100644 --- a/api_docs/kbn_content_management_content_editor.mdx +++ b/api_docs/kbn_content_management_content_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-content-editor title: "@kbn/content-management-content-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-content-editor plugin -date: 2023-12-06 +date: 2023-12-19 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 42b5a7ce37cb3..078ce66b2ad8c 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: 2023-12-06 +date: 2023-12-19 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 8651ad44b48cc..d3ab52966796d 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: 2023-12-06 +date: 2023-12-19 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 0be2448f504e1..6dd3efe5031de 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: 2023-12-06 +date: 2023-12-19 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 1964efd6114bc..f3b2fd099d98b 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: 2023-12-06 +date: 2023-12-19 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 da007d191fa08..5003fe1e61026 100644 --- a/api_docs/kbn_content_management_utils.mdx +++ b/api_docs/kbn_content_management_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-content-management-utils title: "@kbn/content-management-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/content-management-utils plugin -date: 2023-12-06 +date: 2023-12-19 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 b86612fb8626b..47e8575d7c83b 100644 --- a/api_docs/kbn_core_analytics_browser.mdx +++ b/api_docs/kbn_core_analytics_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser title: "@kbn/core-analytics-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser plugin -date: 2023-12-06 +date: 2023-12-19 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 5231fbaadca4f..e0e55eaf60e05 100644 --- a/api_docs/kbn_core_analytics_browser_internal.mdx +++ b/api_docs/kbn_core_analytics_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-internal title: "@kbn/core-analytics-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 663a289a6e7f5..643a0a668358b 100644 --- a/api_docs/kbn_core_analytics_browser_mocks.mdx +++ b/api_docs/kbn_core_analytics_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-browser-mocks title: "@kbn/core-analytics-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-browser-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 a87d828b924aa..c1ae188448fd2 100644 --- a/api_docs/kbn_core_analytics_server.mdx +++ b/api_docs/kbn_core_analytics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server title: "@kbn/core-analytics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server plugin -date: 2023-12-06 +date: 2023-12-19 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 7f5aa7aaa0fe5..1f94d2b8a7bf3 100644 --- a/api_docs/kbn_core_analytics_server_internal.mdx +++ b/api_docs/kbn_core_analytics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-internal title: "@kbn/core-analytics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 504ca839536f5..ed98cc7b3b552 100644 --- a/api_docs/kbn_core_analytics_server_mocks.mdx +++ b/api_docs/kbn_core_analytics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-analytics-server-mocks title: "@kbn/core-analytics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-analytics-server-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 19e49f4da7854..f85047d0ffd55 100644 --- a/api_docs/kbn_core_application_browser.mdx +++ b/api_docs/kbn_core_application_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser title: "@kbn/core-application-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser plugin -date: 2023-12-06 +date: 2023-12-19 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 4ee035a7594f4..49e15ca03242e 100644 --- a/api_docs/kbn_core_application_browser_internal.mdx +++ b/api_docs/kbn_core_application_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-internal title: "@kbn/core-application-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 11aa675d199ef..9a1ad23e57b1d 100644 --- a/api_docs/kbn_core_application_browser_mocks.mdx +++ b/api_docs/kbn_core_application_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-browser-mocks title: "@kbn/core-application-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-browser-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 50b8c06dda953..286d4523c5056 100644 --- a/api_docs/kbn_core_application_common.mdx +++ b/api_docs/kbn_core_application_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-application-common title: "@kbn/core-application-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-application-common plugin -date: 2023-12-06 +date: 2023-12-19 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 523cde31fc56a..89be2a9efb8fc 100644 --- a/api_docs/kbn_core_apps_browser_internal.mdx +++ b/api_docs/kbn_core_apps_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-internal title: "@kbn/core-apps-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 5fd341162deec..6faa5346f53f8 100644 --- a/api_docs/kbn_core_apps_browser_mocks.mdx +++ b/api_docs/kbn_core_apps_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-browser-mocks title: "@kbn/core-apps-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-browser-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 0ff2ab9bf084a..7d96c045c6039 100644 --- a/api_docs/kbn_core_apps_server_internal.mdx +++ b/api_docs/kbn_core_apps_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-apps-server-internal title: "@kbn/core-apps-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-apps-server-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 26e63c9c17e3a..7a6cc1b5aadb0 100644 --- a/api_docs/kbn_core_base_browser_mocks.mdx +++ b/api_docs/kbn_core_base_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-browser-mocks title: "@kbn/core-base-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-browser-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 f187c130a62b3..debc0943aeec7 100644 --- a/api_docs/kbn_core_base_common.mdx +++ b/api_docs/kbn_core_base_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-common title: "@kbn/core-base-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-common plugin -date: 2023-12-06 +date: 2023-12-19 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 45632ba18b9e6..2f5bae5058eb1 100644 --- a/api_docs/kbn_core_base_server_internal.mdx +++ b/api_docs/kbn_core_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-internal title: "@kbn/core-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 864b0bdf57857..1cb44d6ce2e9b 100644 --- a/api_docs/kbn_core_base_server_mocks.mdx +++ b/api_docs/kbn_core_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-base-server-mocks title: "@kbn/core-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-base-server-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 37a6bf7845045..705dfbf807019 100644 --- a/api_docs/kbn_core_capabilities_browser_mocks.mdx +++ b/api_docs/kbn_core_capabilities_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-browser-mocks title: "@kbn/core-capabilities-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-browser-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 bfb9d035bb790..d90dd44ce789a 100644 --- a/api_docs/kbn_core_capabilities_common.mdx +++ b/api_docs/kbn_core_capabilities_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-common title: "@kbn/core-capabilities-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-common plugin -date: 2023-12-06 +date: 2023-12-19 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 40d9f9e6368fe..5c78e9036ec1c 100644 --- a/api_docs/kbn_core_capabilities_server.mdx +++ b/api_docs/kbn_core_capabilities_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server title: "@kbn/core-capabilities-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server plugin -date: 2023-12-06 +date: 2023-12-19 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 9ac899caf71a4..31a59a220b3a3 100644 --- a/api_docs/kbn_core_capabilities_server_mocks.mdx +++ b/api_docs/kbn_core_capabilities_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-capabilities-server-mocks title: "@kbn/core-capabilities-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-capabilities-server-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 627c34aa6fb17..2a3bb515fe78d 100644 --- a/api_docs/kbn_core_chrome_browser.mdx +++ b/api_docs/kbn_core_chrome_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser title: "@kbn/core-chrome-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser plugin -date: 2023-12-06 +date: 2023-12-19 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 950e81978cfa1..d68114c93573c 100644 --- a/api_docs/kbn_core_chrome_browser_mocks.mdx +++ b/api_docs/kbn_core_chrome_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-chrome-browser-mocks title: "@kbn/core-chrome-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-chrome-browser-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 1d1c6caa33995..b8c610f43182d 100644 --- a/api_docs/kbn_core_config_server_internal.mdx +++ b/api_docs/kbn_core_config_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-config-server-internal title: "@kbn/core-config-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-config-server-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 e684e7a01f1a8..ec9042f9baa01 100644 --- a/api_docs/kbn_core_custom_branding_browser.mdx +++ b/api_docs/kbn_core_custom_branding_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser title: "@kbn/core-custom-branding-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser plugin -date: 2023-12-06 +date: 2023-12-19 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 49f1143b6a82b..3cc6a0cd5ca12 100644 --- a/api_docs/kbn_core_custom_branding_browser_internal.mdx +++ b/api_docs/kbn_core_custom_branding_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-internal title: "@kbn/core-custom-branding-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 2cbdc9890009f..b2c52617efb42 100644 --- a/api_docs/kbn_core_custom_branding_browser_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-browser-mocks title: "@kbn/core-custom-branding-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-browser-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 0c6ac2cf616d7..337d8d570fe75 100644 --- a/api_docs/kbn_core_custom_branding_common.mdx +++ b/api_docs/kbn_core_custom_branding_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-common title: "@kbn/core-custom-branding-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-common plugin -date: 2023-12-06 +date: 2023-12-19 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 1f0faca4dd57a..c51f4fd899502 100644 --- a/api_docs/kbn_core_custom_branding_server.mdx +++ b/api_docs/kbn_core_custom_branding_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server title: "@kbn/core-custom-branding-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server plugin -date: 2023-12-06 +date: 2023-12-19 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 043bee2652309..fdb486f3a53db 100644 --- a/api_docs/kbn_core_custom_branding_server_internal.mdx +++ b/api_docs/kbn_core_custom_branding_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-internal title: "@kbn/core-custom-branding-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 cecbb55733665..4f85ec0782b46 100644 --- a/api_docs/kbn_core_custom_branding_server_mocks.mdx +++ b/api_docs/kbn_core_custom_branding_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-custom-branding-server-mocks title: "@kbn/core-custom-branding-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-custom-branding-server-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 4a4b1f07bfb8c..ffca83aec1c32 100644 --- a/api_docs/kbn_core_deprecations_browser.mdx +++ b/api_docs/kbn_core_deprecations_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser title: "@kbn/core-deprecations-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser plugin -date: 2023-12-06 +date: 2023-12-19 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 4310ffed62a01..b881a26ed525c 100644 --- a/api_docs/kbn_core_deprecations_browser_internal.mdx +++ b/api_docs/kbn_core_deprecations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-internal title: "@kbn/core-deprecations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 21b71099e025f..abc6fcc53d83a 100644 --- a/api_docs/kbn_core_deprecations_browser_mocks.mdx +++ b/api_docs/kbn_core_deprecations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-browser-mocks title: "@kbn/core-deprecations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-browser-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 2ff0e55b7e3eb..4b22619d5840f 100644 --- a/api_docs/kbn_core_deprecations_common.mdx +++ b/api_docs/kbn_core_deprecations_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-common title: "@kbn/core-deprecations-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-common plugin -date: 2023-12-06 +date: 2023-12-19 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 3535b1c50d843..2af7281cc8c2a 100644 --- a/api_docs/kbn_core_deprecations_server.mdx +++ b/api_docs/kbn_core_deprecations_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server title: "@kbn/core-deprecations-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server plugin -date: 2023-12-06 +date: 2023-12-19 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 72b7114e4ef1b..698a635c845cd 100644 --- a/api_docs/kbn_core_deprecations_server_internal.mdx +++ b/api_docs/kbn_core_deprecations_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-internal title: "@kbn/core-deprecations-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 f3389336a82c7..f305ae0bb8554 100644 --- a/api_docs/kbn_core_deprecations_server_mocks.mdx +++ b/api_docs/kbn_core_deprecations_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-deprecations-server-mocks title: "@kbn/core-deprecations-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-deprecations-server-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 76f4c1415308a..bccaa3af128c6 100644 --- a/api_docs/kbn_core_doc_links_browser.mdx +++ b/api_docs/kbn_core_doc_links_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser title: "@kbn/core-doc-links-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser plugin -date: 2023-12-06 +date: 2023-12-19 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 483dfadbd8d95..7d925496e3bfa 100644 --- a/api_docs/kbn_core_doc_links_browser_mocks.mdx +++ b/api_docs/kbn_core_doc_links_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-browser-mocks title: "@kbn/core-doc-links-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-browser-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 e711f85c5787e..33c7d6131d6ba 100644 --- a/api_docs/kbn_core_doc_links_server.mdx +++ b/api_docs/kbn_core_doc_links_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server title: "@kbn/core-doc-links-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server plugin -date: 2023-12-06 +date: 2023-12-19 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 28235ee0b99da..3f3944abb9137 100644 --- a/api_docs/kbn_core_doc_links_server_mocks.mdx +++ b/api_docs/kbn_core_doc_links_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-doc-links-server-mocks title: "@kbn/core-doc-links-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-doc-links-server-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 90019f4174845..472aea784f63b 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-internal title: "@kbn/core-elasticsearch-client-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 c4ea467aa0abd..14730698e90fc 100644 --- a/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_client_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-client-server-mocks title: "@kbn/core-elasticsearch-client-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-client-server-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 0ade916bd5131..a81aa8800a343 100644 --- a/api_docs/kbn_core_elasticsearch_server.mdx +++ b/api_docs/kbn_core_elasticsearch_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server title: "@kbn/core-elasticsearch-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server plugin -date: 2023-12-06 +date: 2023-12-19 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 94bc521deeb93..76ef47aa0136c 100644 --- a/api_docs/kbn_core_elasticsearch_server_internal.mdx +++ b/api_docs/kbn_core_elasticsearch_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-internal title: "@kbn/core-elasticsearch-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 686f601c54af0..f3b0abae64318 100644 --- a/api_docs/kbn_core_elasticsearch_server_mocks.mdx +++ b/api_docs/kbn_core_elasticsearch_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-elasticsearch-server-mocks title: "@kbn/core-elasticsearch-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-elasticsearch-server-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 ccb44858f6833..34e5ff99c8d5b 100644 --- a/api_docs/kbn_core_environment_server_internal.mdx +++ b/api_docs/kbn_core_environment_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-internal title: "@kbn/core-environment-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 4f2891607d3ec..49949886351d5 100644 --- a/api_docs/kbn_core_environment_server_mocks.mdx +++ b/api_docs/kbn_core_environment_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-environment-server-mocks title: "@kbn/core-environment-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-environment-server-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 26f455dcd4b32..94026d46f38ee 100644 --- a/api_docs/kbn_core_execution_context_browser.mdx +++ b/api_docs/kbn_core_execution_context_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser title: "@kbn/core-execution-context-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser plugin -date: 2023-12-06 +date: 2023-12-19 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 45834d91eb5ca..072c559b83818 100644 --- a/api_docs/kbn_core_execution_context_browser_internal.mdx +++ b/api_docs/kbn_core_execution_context_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-internal title: "@kbn/core-execution-context-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 4a62f202dbcd7..2d6910df5140d 100644 --- a/api_docs/kbn_core_execution_context_browser_mocks.mdx +++ b/api_docs/kbn_core_execution_context_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-browser-mocks title: "@kbn/core-execution-context-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-browser-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 6de88713e550d..70f590ee65a44 100644 --- a/api_docs/kbn_core_execution_context_common.mdx +++ b/api_docs/kbn_core_execution_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-common title: "@kbn/core-execution-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-common plugin -date: 2023-12-06 +date: 2023-12-19 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 9a389d1a54052..59f25bb7aa157 100644 --- a/api_docs/kbn_core_execution_context_server.mdx +++ b/api_docs/kbn_core_execution_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server title: "@kbn/core-execution-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server plugin -date: 2023-12-06 +date: 2023-12-19 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 f47a9e58365f9..b2053822cb352 100644 --- a/api_docs/kbn_core_execution_context_server_internal.mdx +++ b/api_docs/kbn_core_execution_context_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-internal title: "@kbn/core-execution-context-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 1807609122c7b..f6534ddf93234 100644 --- a/api_docs/kbn_core_execution_context_server_mocks.mdx +++ b/api_docs/kbn_core_execution_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-execution-context-server-mocks title: "@kbn/core-execution-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-execution-context-server-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 0f7ef6bc3e217..23d3f869bf113 100644 --- a/api_docs/kbn_core_fatal_errors_browser.mdx +++ b/api_docs/kbn_core_fatal_errors_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser title: "@kbn/core-fatal-errors-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser plugin -date: 2023-12-06 +date: 2023-12-19 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 aa34df5522543..0830813a4c17b 100644 --- a/api_docs/kbn_core_fatal_errors_browser_mocks.mdx +++ b/api_docs/kbn_core_fatal_errors_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-fatal-errors-browser-mocks title: "@kbn/core-fatal-errors-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-fatal-errors-browser-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 59c1ad8d0e421..beaed113982a8 100644 --- a/api_docs/kbn_core_http_browser.mdx +++ b/api_docs/kbn_core_http_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser title: "@kbn/core-http-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser plugin -date: 2023-12-06 +date: 2023-12-19 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 1c2545ccaf4ad..d81b97929f7d7 100644 --- a/api_docs/kbn_core_http_browser_internal.mdx +++ b/api_docs/kbn_core_http_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-internal title: "@kbn/core-http-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 967875f54e4f6..8923821b86b34 100644 --- a/api_docs/kbn_core_http_browser_mocks.mdx +++ b/api_docs/kbn_core_http_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-browser-mocks title: "@kbn/core-http-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-browser-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 6ad6e6f1339f5..f8d9c4dda7248 100644 --- a/api_docs/kbn_core_http_common.mdx +++ b/api_docs/kbn_core_http_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-common title: "@kbn/core-http-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-common plugin -date: 2023-12-06 +date: 2023-12-19 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 014aacf3cb650..c1d4d65e6b3bd 100644 --- a/api_docs/kbn_core_http_context_server_mocks.mdx +++ b/api_docs/kbn_core_http_context_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-context-server-mocks title: "@kbn/core-http-context-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-context-server-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 a74e17d699b05..ac23a8f4f8e41 100644 --- a/api_docs/kbn_core_http_request_handler_context_server.mdx +++ b/api_docs/kbn_core_http_request_handler_context_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-request-handler-context-server title: "@kbn/core-http-request-handler-context-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-request-handler-context-server plugin -date: 2023-12-06 +date: 2023-12-19 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 18d6ac89cddb2..eae57c259bf2b 100644 --- a/api_docs/kbn_core_http_resources_server.mdx +++ b/api_docs/kbn_core_http_resources_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server title: "@kbn/core-http-resources-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server plugin -date: 2023-12-06 +date: 2023-12-19 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 89960047bc769..d856c895d875a 100644 --- a/api_docs/kbn_core_http_resources_server_internal.mdx +++ b/api_docs/kbn_core_http_resources_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-internal title: "@kbn/core-http-resources-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 8592794e84a80..f8694b0d18e3b 100644 --- a/api_docs/kbn_core_http_resources_server_mocks.mdx +++ b/api_docs/kbn_core_http_resources_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-resources-server-mocks title: "@kbn/core-http-resources-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-resources-server-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 4a5f8a6ee68f2..24950bd8422b3 100644 --- a/api_docs/kbn_core_http_router_server_internal.mdx +++ b/api_docs/kbn_core_http_router_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-internal title: "@kbn/core-http-router-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 a63ad17c6b934..d1928d54c4a26 100644 --- a/api_docs/kbn_core_http_router_server_mocks.mdx +++ b/api_docs/kbn_core_http_router_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-router-server-mocks title: "@kbn/core-http-router-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-router-server-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 7269ea03eab1d..9f2a3a12bdd55 100644 --- a/api_docs/kbn_core_http_server.devdocs.json +++ b/api_docs/kbn_core_http_server.devdocs.json @@ -4420,6 +4420,10 @@ "plugin": "serverlessSearch", "path": "x-pack/plugins/serverless_search/server/routes/connectors_routes.ts" }, + { + "plugin": "serverlessSearch", + "path": "x-pack/plugins/serverless_search/server/routes/mapping_routes.ts" + }, { "plugin": "snapshotRestore", "path": "x-pack/plugins/snapshot_restore/server/routes/api/app.ts" @@ -6818,6 +6822,10 @@ "plugin": "serverlessSearch", "path": "x-pack/plugins/serverless_search/server/routes/api_key_routes.ts" }, + { + "plugin": "serverlessSearch", + "path": "x-pack/plugins/serverless_search/server/routes/indices_routes.ts" + }, { "plugin": "serverlessSearch", "path": "x-pack/plugins/serverless_search/server/routes/connectors_routes.ts" @@ -13375,7 +13383,7 @@ }, { "plugin": "cloudSecurityPosture", - "path": "x-pack/plugins/cloud_security_posture/server/routes/csp_rule_template/get_csp_rule_template.ts" + "path": "x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/find/find.ts" }, { "plugin": "cloudSecurityPosture", @@ -13677,6 +13685,10 @@ "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/get.ts" }, + { + "plugin": "securitySolution", + "path": "x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/privileges.ts" + }, { "plugin": "securitySolution", "path": "x-pack/plugins/security_solution/server/endpoint/routes/metadata/index.ts" @@ -14683,6 +14695,10 @@ "plugin": "canvas", "path": "x-pack/plugins/canvas/server/routes/workpad/import.ts" }, + { + "plugin": "cloudSecurityPosture", + "path": "x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/bulk_action/bulk_action.ts" + }, { "plugin": "ecsDataQualityDashboard", "path": "x-pack/plugins/ecs_data_quality_dashboard/server/routes/get_unallowed_field_values.ts" diff --git a/api_docs/kbn_core_http_server.mdx b/api_docs/kbn_core_http_server.mdx index 289ad233ee67a..b098b3a6370de 100644 --- a/api_docs/kbn_core_http_server.mdx +++ b/api_docs/kbn_core_http_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server title: "@kbn/core-http-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server'] --- import kbnCoreHttpServerObj from './kbn_core_http_server.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_internal.mdx b/api_docs/kbn_core_http_server_internal.mdx index 42f77ee79a73c..5f363498c1328 100644 --- a/api_docs/kbn_core_http_server_internal.mdx +++ b/api_docs/kbn_core_http_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-internal title: "@kbn/core-http-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-internal plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-http-server-internal'] --- import kbnCoreHttpServerInternalObj from './kbn_core_http_server_internal.devdocs.json'; diff --git a/api_docs/kbn_core_http_server_mocks.mdx b/api_docs/kbn_core_http_server_mocks.mdx index 006e2f7ff80cb..a5c97a27bac39 100644 --- a/api_docs/kbn_core_http_server_mocks.mdx +++ b/api_docs/kbn_core_http_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-http-server-mocks title: "@kbn/core-http-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-http-server-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 8adfd198cb3cd..9110ceef27f95 100644 --- a/api_docs/kbn_core_i18n_browser.mdx +++ b/api_docs/kbn_core_i18n_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser title: "@kbn/core-i18n-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser plugin -date: 2023-12-06 +date: 2023-12-19 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 f6254599c9b51..dac6807b16561 100644 --- a/api_docs/kbn_core_i18n_browser_mocks.mdx +++ b/api_docs/kbn_core_i18n_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-browser-mocks title: "@kbn/core-i18n-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-browser-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 187a4a8f01291..617a6da45d448 100644 --- a/api_docs/kbn_core_i18n_server.mdx +++ b/api_docs/kbn_core_i18n_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server title: "@kbn/core-i18n-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server plugin -date: 2023-12-06 +date: 2023-12-19 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 8eae2dd6101a4..826199c29a862 100644 --- a/api_docs/kbn_core_i18n_server_internal.mdx +++ b/api_docs/kbn_core_i18n_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-internal title: "@kbn/core-i18n-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 f9b64a8ebc920..67411f732c09e 100644 --- a/api_docs/kbn_core_i18n_server_mocks.mdx +++ b/api_docs/kbn_core_i18n_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-i18n-server-mocks title: "@kbn/core-i18n-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-i18n-server-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 8029f50064266..168f4843c6a12 100644 --- a/api_docs/kbn_core_injected_metadata_browser_mocks.mdx +++ b/api_docs/kbn_core_injected_metadata_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-injected-metadata-browser-mocks title: "@kbn/core-injected-metadata-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-injected-metadata-browser-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 06d8a551056ee..2d5d3338babf6 100644 --- a/api_docs/kbn_core_integrations_browser_internal.mdx +++ b/api_docs/kbn_core_integrations_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-internal title: "@kbn/core-integrations-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 7f7e7c54b35ff..959aa8177c7fc 100644 --- a/api_docs/kbn_core_integrations_browser_mocks.mdx +++ b/api_docs/kbn_core_integrations_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-integrations-browser-mocks title: "@kbn/core-integrations-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-integrations-browser-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 159366e24880a..01e8592314438 100644 --- a/api_docs/kbn_core_lifecycle_browser.mdx +++ b/api_docs/kbn_core_lifecycle_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser title: "@kbn/core-lifecycle-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser plugin -date: 2023-12-06 +date: 2023-12-19 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 c3bbf04503213..1cf75966060d8 100644 --- a/api_docs/kbn_core_lifecycle_browser_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-browser-mocks title: "@kbn/core-lifecycle-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-browser-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 d074cf9bd8ab5..4e868fc119569 100644 --- a/api_docs/kbn_core_lifecycle_server.mdx +++ b/api_docs/kbn_core_lifecycle_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server title: "@kbn/core-lifecycle-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server plugin -date: 2023-12-06 +date: 2023-12-19 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 2eee201f8a935..2f240b4a17fb8 100644 --- a/api_docs/kbn_core_lifecycle_server_mocks.mdx +++ b/api_docs/kbn_core_lifecycle_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-lifecycle-server-mocks title: "@kbn/core-lifecycle-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-lifecycle-server-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 813c22b2d0f6b..2e9297cee4e2c 100644 --- a/api_docs/kbn_core_logging_browser_mocks.mdx +++ b/api_docs/kbn_core_logging_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-browser-mocks title: "@kbn/core-logging-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-browser-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 0ab02fce28f97..581469cdbe353 100644 --- a/api_docs/kbn_core_logging_common_internal.mdx +++ b/api_docs/kbn_core_logging_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-common-internal title: "@kbn/core-logging-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-common-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 e4e8d277b28a0..96008363f31a6 100644 --- a/api_docs/kbn_core_logging_server.mdx +++ b/api_docs/kbn_core_logging_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server title: "@kbn/core-logging-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server plugin -date: 2023-12-06 +date: 2023-12-19 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 ac350f2d2da77..a35247cdb1aa4 100644 --- a/api_docs/kbn_core_logging_server_internal.mdx +++ b/api_docs/kbn_core_logging_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-internal title: "@kbn/core-logging-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 8e0cf90b5bb79..b88510626306b 100644 --- a/api_docs/kbn_core_logging_server_mocks.mdx +++ b/api_docs/kbn_core_logging_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-logging-server-mocks title: "@kbn/core-logging-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-logging-server-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 b49e32449333d..14200348b8b20 100644 --- a/api_docs/kbn_core_metrics_collectors_server_internal.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-internal title: "@kbn/core-metrics-collectors-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 d4c20e05d85b4..e2a8d3af931fb 100644 --- a/api_docs/kbn_core_metrics_collectors_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_collectors_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-collectors-server-mocks title: "@kbn/core-metrics-collectors-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-collectors-server-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 7ccc1521887dc..be61455546c4f 100644 --- a/api_docs/kbn_core_metrics_server.mdx +++ b/api_docs/kbn_core_metrics_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server title: "@kbn/core-metrics-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server plugin -date: 2023-12-06 +date: 2023-12-19 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 1c7128135a509..4bdfa09c0d314 100644 --- a/api_docs/kbn_core_metrics_server_internal.mdx +++ b/api_docs/kbn_core_metrics_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-internal title: "@kbn/core-metrics-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 c00c179b0dec1..c595fa93a7e6a 100644 --- a/api_docs/kbn_core_metrics_server_mocks.mdx +++ b/api_docs/kbn_core_metrics_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-metrics-server-mocks title: "@kbn/core-metrics-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-metrics-server-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 56354c13cd9cf..2361d996ddfe8 100644 --- a/api_docs/kbn_core_mount_utils_browser.mdx +++ b/api_docs/kbn_core_mount_utils_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-mount-utils-browser title: "@kbn/core-mount-utils-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-mount-utils-browser plugin -date: 2023-12-06 +date: 2023-12-19 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 b76d95428b49f..d5d98acccdb4f 100644 --- a/api_docs/kbn_core_node_server.mdx +++ b/api_docs/kbn_core_node_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server title: "@kbn/core-node-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server plugin -date: 2023-12-06 +date: 2023-12-19 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 972fe030f8801..4624e22edb25a 100644 --- a/api_docs/kbn_core_node_server_internal.mdx +++ b/api_docs/kbn_core_node_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-internal title: "@kbn/core-node-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 aaca0da559192..1e4a6590b8066 100644 --- a/api_docs/kbn_core_node_server_mocks.mdx +++ b/api_docs/kbn_core_node_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-node-server-mocks title: "@kbn/core-node-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-node-server-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 dad921a5a4f91..1c47750f19680 100644 --- a/api_docs/kbn_core_notifications_browser.mdx +++ b/api_docs/kbn_core_notifications_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser title: "@kbn/core-notifications-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser plugin -date: 2023-12-06 +date: 2023-12-19 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 3aa7e3c4d6edc..5538ce7e1b470 100644 --- a/api_docs/kbn_core_notifications_browser_internal.mdx +++ b/api_docs/kbn_core_notifications_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-internal title: "@kbn/core-notifications-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 590c64b31d2f1..b376982053f61 100644 --- a/api_docs/kbn_core_notifications_browser_mocks.mdx +++ b/api_docs/kbn_core_notifications_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-notifications-browser-mocks title: "@kbn/core-notifications-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-notifications-browser-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 ee25fdb41e4e0..04569facbcbe7 100644 --- a/api_docs/kbn_core_overlays_browser.mdx +++ b/api_docs/kbn_core_overlays_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser title: "@kbn/core-overlays-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser plugin -date: 2023-12-06 +date: 2023-12-19 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 8632b2077520b..1239c10d3978b 100644 --- a/api_docs/kbn_core_overlays_browser_internal.mdx +++ b/api_docs/kbn_core_overlays_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-internal title: "@kbn/core-overlays-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 cc0b53072a31f..842beedeb1994 100644 --- a/api_docs/kbn_core_overlays_browser_mocks.mdx +++ b/api_docs/kbn_core_overlays_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-overlays-browser-mocks title: "@kbn/core-overlays-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-overlays-browser-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 c88dd187b402f..08b2603bbc73a 100644 --- a/api_docs/kbn_core_plugins_browser.mdx +++ b/api_docs/kbn_core_plugins_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser title: "@kbn/core-plugins-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser plugin -date: 2023-12-06 +date: 2023-12-19 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.devdocs.json b/api_docs/kbn_core_plugins_browser_mocks.devdocs.json index bbc7019c8c6f0..d9c3b4834d040 100644 --- a/api_docs/kbn_core_plugins_browser_mocks.devdocs.json +++ b/api_docs/kbn_core_plugins_browser_mocks.devdocs.json @@ -93,7 +93,13 @@ "description": [], "signature": [ "(config?: unknown, { buildFlavor }?: { buildFlavor?: ", - "BuildFlavor", + { + "pluginId": "@kbn/config", + "scope": "common", + "docId": "kibKbnConfigPluginApi", + "section": "def-common.BuildFlavor", + "text": "BuildFlavor" + }, " | undefined; }) => ", { "pluginId": "@kbn/core-plugins-browser", @@ -132,7 +138,13 @@ "description": [], "signature": [ "{ buildFlavor?: ", - "BuildFlavor", + { + "pluginId": "@kbn/config", + "scope": "common", + "docId": "kibKbnConfigPluginApi", + "section": "def-common.BuildFlavor", + "text": "BuildFlavor" + }, " | undefined; }" ], "path": "packages/core/plugins/core-plugins-browser-mocks/src/plugins_service.mock.ts", diff --git a/api_docs/kbn_core_plugins_browser_mocks.mdx b/api_docs/kbn_core_plugins_browser_mocks.mdx index 24654422cb36b..372b522eb341b 100644 --- a/api_docs/kbn_core_plugins_browser_mocks.mdx +++ b/api_docs/kbn_core_plugins_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-browser-mocks title: "@kbn/core-plugins-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-browser-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 8c3ac574c4983..4a65561a81a38 100644 --- a/api_docs/kbn_core_plugins_contracts_browser.mdx +++ b/api_docs/kbn_core_plugins_contracts_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-contracts-browser title: "@kbn/core-plugins-contracts-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-contracts-browser plugin -date: 2023-12-06 +date: 2023-12-19 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 c257dfdf2c3d7..034a346b6d354 100644 --- a/api_docs/kbn_core_plugins_contracts_server.mdx +++ b/api_docs/kbn_core_plugins_contracts_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-contracts-server title: "@kbn/core-plugins-contracts-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-contracts-server plugin -date: 2023-12-06 +date: 2023-12-19 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 fc8273b259461..815fa4e7b7670 100644 --- a/api_docs/kbn_core_plugins_server.mdx +++ b/api_docs/kbn_core_plugins_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server title: "@kbn/core-plugins-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server plugin -date: 2023-12-06 +date: 2023-12-19 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 d4bc54fa35e1d..3016f85fc3314 100644 --- a/api_docs/kbn_core_plugins_server_mocks.mdx +++ b/api_docs/kbn_core_plugins_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-plugins-server-mocks title: "@kbn/core-plugins-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-plugins-server-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 5af3564d717ac..56d82e2e0de9e 100644 --- a/api_docs/kbn_core_preboot_server.mdx +++ b/api_docs/kbn_core_preboot_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server title: "@kbn/core-preboot-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server plugin -date: 2023-12-06 +date: 2023-12-19 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 78022ff59ea90..819fa334184ea 100644 --- a/api_docs/kbn_core_preboot_server_mocks.mdx +++ b/api_docs/kbn_core_preboot_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-preboot-server-mocks title: "@kbn/core-preboot-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-preboot-server-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 a7a3ad38a2eae..8a3b2b4f5550b 100644 --- a/api_docs/kbn_core_rendering_browser_mocks.mdx +++ b/api_docs/kbn_core_rendering_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-browser-mocks title: "@kbn/core-rendering-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-browser-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 c93e739c9c835..f9964d0ee0815 100644 --- a/api_docs/kbn_core_rendering_server_internal.mdx +++ b/api_docs/kbn_core_rendering_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-internal title: "@kbn/core-rendering-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 378237fc12355..b1e39adff3472 100644 --- a/api_docs/kbn_core_rendering_server_mocks.mdx +++ b/api_docs/kbn_core_rendering_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-rendering-server-mocks title: "@kbn/core-rendering-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-rendering-server-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 8cdd740695be2..947e5d3c34900 100644 --- a/api_docs/kbn_core_root_server_internal.mdx +++ b/api_docs/kbn_core_root_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-root-server-internal title: "@kbn/core-root-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-root-server-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 edf41b5bf1d36..7c7f7370b4583 100644 --- a/api_docs/kbn_core_saved_objects_api_browser.mdx +++ b/api_docs/kbn_core_saved_objects_api_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-browser title: "@kbn/core-saved-objects-api-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-browser plugin -date: 2023-12-06 +date: 2023-12-19 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 51dfc7d588784..cba6a38ce7957 100644 --- a/api_docs/kbn_core_saved_objects_api_server.devdocs.json +++ b/api_docs/kbn_core_saved_objects_api_server.devdocs.json @@ -2716,11 +2716,11 @@ }, { "plugin": "alerting", - "path": "x-pack/plugins/alerting/server/saved_objects/geo_containment/migrations.ts" + "path": "x-pack/plugins/alerting/server/types.ts" }, { "plugin": "alerting", - "path": "x-pack/plugins/alerting/server/saved_objects/geo_containment/migrations.ts" + "path": "x-pack/plugins/alerting/server/types.ts" }, { "plugin": "alerting", @@ -2748,11 +2748,11 @@ }, { "plugin": "alerting", - "path": "x-pack/plugins/alerting/server/types.ts" + "path": "x-pack/plugins/alerting/server/saved_objects/geo_containment/migrations.ts" }, { "plugin": "alerting", - "path": "x-pack/plugins/alerting/server/types.ts" + "path": "x-pack/plugins/alerting/server/saved_objects/geo_containment/migrations.ts" }, { "plugin": "savedSearch", @@ -6035,15 +6035,15 @@ }, { "plugin": "@kbn/core-saved-objects-api-server-internal", - "path": "packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.test.ts" + "path": "packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/create.test.ts" }, { "plugin": "@kbn/core-saved-objects-api-server-internal", - "path": "packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.test.ts" + "path": "packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/create.test.ts" }, { "plugin": "@kbn/core-saved-objects-api-server-internal", - "path": "packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.test.ts" + "path": "packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/create.test.ts" } ] }, @@ -7207,7 +7207,7 @@ }, { "plugin": "@kbn/core-saved-objects-api-server-internal", - "path": "packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.test.ts" + "path": "packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/increment_counter.test.ts" } ] }, diff --git a/api_docs/kbn_core_saved_objects_api_server.mdx b/api_docs/kbn_core_saved_objects_api_server.mdx index 532353dcd5d9c..6373b694a6a6c 100644 --- a/api_docs/kbn_core_saved_objects_api_server.mdx +++ b/api_docs/kbn_core_saved_objects_api_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server title: "@kbn/core-saved-objects-api-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server plugin -date: 2023-12-06 +date: 2023-12-19 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 b497a27a4f659..e8202b1e9bb87 100644 --- a/api_docs/kbn_core_saved_objects_api_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_api_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-api-server-mocks title: "@kbn/core-saved-objects-api-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-api-server-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 ab83d16639899..ca5d32d718b8b 100644 --- a/api_docs/kbn_core_saved_objects_base_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-internal title: "@kbn/core-saved-objects-base-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 5b5891b3871db..288216bae9032 100644 --- a/api_docs/kbn_core_saved_objects_base_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_base_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-base-server-mocks title: "@kbn/core-saved-objects-base-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-base-server-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 bd45ae92a4372..eb82fe8ff1683 100644 --- a/api_docs/kbn_core_saved_objects_browser.mdx +++ b/api_docs/kbn_core_saved_objects_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser title: "@kbn/core-saved-objects-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser plugin -date: 2023-12-06 +date: 2023-12-19 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 1d018d5189aaa..55e760b442b36 100644 --- a/api_docs/kbn_core_saved_objects_browser_internal.mdx +++ b/api_docs/kbn_core_saved_objects_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-internal title: "@kbn/core-saved-objects-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 a148c06a70566..281f642c8275c 100644 --- a/api_docs/kbn_core_saved_objects_browser_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-browser-mocks title: "@kbn/core-saved-objects-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-browser-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 76c4f239a1711..999cf23787994 100644 --- a/api_docs/kbn_core_saved_objects_common.mdx +++ b/api_docs/kbn_core_saved_objects_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-common title: "@kbn/core-saved-objects-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-common plugin -date: 2023-12-06 +date: 2023-12-19 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 887fb99d53fb1..2e1da87c202b4 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-internal title: "@kbn/core-saved-objects-import-export-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 4114bc242151f..b4b0d55d1e868 100644 --- a/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_import_export_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-import-export-server-mocks title: "@kbn/core-saved-objects-import-export-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-import-export-server-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 f85fc0a17bbce..5f1100e945c00 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-internal title: "@kbn/core-saved-objects-migration-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 b26b175d9e7b4..d85aa3a4b97ec 100644 --- a/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_migration_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-migration-server-mocks title: "@kbn/core-saved-objects-migration-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-migration-server-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 fc752c0f8b171..4625cbdfa68fb 100644 --- a/api_docs/kbn_core_saved_objects_server.devdocs.json +++ b/api_docs/kbn_core_saved_objects_server.devdocs.json @@ -6181,11 +6181,11 @@ }, { "plugin": "alerting", - "path": "x-pack/plugins/alerting/server/saved_objects/geo_containment/migrations.ts" + "path": "x-pack/plugins/alerting/server/types.ts" }, { "plugin": "alerting", - "path": "x-pack/plugins/alerting/server/saved_objects/geo_containment/migrations.ts" + "path": "x-pack/plugins/alerting/server/types.ts" }, { "plugin": "alerting", @@ -6213,11 +6213,11 @@ }, { "plugin": "alerting", - "path": "x-pack/plugins/alerting/server/types.ts" + "path": "x-pack/plugins/alerting/server/saved_objects/geo_containment/migrations.ts" }, { "plugin": "alerting", - "path": "x-pack/plugins/alerting/server/types.ts" + "path": "x-pack/plugins/alerting/server/saved_objects/geo_containment/migrations.ts" }, { "plugin": "savedSearch", @@ -6333,6 +6333,215 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectDoc", + "type": "Interface", + "tags": [], + "label": "SavedObjectDoc", + "description": [ + "\nSaved Object base document\n" + ], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectDoc", + "text": "SavedObjectDoc" + }, + "" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/serialization.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectDoc.attributes", + "type": "Uncategorized", + "tags": [], + "label": "attributes", + "description": [], + "signature": [ + "T" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/serialization.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectDoc.id", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "path": "packages/core/saved-objects/core-saved-objects-server/src/serialization.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectDoc.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "packages/core/saved-objects/core-saved-objects-server/src/serialization.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectDoc.namespace", + "type": "string", + "tags": [], + "label": "namespace", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/serialization.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectDoc.namespaces", + "type": "Array", + "tags": [], + "label": "namespaces", + "description": [], + "signature": [ + "string[] | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/serialization.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectDoc.migrationVersion", + "type": "Object", + "tags": [], + "label": "migrationVersion", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-saved-objects-common", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsCommonPluginApi", + "section": "def-common.SavedObjectsMigrationVersion", + "text": "SavedObjectsMigrationVersion" + }, + " | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/serialization.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectDoc.coreMigrationVersion", + "type": "string", + "tags": [], + "label": "coreMigrationVersion", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/serialization.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectDoc.typeMigrationVersion", + "type": "string", + "tags": [], + "label": "typeMigrationVersion", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/serialization.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectDoc.version", + "type": "string", + "tags": [], + "label": "version", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/serialization.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectDoc.updated_at", + "type": "string", + "tags": [], + "label": "updated_at", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/serialization.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectDoc.created_at", + "type": "string", + "tags": [], + "label": "created_at", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/serialization.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectDoc.originId", + "type": "string", + "tags": [], + "label": "originId", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/serialization.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/core-saved-objects-server", + "id": "def-common.SavedObjectDoc.managed", + "type": "CompoundType", + "tags": [], + "label": "managed", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "packages/core/saved-objects/core-saved-objects-server/src/serialization.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/core-saved-objects-server", "id": "def-common.SavedObjectExportBaseOptions", @@ -6654,7 +6863,14 @@ "label": "doc", "description": [], "signature": [ - "SavedObjectDoc & { references?: ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectDoc", + "text": "SavedObjectDoc" + }, + " & { references?: ", { "pluginId": "@kbn/core-saved-objects-common", "scope": "common", @@ -6831,7 +7047,14 @@ "label": "document", "description": [], "signature": [ - "SavedObjectDoc & { references?: ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectDoc", + "text": "SavedObjectDoc" + }, + " & { references?: ", { "pluginId": "@kbn/core-saved-objects-common", "scope": "common", @@ -7827,7 +8050,14 @@ "label": "document", "description": [], "signature": [ - "SavedObjectDoc & { references?: ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectDoc", + "text": "SavedObjectDoc" + }, + " & { references?: ", { "pluginId": "@kbn/core-saved-objects-common", "scope": "common", @@ -8096,7 +8326,14 @@ "label": "document", "description": [], "signature": [ - "SavedObjectDoc & { references?: ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectDoc", + "text": "SavedObjectDoc" + }, + " & { references?: ", { "pluginId": "@kbn/core-saved-objects-common", "scope": "common", @@ -11251,7 +11488,14 @@ "label": "doc", "description": [], "signature": [ - "SavedObjectDoc & { references?: ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectDoc", + "text": "SavedObjectDoc" + }, + " & { references?: ", { "pluginId": "@kbn/core-saved-objects-common", "scope": "common", @@ -11337,7 +11581,14 @@ "label": "document", "description": [], "signature": [ - "SavedObjectDoc & { references?: ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectDoc", + "text": "SavedObjectDoc" + }, + " & { references?: ", { "pluginId": "@kbn/core-saved-objects-common", "scope": "common", @@ -11384,7 +11635,14 @@ "\nDocument type used during model migration.\n" ], "signature": [ - "SavedObjectDoc & { references?: ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectDoc", + "text": "SavedObjectDoc" + }, + " & { references?: ", { "pluginId": "@kbn/core-saved-objects-common", "scope": "common", @@ -11448,7 +11706,14 @@ "label": "document", "description": [], "signature": [ - "SavedObjectDoc & { references?: ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectDoc", + "text": "SavedObjectDoc" + }, + " & { references?: ", { "pluginId": "@kbn/core-saved-objects-common", "scope": "common", @@ -11534,7 +11799,14 @@ "label": "document", "description": [], "signature": [ - "SavedObjectDoc & { references?: ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectDoc", + "text": "SavedObjectDoc" + }, + " & { references?: ", { "pluginId": "@kbn/core-saved-objects-common", "scope": "common", @@ -11671,7 +11943,14 @@ "\nDescribes Saved Object documents that have passed through the migration\nframework and are guaranteed to have a `references` root property.\n" ], "signature": [ - "SavedObjectDoc & { references: ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectDoc", + "text": "SavedObjectDoc" + }, + " & { references: ", { "pluginId": "@kbn/core-saved-objects-common", "scope": "common", @@ -12505,7 +12784,14 @@ "\nDescribes Saved Object documents from Kibana < 7.0.0 which don't have a\n`references` root property defined. This type should only be used in\nmigrations.\n" ], "signature": [ - "SavedObjectDoc & { references?: ", + { + "pluginId": "@kbn/core-saved-objects-server", + "scope": "common", + "docId": "kibKbnCoreSavedObjectsServerPluginApi", + "section": "def-common.SavedObjectDoc", + "text": "SavedObjectDoc" + }, + " & { references?: ", { "pluginId": "@kbn/core-saved-objects-common", "scope": "common", diff --git a/api_docs/kbn_core_saved_objects_server.mdx b/api_docs/kbn_core_saved_objects_server.mdx index ec6d429102cc3..74fd1b9b6d5ec 100644 --- a/api_docs/kbn_core_saved_objects_server.mdx +++ b/api_docs/kbn_core_saved_objects_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server title: "@kbn/core-saved-objects-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/core-saved-objects-server'] --- import kbnCoreSavedObjectsServerObj from './kbn_core_saved_objects_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 | |-------------------|-----------|------------------------|-----------------| -| 541 | 1 | 117 | 4 | +| 555 | 1 | 130 | 4 | ## Common diff --git a/api_docs/kbn_core_saved_objects_server_internal.mdx b/api_docs/kbn_core_saved_objects_server_internal.mdx index ba0fc2625e733..5e25cde7941e8 100644 --- a/api_docs/kbn_core_saved_objects_server_internal.mdx +++ b/api_docs/kbn_core_saved_objects_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-internal title: "@kbn/core-saved-objects-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 825dd41eb3e45..e87796cd6e71a 100644 --- a/api_docs/kbn_core_saved_objects_server_mocks.mdx +++ b/api_docs/kbn_core_saved_objects_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-server-mocks title: "@kbn/core-saved-objects-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-server-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 b306fe34bad3a..be41a13834cf0 100644 --- a/api_docs/kbn_core_saved_objects_utils_server.mdx +++ b/api_docs/kbn_core_saved_objects_utils_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-saved-objects-utils-server title: "@kbn/core-saved-objects-utils-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-saved-objects-utils-server plugin -date: 2023-12-06 +date: 2023-12-19 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 736ae44b7cf5e..fac78d294c1d5 100644 --- a/api_docs/kbn_core_status_common.mdx +++ b/api_docs/kbn_core_status_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common title: "@kbn/core-status-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common plugin -date: 2023-12-06 +date: 2023-12-19 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 a22cbc3996c19..44c611d6ce25a 100644 --- a/api_docs/kbn_core_status_common_internal.mdx +++ b/api_docs/kbn_core_status_common_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-common-internal title: "@kbn/core-status-common-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-common-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 770e11254d5ad..d0252260f1317 100644 --- a/api_docs/kbn_core_status_server.mdx +++ b/api_docs/kbn_core_status_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server title: "@kbn/core-status-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server plugin -date: 2023-12-06 +date: 2023-12-19 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 d08d25ab0af65..e1c22722c55a9 100644 --- a/api_docs/kbn_core_status_server_internal.mdx +++ b/api_docs/kbn_core_status_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-internal title: "@kbn/core-status-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 186bbc75bdfd7..6d5d58ba2ea8d 100644 --- a/api_docs/kbn_core_status_server_mocks.mdx +++ b/api_docs/kbn_core_status_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-status-server-mocks title: "@kbn/core-status-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-status-server-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 fb90c0de54212..1d5f488286f36 100644 --- a/api_docs/kbn_core_test_helpers_deprecations_getters.mdx +++ b/api_docs/kbn_core_test_helpers_deprecations_getters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-deprecations-getters title: "@kbn/core-test-helpers-deprecations-getters" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-deprecations-getters plugin -date: 2023-12-06 +date: 2023-12-19 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 89e8534c52608..cc3818fb4532b 100644 --- a/api_docs/kbn_core_test_helpers_http_setup_browser.mdx +++ b/api_docs/kbn_core_test_helpers_http_setup_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-http-setup-browser title: "@kbn/core-test-helpers-http-setup-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-http-setup-browser plugin -date: 2023-12-06 +date: 2023-12-19 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 8c5c13b2e299c..27eb99418587e 100644 --- a/api_docs/kbn_core_test_helpers_kbn_server.mdx +++ b/api_docs/kbn_core_test_helpers_kbn_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-kbn-server title: "@kbn/core-test-helpers-kbn-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-kbn-server plugin -date: 2023-12-06 +date: 2023-12-19 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 64c9cd0b69890..b3ff61e31f38c 100644 --- a/api_docs/kbn_core_test_helpers_model_versions.mdx +++ b/api_docs/kbn_core_test_helpers_model_versions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-model-versions title: "@kbn/core-test-helpers-model-versions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-model-versions plugin -date: 2023-12-06 +date: 2023-12-19 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 ca19d2b4aae61..6a2cb3129381f 100644 --- a/api_docs/kbn_core_test_helpers_so_type_serializer.mdx +++ b/api_docs/kbn_core_test_helpers_so_type_serializer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-so-type-serializer title: "@kbn/core-test-helpers-so-type-serializer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-so-type-serializer plugin -date: 2023-12-06 +date: 2023-12-19 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 7e4fda9109c66..1da3f33db7341 100644 --- a/api_docs/kbn_core_test_helpers_test_utils.mdx +++ b/api_docs/kbn_core_test_helpers_test_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-test-helpers-test-utils title: "@kbn/core-test-helpers-test-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-test-helpers-test-utils plugin -date: 2023-12-06 +date: 2023-12-19 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 c9c5b8c691e50..2314addf03f47 100644 --- a/api_docs/kbn_core_theme_browser.mdx +++ b/api_docs/kbn_core_theme_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser title: "@kbn/core-theme-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser plugin -date: 2023-12-06 +date: 2023-12-19 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 6d545e224a912..c77212f34cc23 100644 --- a/api_docs/kbn_core_theme_browser_mocks.mdx +++ b/api_docs/kbn_core_theme_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-theme-browser-mocks title: "@kbn/core-theme-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-theme-browser-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 b6c5bc787ea2d..9377e25bca853 100644 --- a/api_docs/kbn_core_ui_settings_browser.mdx +++ b/api_docs/kbn_core_ui_settings_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser title: "@kbn/core-ui-settings-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser plugin -date: 2023-12-06 +date: 2023-12-19 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 15b921c751934..01a53dbd7824e 100644 --- a/api_docs/kbn_core_ui_settings_browser_internal.mdx +++ b/api_docs/kbn_core_ui_settings_browser_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-internal title: "@kbn/core-ui-settings-browser-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 59fe3e2baa211..6e60ab9cc92a1 100644 --- a/api_docs/kbn_core_ui_settings_browser_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_browser_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-browser-mocks title: "@kbn/core-ui-settings-browser-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-browser-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 8e2da2c987d2a..e03f0506fc95d 100644 --- a/api_docs/kbn_core_ui_settings_common.mdx +++ b/api_docs/kbn_core_ui_settings_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-common title: "@kbn/core-ui-settings-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-common plugin -date: 2023-12-06 +date: 2023-12-19 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 3b5988b9f038e..5e188dd30ba4a 100644 --- a/api_docs/kbn_core_ui_settings_server.mdx +++ b/api_docs/kbn_core_ui_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server title: "@kbn/core-ui-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server plugin -date: 2023-12-06 +date: 2023-12-19 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 cc360a075dd0c..f2275a15870c1 100644 --- a/api_docs/kbn_core_ui_settings_server_internal.mdx +++ b/api_docs/kbn_core_ui_settings_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-internal title: "@kbn/core-ui-settings-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 31f31f84f8182..405235278cf43 100644 --- a/api_docs/kbn_core_ui_settings_server_mocks.mdx +++ b/api_docs/kbn_core_ui_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-ui-settings-server-mocks title: "@kbn/core-ui-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-ui-settings-server-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 31ccc8b6dc9ce..670019d88384e 100644 --- a/api_docs/kbn_core_usage_data_server.mdx +++ b/api_docs/kbn_core_usage_data_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server title: "@kbn/core-usage-data-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server plugin -date: 2023-12-06 +date: 2023-12-19 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 0d9fd4d75e2f3..32738d7554817 100644 --- a/api_docs/kbn_core_usage_data_server_internal.mdx +++ b/api_docs/kbn_core_usage_data_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-internal title: "@kbn/core-usage-data-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 83634bea48d9b..a49b7080fd441 100644 --- a/api_docs/kbn_core_usage_data_server_mocks.mdx +++ b/api_docs/kbn_core_usage_data_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-usage-data-server-mocks title: "@kbn/core-usage-data-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-usage-data-server-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 aba65548ae33e..ea835caf267f0 100644 --- a/api_docs/kbn_core_user_settings_server.mdx +++ b/api_docs/kbn_core_user_settings_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server title: "@kbn/core-user-settings-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server plugin -date: 2023-12-06 +date: 2023-12-19 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 4fd14aef7ebc3..149f43efb4bff 100644 --- a/api_docs/kbn_core_user_settings_server_internal.mdx +++ b/api_docs/kbn_core_user_settings_server_internal.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server-internal title: "@kbn/core-user-settings-server-internal" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server-internal plugin -date: 2023-12-06 +date: 2023-12-19 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 b098381d6e61b..ad0bbdb8ac59f 100644 --- a/api_docs/kbn_core_user_settings_server_mocks.mdx +++ b/api_docs/kbn_core_user_settings_server_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-core-user-settings-server-mocks title: "@kbn/core-user-settings-server-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/core-user-settings-server-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 ea735a65db460..ed1d6b7ec9b20 100644 --- a/api_docs/kbn_crypto.mdx +++ b/api_docs/kbn_crypto.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto title: "@kbn/crypto" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto plugin -date: 2023-12-06 +date: 2023-12-19 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 6478681e16f4f..fc750253f1aaf 100644 --- a/api_docs/kbn_crypto_browser.mdx +++ b/api_docs/kbn_crypto_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-crypto-browser title: "@kbn/crypto-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/crypto-browser plugin -date: 2023-12-06 +date: 2023-12-19 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 827cf426e0ac1..7e1b3b8b287c3 100644 --- a/api_docs/kbn_custom_icons.mdx +++ b/api_docs/kbn_custom_icons.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-custom-icons title: "@kbn/custom-icons" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/custom-icons plugin -date: 2023-12-06 +date: 2023-12-19 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 179ca7a031295..53c0b5b8b26da 100644 --- a/api_docs/kbn_custom_integrations.mdx +++ b/api_docs/kbn_custom_integrations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-custom-integrations title: "@kbn/custom-integrations" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/custom-integrations plugin -date: 2023-12-06 +date: 2023-12-19 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 5d384ceca36f8..89188e5435826 100644 --- a/api_docs/kbn_cypress_config.mdx +++ b/api_docs/kbn_cypress_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-cypress-config title: "@kbn/cypress-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/cypress-config plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/cypress-config'] --- import kbnCypressConfigObj from './kbn_cypress_config.devdocs.json'; diff --git a/api_docs/kbn_data_service.mdx b/api_docs/kbn_data_service.mdx index 21d45f5ea757f..f6629261e90ca 100644 --- a/api_docs/kbn_data_service.mdx +++ b/api_docs/kbn_data_service.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-data-service title: "@kbn/data-service" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/data-service plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/data-service'] --- import kbnDataServiceObj from './kbn_data_service.devdocs.json'; diff --git a/api_docs/kbn_datemath.mdx b/api_docs/kbn_datemath.mdx index 358a437ffe2c9..e734d920e375f 100644 --- a/api_docs/kbn_datemath.mdx +++ b/api_docs/kbn_datemath.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-datemath title: "@kbn/datemath" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/datemath plugin -date: 2023-12-06 +date: 2023-12-19 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 3ba32bf99b1bf..481a498ab4abb 100644 --- a/api_docs/kbn_deeplinks_analytics.mdx +++ b/api_docs/kbn_deeplinks_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-analytics title: "@kbn/deeplinks-analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-analytics plugin -date: 2023-12-06 +date: 2023-12-19 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 f4d5df6045426..6714fd2602cb5 100644 --- a/api_docs/kbn_deeplinks_devtools.mdx +++ b/api_docs/kbn_deeplinks_devtools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-devtools title: "@kbn/deeplinks-devtools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-devtools plugin -date: 2023-12-06 +date: 2023-12-19 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 e18e7fc4c02b6..321247d98dbd3 100644 --- a/api_docs/kbn_deeplinks_management.mdx +++ b/api_docs/kbn_deeplinks_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-management title: "@kbn/deeplinks-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-management plugin -date: 2023-12-06 +date: 2023-12-19 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 5bac356eaac98..fa4a891d8264f 100644 --- a/api_docs/kbn_deeplinks_ml.mdx +++ b/api_docs/kbn_deeplinks_ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-ml title: "@kbn/deeplinks-ml" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-ml plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-ml'] --- import kbnDeeplinksMlObj from './kbn_deeplinks_ml.devdocs.json'; diff --git a/api_docs/kbn_deeplinks_observability.devdocs.json b/api_docs/kbn_deeplinks_observability.devdocs.json index 866d03989c167..c1148395fe4d8 100644 --- a/api_docs/kbn_deeplinks_observability.devdocs.json +++ b/api_docs/kbn_deeplinks_observability.devdocs.json @@ -190,15 +190,22 @@ }, { "parentPluginId": "@kbn/deeplinks-observability", - "id": "def-common.LogExplorerNavigationParams.sort", + "id": "def-common.LogExplorerNavigationParams.filters", "type": "Array", "tags": [], - "label": "sort", + "label": "filters", "description": [ - "\nArray of the used sorting [[field,direction],...]" + "\nOptionally apply free-form filters." ], "signature": [ - "string[][] | undefined" + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[] | undefined" ], "path": "packages/deeplinks/observability/locators/log_explorer.ts", "deprecated": false, @@ -206,22 +213,22 @@ }, { "parentPluginId": "@kbn/deeplinks-observability", - "id": "def-common.LogExplorerNavigationParams.filters", - "type": "Array", + "id": "def-common.LogExplorerNavigationParams.filterControls", + "type": "Object", "tags": [], - "label": "filters", + "label": "filterControls", "description": [ - "\nOptionally apply filters." + "\nOptionally apply curated filter controls" ], "signature": [ { - "pluginId": "@kbn/es-query", + "pluginId": "@kbn/deeplinks-observability", "scope": "common", - "docId": "kibKbnEsQueryPluginApi", - "section": "def-common.Filter", - "text": "Filter" + "docId": "kibKbnDeeplinksObservabilityPluginApi", + "section": "def-common.FilterControls", + "text": "FilterControls" }, - "[] | undefined" + " | undefined" ], "path": "packages/deeplinks/observability/locators/log_explorer.ts", "deprecated": false, @@ -495,6 +502,44 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/deeplinks-observability", + "id": "def-common.FilterControls", + "type": "Type", + "tags": [], + "label": "FilterControls", + "description": [], + "signature": [ + "{ namespace?: ", + { + "pluginId": "@kbn/deeplinks-observability", + "scope": "common", + "docId": "kibKbnDeeplinksObservabilityPluginApi", + "section": "def-common.ListFilterControl", + "text": "ListFilterControl" + }, + " | undefined; }" + ], + "path": "packages/deeplinks/observability/locators/log_explorer.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/deeplinks-observability", + "id": "def-common.ListFilterControl", + "type": "Type", + "tags": [], + "label": "ListFilterControl", + "description": [], + "signature": [ + "{ mode: \"include\"; values: string[]; }" + ], + "path": "packages/deeplinks/observability/locators/log_explorer.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/deeplinks-observability", "id": "def-common.LOG_EXPLORER_LOCATOR_ID", @@ -510,6 +555,36 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/deeplinks-observability", + "id": "def-common.LOGS_APP_ID", + "type": "string", + "tags": [], + "label": "LOGS_APP_ID", + "description": [], + "signature": [ + "\"logs\"" + ], + "path": "packages/deeplinks/observability/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/deeplinks-observability", + "id": "def-common.OBSERVABILITY_LOG_EXPLORER_APP_ID", + "type": "string", + "tags": [], + "label": "OBSERVABILITY_LOG_EXPLORER_APP_ID", + "description": [], + "signature": [ + "\"observability-log-explorer\"" + ], + "path": "packages/deeplinks/observability/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/deeplinks-observability", "id": "def-common.OBSERVABILITY_ONBOARDING_APP_ID", @@ -540,6 +615,21 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/deeplinks-observability", + "id": "def-common.OBSERVABILITY_OVERVIEW_APP_ID", + "type": "string", + "tags": [], + "label": "OBSERVABILITY_OVERVIEW_APP_ID", + "description": [], + "signature": [ + "\"observability-overview\"" + ], + "path": "packages/deeplinks/observability/constants.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/deeplinks-observability", "id": "def-common.RefreshInterval", diff --git a/api_docs/kbn_deeplinks_observability.mdx b/api_docs/kbn_deeplinks_observability.mdx index 4b545c5fed8d7..76023dbad7e3e 100644 --- a/api_docs/kbn_deeplinks_observability.mdx +++ b/api_docs/kbn_deeplinks_observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-observability title: "@kbn/deeplinks-observability" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-observability plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/deeplinks-observability'] --- import kbnDeeplinksObservabilityObj from './kbn_deeplinks_observability.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 26 | 0 | 16 | 0 | +| 31 | 0 | 21 | 0 | ## Common diff --git a/api_docs/kbn_deeplinks_search.mdx b/api_docs/kbn_deeplinks_search.mdx index 784dd1ef28d8e..759b2b92bf63a 100644 --- a/api_docs/kbn_deeplinks_search.mdx +++ b/api_docs/kbn_deeplinks_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-deeplinks-search title: "@kbn/deeplinks-search" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/deeplinks-search plugin -date: 2023-12-06 +date: 2023-12-19 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 dfe5cb5d17531..fc4ccb1dbcc9e 100644 --- a/api_docs/kbn_default_nav_analytics.mdx +++ b/api_docs/kbn_default_nav_analytics.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-analytics title: "@kbn/default-nav-analytics" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-analytics plugin -date: 2023-12-06 +date: 2023-12-19 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 1657b4bf2e811..ba3b5a9b78f4d 100644 --- a/api_docs/kbn_default_nav_devtools.mdx +++ b/api_docs/kbn_default_nav_devtools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-devtools title: "@kbn/default-nav-devtools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-devtools plugin -date: 2023-12-06 +date: 2023-12-19 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 c2d1e334fab0a..08cdbe2a68571 100644 --- a/api_docs/kbn_default_nav_management.mdx +++ b/api_docs/kbn_default_nav_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-management title: "@kbn/default-nav-management" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-management plugin -date: 2023-12-06 +date: 2023-12-19 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 961b67847c595..6a1552f850131 100644 --- a/api_docs/kbn_default_nav_ml.mdx +++ b/api_docs/kbn_default_nav_ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-default-nav-ml title: "@kbn/default-nav-ml" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/default-nav-ml plugin -date: 2023-12-06 +date: 2023-12-19 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 9e31040426108..b520779371260 100644 --- a/api_docs/kbn_dev_cli_errors.mdx +++ b/api_docs/kbn_dev_cli_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-errors title: "@kbn/dev-cli-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-errors plugin -date: 2023-12-06 +date: 2023-12-19 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 c5a9900c27a9e..1c85d879f5bb0 100644 --- a/api_docs/kbn_dev_cli_runner.mdx +++ b/api_docs/kbn_dev_cli_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-cli-runner title: "@kbn/dev-cli-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-cli-runner plugin -date: 2023-12-06 +date: 2023-12-19 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 9412574e49ec6..7797b8ab752f1 100644 --- a/api_docs/kbn_dev_proc_runner.mdx +++ b/api_docs/kbn_dev_proc_runner.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-proc-runner title: "@kbn/dev-proc-runner" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-proc-runner plugin -date: 2023-12-06 +date: 2023-12-19 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 4c934cbad51d9..e331883830523 100644 --- a/api_docs/kbn_dev_utils.mdx +++ b/api_docs/kbn_dev_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dev-utils title: "@kbn/dev-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dev-utils plugin -date: 2023-12-06 +date: 2023-12-19 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 139f393095cc3..12cb482ab3f17 100644 --- a/api_docs/kbn_discover_utils.mdx +++ b/api_docs/kbn_discover_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-discover-utils title: "@kbn/discover-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/discover-utils plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/discover-utils'] --- import kbnDiscoverUtilsObj from './kbn_discover_utils.devdocs.json'; diff --git a/api_docs/kbn_doc_links.devdocs.json b/api_docs/kbn_doc_links.devdocs.json index 6a8a72c006b1b..0120336a220ea 100644 --- a/api_docs/kbn_doc_links.devdocs.json +++ b/api_docs/kbn_doc_links.devdocs.json @@ -27,7 +27,7 @@ "label": "getDocLinks", "description": [], "signature": [ - "({ kibanaBranch }: ", + "({ kibanaBranch, buildFlavor }: ", "GetDocLinkOptions", ") => ", { @@ -47,7 +47,7 @@ "id": "def-common.getDocLinks.$1", "type": "Object", "tags": [], - "label": "{ kibanaBranch }", + "label": "{ kibanaBranch, buildFlavor }", "description": [], "signature": [ "GetDocLinkOptions" @@ -69,7 +69,7 @@ "label": "getDocLinksMeta", "description": [], "signature": [ - "({ kibanaBranch }: ", + "({ kibanaBranch, buildFlavor, }: ", "GetDocLinksMetaOptions", ") => ", { @@ -89,7 +89,7 @@ "id": "def-common.getDocLinksMeta.$1", "type": "Object", "tags": [], - "label": "{ kibanaBranch }", + "label": "{\n kibanaBranch,\n buildFlavor,\n}", "description": [], "signature": [ "GetDocLinksMetaOptions" @@ -840,7 +840,7 @@ "label": "fleet", "description": [], "signature": [ - "{ readonly beatsAgentComparison: string; readonly guide: string; readonly fleetServer: string; readonly fleetServerAddFleetServer: string; readonly esSettings: string; readonly settings: string; readonly logstashSettings: string; readonly kafkaSettings: string; readonly settingsFleetServerHostSettings: string; readonly settingsFleetServerProxySettings: string; readonly troubleshooting: string; readonly elasticAgent: string; readonly datastreams: string; readonly datastreamsILM: string; readonly datastreamsNamingScheme: string; readonly datastreamsManualRollover: string; readonly datastreamsTSDS: string; readonly datastreamsTSDSMetrics: string; readonly installElasticAgent: string; readonly installElasticAgentStandalone: string; readonly packageSignatures: string; readonly upgradeElasticAgent: string; readonly learnMoreBlog: string; readonly apiKeysLearnMore: string; readonly onPremRegistry: string; readonly secureLogstash: string; readonly agentPolicy: string; readonly api: string; readonly uninstallAgent: string; readonly installAndUninstallIntegrationAssets: string; readonly elasticAgentInputConfiguration: string; readonly policySecrets: string; readonly remoteESOoutput: string; }" + "{ readonly beatsAgentComparison: string; readonly guide: string; readonly fleetServer: string; readonly fleetServerAddFleetServer: string; readonly esSettings: string; readonly settings: string; readonly logstashSettings: string; readonly kafkaSettings: string; readonly settingsFleetServerHostSettings: string; readonly settingsFleetServerProxySettings: string; readonly troubleshooting: string; readonly elasticAgent: string; readonly datastreams: string; readonly datastreamsILM: string; readonly datastreamsNamingScheme: string; readonly datastreamsManualRollover: string; readonly datastreamsTSDS: string; readonly datastreamsTSDSMetrics: string; readonly installElasticAgent: string; readonly installElasticAgentStandalone: string; readonly packageSignatures: string; readonly upgradeElasticAgent: string; readonly learnMoreBlog: string; readonly apiKeysLearnMore: string; readonly onPremRegistry: string; readonly secureLogstash: string; readonly agentPolicy: string; readonly api: string; readonly uninstallAgent: string; readonly installAndUninstallIntegrationAssets: string; readonly elasticAgentInputConfiguration: string; readonly policySecrets: string; readonly remoteESOoutput: string; readonly performancePresets: string; }" ], "path": "packages/kbn-doc-links/src/types.ts", "deprecated": false, diff --git a/api_docs/kbn_doc_links.mdx b/api_docs/kbn_doc_links.mdx index 02288652f6d70..804d5929c2d42 100644 --- a/api_docs/kbn_doc_links.mdx +++ b/api_docs/kbn_doc_links.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-doc-links title: "@kbn/doc-links" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/doc-links plugin -date: 2023-12-06 +date: 2023-12-19 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 0149076fa9bb7..6f2e9e99e0b01 100644 --- a/api_docs/kbn_docs_utils.mdx +++ b/api_docs/kbn_docs_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-docs-utils title: "@kbn/docs-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/docs-utils plugin -date: 2023-12-06 +date: 2023-12-19 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 a44e98f40d55f..7a7f728b54a28 100644 --- a/api_docs/kbn_dom_drag_drop.mdx +++ b/api_docs/kbn_dom_drag_drop.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-dom-drag-drop title: "@kbn/dom-drag-drop" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/dom-drag-drop plugin -date: 2023-12-06 +date: 2023-12-19 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 695da72403d05..9826ad9790f46 100644 --- a/api_docs/kbn_ebt_tools.mdx +++ b/api_docs/kbn_ebt_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ebt-tools title: "@kbn/ebt-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ebt-tools plugin -date: 2023-12-06 +date: 2023-12-19 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 fe019478b9ee0..0e07f4b5e1639 100644 --- a/api_docs/kbn_ecs.mdx +++ b/api_docs/kbn_ecs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ecs title: "@kbn/ecs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ecs plugin -date: 2023-12-06 +date: 2023-12-19 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 0af210660c732..38c474ed3963c 100644 --- a/api_docs/kbn_ecs_data_quality_dashboard.mdx +++ b/api_docs/kbn_ecs_data_quality_dashboard.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ecs-data-quality-dashboard title: "@kbn/ecs-data-quality-dashboard" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ecs-data-quality-dashboard plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ecs-data-quality-dashboard'] --- import kbnEcsDataQualityDashboardObj from './kbn_ecs_data_quality_dashboard.devdocs.json'; diff --git a/api_docs/kbn_elastic_agent_utils.devdocs.json b/api_docs/kbn_elastic_agent_utils.devdocs.json index 66197d89c54ba..9814a2afd0fa3 100644 --- a/api_docs/kbn_elastic_agent_utils.devdocs.json +++ b/api_docs/kbn_elastic_agent_utils.devdocs.json @@ -431,7 +431,7 @@ "label": "AgentName", "description": [], "signature": [ - "\"java\" | \"ruby\" | \"go\" | \"dotnet\" | \"php\" | \"otlp\" | \"android/java\" | \"iOS/swift\" | \"rum-js\" | \"js-base\" | \"opentelemetry/webjs\" | \"opentelemetry/java\" | \"nodejs\" | \"python\" | \"opentelemetry/cpp\" | \"opentelemetry/dotnet\" | \"opentelemetry/erlang\" | \"opentelemetry/go\" | \"opentelemetry/nodejs\" | \"opentelemetry/php\" | \"opentelemetry/python\" | \"opentelemetry/ruby\" | \"opentelemetry/rust\" | \"opentelemetry/swift\"" + "\"java\" | \"ruby\" | \"go\" | \"dotnet\" | \"php\" | \"otlp\" | \"android/java\" | \"iOS/swift\" | \"rum-js\" | \"js-base\" | \"opentelemetry/webjs\" | \"opentelemetry/java\" | \"nodejs\" | \"python\" | \"opentelemetry/cpp\" | \"opentelemetry/dotnet\" | \"opentelemetry/erlang\" | \"opentelemetry/go\" | \"opentelemetry/nodejs\" | \"opentelemetry/php\" | \"opentelemetry/python\" | \"opentelemetry/ruby\" | \"opentelemetry/rust\" | \"opentelemetry/swift\" | \"opentelemetry/android\"" ], "path": "packages/kbn-elastic-agent-utils/src/agent_names.ts", "deprecated": false, @@ -544,7 +544,7 @@ "label": "OpenTelemetryAgentName", "description": [], "signature": [ - "\"otlp\" | \"opentelemetry/webjs\" | \"opentelemetry/java\" | \"opentelemetry/cpp\" | \"opentelemetry/dotnet\" | \"opentelemetry/erlang\" | \"opentelemetry/go\" | \"opentelemetry/nodejs\" | \"opentelemetry/php\" | \"opentelemetry/python\" | \"opentelemetry/ruby\" | \"opentelemetry/rust\" | \"opentelemetry/swift\"" + "\"otlp\" | \"opentelemetry/webjs\" | \"opentelemetry/java\" | \"opentelemetry/cpp\" | \"opentelemetry/dotnet\" | \"opentelemetry/erlang\" | \"opentelemetry/go\" | \"opentelemetry/nodejs\" | \"opentelemetry/php\" | \"opentelemetry/python\" | \"opentelemetry/ruby\" | \"opentelemetry/rust\" | \"opentelemetry/swift\" | \"opentelemetry/android\"" ], "path": "packages/kbn-elastic-agent-utils/src/agent_names.ts", "deprecated": false, diff --git a/api_docs/kbn_elastic_agent_utils.mdx b/api_docs/kbn_elastic_agent_utils.mdx index c5192b5d7b660..255169e7b3e96 100644 --- a/api_docs/kbn_elastic_agent_utils.mdx +++ b/api_docs/kbn_elastic_agent_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-agent-utils title: "@kbn/elastic-agent-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-agent-utils plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-agent-utils'] --- import kbnElasticAgentUtilsObj from './kbn_elastic_agent_utils.devdocs.json'; diff --git a/api_docs/kbn_elastic_assistant.devdocs.json b/api_docs/kbn_elastic_assistant.devdocs.json index e0d5da4f61278..8b855265a22ca 100644 --- a/api_docs/kbn_elastic_assistant.devdocs.json +++ b/api_docs/kbn_elastic_assistant.devdocs.json @@ -159,7 +159,7 @@ "label": "AssistantProvider", "description": [], "signature": [ - "({ actionTypeRegistry, assistantAvailability, assistantStreamingEnabled, assistantTelemetry, augmentMessageCodeBlocks, baseAllow, baseAllowReplacement, defaultAllow, defaultAllowReplacement, docLinks, basePath, basePromptContexts, baseQuickPrompts, baseSystemPrompts, children, getComments, http, getInitialConversations, modelEvaluatorEnabled, nameSpace, setConversations, setDefaultAllow, setDefaultAllowReplacement, title, }: React.PropsWithChildren<", + "({ actionTypeRegistry, alertsIndexPattern, assistantAvailability, assistantStreamingEnabled, assistantTelemetry, augmentMessageCodeBlocks, baseAllow, baseAllowReplacement, defaultAllow, defaultAllowReplacement, docLinks, basePath, basePromptContexts, baseQuickPrompts, baseSystemPrompts, children, getComments, http, getInitialConversations, modelEvaluatorEnabled, nameSpace, ragOnAlerts, setConversations, setDefaultAllow, setDefaultAllowReplacement, title, toasts, }: React.PropsWithChildren<", "AssistantProviderProps", ">) => JSX.Element" ], @@ -172,7 +172,7 @@ "id": "def-public.AssistantProvider.$1", "type": "CompoundType", "tags": [], - "label": "{\n actionTypeRegistry,\n assistantAvailability,\n assistantStreamingEnabled = false,\n assistantTelemetry,\n augmentMessageCodeBlocks,\n baseAllow,\n baseAllowReplacement,\n defaultAllow,\n defaultAllowReplacement,\n docLinks,\n basePath,\n basePromptContexts = [],\n baseQuickPrompts = [],\n baseSystemPrompts = BASE_SYSTEM_PROMPTS,\n children,\n getComments,\n http,\n getInitialConversations,\n modelEvaluatorEnabled = false,\n nameSpace = DEFAULT_ASSISTANT_NAMESPACE,\n setConversations,\n setDefaultAllow,\n setDefaultAllowReplacement,\n title = DEFAULT_ASSISTANT_TITLE,\n}", + "label": "{\n actionTypeRegistry,\n alertsIndexPattern,\n assistantAvailability,\n assistantStreamingEnabled = false,\n assistantTelemetry,\n augmentMessageCodeBlocks,\n baseAllow,\n baseAllowReplacement,\n defaultAllow,\n defaultAllowReplacement,\n docLinks,\n basePath,\n basePromptContexts = [],\n baseQuickPrompts = [],\n baseSystemPrompts = BASE_SYSTEM_PROMPTS,\n children,\n getComments,\n http,\n getInitialConversations,\n modelEvaluatorEnabled = false,\n nameSpace = DEFAULT_ASSISTANT_NAMESPACE,\n ragOnAlerts = false,\n setConversations,\n setDefaultAllow,\n setDefaultAllowReplacement,\n title = DEFAULT_ASSISTANT_TITLE,\n toasts,\n}", "description": [], "signature": [ "React.PropsWithChildren<", @@ -970,6 +970,20 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "@kbn/elastic-assistant", + "id": "def-public.Message.replacements", + "type": "Object", + "tags": [], + "label": "replacements", + "description": [], + "signature": [ + "Record | undefined" + ], + "path": "x-pack/packages/kbn-elastic-assistant/impl/assistant_context/types.tsx", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "@kbn/elastic-assistant", "id": "def-public.Message.content", diff --git a/api_docs/kbn_elastic_assistant.mdx b/api_docs/kbn_elastic_assistant.mdx index c5a9a4b849562..471b2f2e1d41b 100644 --- a/api_docs/kbn_elastic_assistant.mdx +++ b/api_docs/kbn_elastic_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-elastic-assistant title: "@kbn/elastic-assistant" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/elastic-assistant plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-assistant'] --- import kbnElasticAssistantObj from './kbn_elastic_assistant.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/security-solution](https://github.com/orgs/elastic/teams/secur | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 97 | 0 | 77 | 6 | +| 98 | 0 | 78 | 6 | ## Client diff --git a/api_docs/kbn_elastic_assistant_common.devdocs.json b/api_docs/kbn_elastic_assistant_common.devdocs.json new file mode 100644 index 0000000000000..61136c90e37b6 --- /dev/null +++ b/api_docs/kbn_elastic_assistant_common.devdocs.json @@ -0,0 +1,459 @@ +{ + "id": "@kbn/elastic-assistant-common", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/elastic-assistant-common", + "id": "def-common.getAnonymizedValue", + "type": "Function", + "tags": [], + "label": "getAnonymizedValue", + "description": [], + "signature": [ + "({ currentReplacements, rawValue, }: { currentReplacements: Record | undefined; rawValue: string; }) => string" + ], + "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/get_anonymized_value/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/elastic-assistant-common", + "id": "def-common.getAnonymizedValue.$1", + "type": "Object", + "tags": [], + "label": "{\n currentReplacements,\n rawValue,\n}", + "description": [], + "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/get_anonymized_value/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/elastic-assistant-common", + "id": "def-common.getAnonymizedValue.$1.currentReplacements", + "type": "Object", + "tags": [], + "label": "currentReplacements", + "description": [], + "signature": [ + "Record | undefined" + ], + "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/get_anonymized_value/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/elastic-assistant-common", + "id": "def-common.getAnonymizedValue.$1.rawValue", + "type": "string", + "tags": [], + "label": "rawValue", + "description": [], + "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/get_anonymized_value/index.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/elastic-assistant-common", + "id": "def-common.getIsDataAnonymizable", + "type": "Function", + "tags": [], + "label": "getIsDataAnonymizable", + "description": [], + "signature": [ + "(rawData: string | Record) => boolean" + ], + "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/helpers/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/elastic-assistant-common", + "id": "def-common.getIsDataAnonymizable.$1", + "type": "CompoundType", + "tags": [], + "label": "rawData", + "description": [], + "signature": [ + "string | Record" + ], + "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/helpers/index.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/elastic-assistant-common", + "id": "def-common.isAllowed", + "type": "Function", + "tags": [], + "label": "isAllowed", + "description": [], + "signature": [ + "({ allowSet, field }: { allowSet: Set; field: string; }) => boolean" + ], + "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/helpers/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/elastic-assistant-common", + "id": "def-common.isAllowed.$1", + "type": "Object", + "tags": [], + "label": "{ allowSet, field }", + "description": [], + "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/helpers/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/elastic-assistant-common", + "id": "def-common.isAllowed.$1.allowSet", + "type": "Object", + "tags": [], + "label": "allowSet", + "description": [], + "signature": [ + "Set" + ], + "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/helpers/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/elastic-assistant-common", + "id": "def-common.isAllowed.$1.field", + "type": "string", + "tags": [], + "label": "field", + "description": [], + "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/helpers/index.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/elastic-assistant-common", + "id": "def-common.isAnonymized", + "type": "Function", + "tags": [], + "label": "isAnonymized", + "description": [], + "signature": [ + "({ allowReplacementSet, field, }: { allowReplacementSet: Set; field: string; }) => boolean" + ], + "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/helpers/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/elastic-assistant-common", + "id": "def-common.isAnonymized.$1", + "type": "Object", + "tags": [], + "label": "{\n allowReplacementSet,\n field,\n}", + "description": [], + "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/helpers/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/elastic-assistant-common", + "id": "def-common.isAnonymized.$1.allowReplacementSet", + "type": "Object", + "tags": [], + "label": "allowReplacementSet", + "description": [], + "signature": [ + "Set" + ], + "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/helpers/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/elastic-assistant-common", + "id": "def-common.isAnonymized.$1.field", + "type": "string", + "tags": [], + "label": "field", + "description": [], + "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/helpers/index.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/elastic-assistant-common", + "id": "def-common.isDenied", + "type": "Function", + "tags": [], + "label": "isDenied", + "description": [], + "signature": [ + "({ allowSet, field }: { allowSet: Set; field: string; }) => boolean" + ], + "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/helpers/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/elastic-assistant-common", + "id": "def-common.isDenied.$1", + "type": "Object", + "tags": [], + "label": "{ allowSet, field }", + "description": [], + "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/helpers/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/elastic-assistant-common", + "id": "def-common.isDenied.$1.allowSet", + "type": "Object", + "tags": [], + "label": "allowSet", + "description": [], + "signature": [ + "Set" + ], + "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/helpers/index.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/elastic-assistant-common", + "id": "def-common.isDenied.$1.field", + "type": "string", + "tags": [], + "label": "field", + "description": [], + "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/helpers/index.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/elastic-assistant-common", + "id": "def-common.transformRawData", + "type": "Function", + "tags": [], + "label": "transformRawData", + "description": [], + "signature": [ + "({ allow, allowReplacement, currentReplacements, getAnonymizedValue, onNewReplacements, rawData, }: { allow: string[]; allowReplacement: string[]; currentReplacements: Record | undefined; getAnonymizedValue: ({ currentReplacements, rawValue, }: { currentReplacements: Record | undefined; rawValue: string; }) => string; onNewReplacements?: ((replacements: Record) => void) | undefined; rawData: string | Record; }) => string" + ], + "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/transform_raw_data/index.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/elastic-assistant-common", + "id": "def-common.transformRawData.$1", + "type": "Object", + "tags": [], + "label": "{\n allow,\n allowReplacement,\n currentReplacements,\n getAnonymizedValue,\n onNewReplacements,\n rawData,\n}", + "description": [], + "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/transform_raw_data/index.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/elastic-assistant-common", + "id": "def-common.transformRawData.$1.allow", + "type": "Array", + "tags": [], + "label": "allow", + "description": [], + "signature": [ + "string[]" + ], + "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/transform_raw_data/index.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/elastic-assistant-common", + "id": "def-common.transformRawData.$1.allowReplacement", + "type": "Array", + "tags": [], + "label": "allowReplacement", + "description": [], + "signature": [ + "string[]" + ], + "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/transform_raw_data/index.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/elastic-assistant-common", + "id": "def-common.transformRawData.$1.currentReplacements", + "type": "Object", + "tags": [], + "label": "currentReplacements", + "description": [], + "signature": [ + "Record | undefined" + ], + "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/transform_raw_data/index.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/elastic-assistant-common", + "id": "def-common.transformRawData.$1.getAnonymizedValue", + "type": "Function", + "tags": [], + "label": "getAnonymizedValue", + "description": [], + "signature": [ + "({ currentReplacements, rawValue, }: { currentReplacements: Record | undefined; rawValue: string; }) => string" + ], + "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/transform_raw_data/index.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/elastic-assistant-common", + "id": "def-common.transformRawData.$1.getAnonymizedValue.$1", + "type": "Object", + "tags": [], + "label": "{\n currentReplacements,\n rawValue,\n }", + "description": [], + "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/transform_raw_data/index.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/elastic-assistant-common", + "id": "def-common.transformRawData.$1.getAnonymizedValue.$1.currentReplacements", + "type": "Object", + "tags": [], + "label": "currentReplacements", + "description": [], + "signature": [ + "Record | undefined" + ], + "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/transform_raw_data/index.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/elastic-assistant-common", + "id": "def-common.transformRawData.$1.getAnonymizedValue.$1.rawValue", + "type": "string", + "tags": [], + "label": "rawValue", + "description": [], + "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/transform_raw_data/index.tsx", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/elastic-assistant-common", + "id": "def-common.transformRawData.$1.onNewReplacements", + "type": "Function", + "tags": [], + "label": "onNewReplacements", + "description": [], + "signature": [ + "((replacements: Record) => void) | undefined" + ], + "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/transform_raw_data/index.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/elastic-assistant-common", + "id": "def-common.transformRawData.$1.onNewReplacements.$1", + "type": "Object", + "tags": [], + "label": "replacements", + "description": [], + "signature": [ + "Record" + ], + "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/transform_raw_data/index.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/elastic-assistant-common", + "id": "def-common.transformRawData.$1.rawData", + "type": "CompoundType", + "tags": [], + "label": "rawData", + "description": [], + "signature": [ + "string | Record" + ], + "path": "x-pack/packages/kbn-elastic-assistant-common/impl/data_anonymization/transform_raw_data/index.tsx", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_elastic_assistant_common.mdx b/api_docs/kbn_elastic_assistant_common.mdx new file mode 100644 index 0000000000000..598b1f66f7c66 --- /dev/null +++ b/api_docs/kbn_elastic_assistant_common.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: kibKbnElasticAssistantCommonPluginApi +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: 2023-12-19 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/elastic-assistant-common'] +--- +import kbnElasticAssistantCommonObj from './kbn_elastic_assistant_common.devdocs.json'; + + + +Contact [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 30 | 0 | 30 | 0 | + +## Common + +### Functions + + diff --git a/api_docs/kbn_es.mdx b/api_docs/kbn_es.mdx index b3d931abdc13c..4afb3cd6365b8 100644 --- a/api_docs/kbn_es.mdx +++ b/api_docs/kbn_es.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es title: "@kbn/es" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es plugin -date: 2023-12-06 +date: 2023-12-19 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 1105b63d38238..8161fd4de58c5 100644 --- a/api_docs/kbn_es_archiver.mdx +++ b/api_docs/kbn_es_archiver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-archiver title: "@kbn/es-archiver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-archiver plugin -date: 2023-12-06 +date: 2023-12-19 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 60fbe00eba920..a9d715cdf3374 100644 --- a/api_docs/kbn_es_errors.mdx +++ b/api_docs/kbn_es_errors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-errors title: "@kbn/es-errors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-errors plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-errors'] --- import kbnEsErrorsObj from './kbn_es_errors.devdocs.json'; diff --git a/api_docs/kbn_es_query.devdocs.json b/api_docs/kbn_es_query.devdocs.json index 2ae012946a9ce..60ee58badf7ce 100644 --- a/api_docs/kbn_es_query.devdocs.json +++ b/api_docs/kbn_es_query.devdocs.json @@ -5394,6 +5394,105 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/es-query", + "id": "def-common.ExecutionContextSearch", + "type": "Interface", + "tags": [], + "label": "ExecutionContextSearch", + "description": [], + "path": "packages/kbn-es-query/src/expressions/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/es-query", + "id": "def-common.ExecutionContextSearch.filters", + "type": "Array", + "tags": [], + "label": "filters", + "description": [], + "signature": [ + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[] | undefined" + ], + "path": "packages/kbn-es-query/src/expressions/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/es-query", + "id": "def-common.ExecutionContextSearch.query", + "type": "CompoundType", + "tags": [], + "label": "query", + "description": [], + "signature": [ + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Query", + "text": "Query" + }, + " | ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Query", + "text": "Query" + }, + "[] | undefined" + ], + "path": "packages/kbn-es-query/src/expressions/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/es-query", + "id": "def-common.ExecutionContextSearch.timeRange", + "type": "Object", + "tags": [], + "label": "timeRange", + "description": [], + "signature": [ + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.TimeRange", + "text": "TimeRange" + }, + " | undefined" + ], + "path": "packages/kbn-es-query/src/expressions/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/es-query", + "id": "def-common.ExecutionContextSearch.disableWarningToasts", + "type": "CompoundType", + "tags": [], + "label": "disableWarningToasts", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-es-query/src/expressions/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/es-query", "id": "def-common.FilterCompareOptions", diff --git a/api_docs/kbn_es_query.mdx b/api_docs/kbn_es_query.mdx index 7a16bba0fdc74..bc8a3db90c8ea 100644 --- a/api_docs/kbn_es_query.mdx +++ b/api_docs/kbn_es_query.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-query title: "@kbn/es-query" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-query plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-query'] --- import kbnEsQueryObj from './kbn_es_query.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/k | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 261 | 1 | 201 | 15 | +| 266 | 1 | 206 | 15 | ## Common diff --git a/api_docs/kbn_es_types.mdx b/api_docs/kbn_es_types.mdx index 10848b0c5a95f..d9654bacd4b93 100644 --- a/api_docs/kbn_es_types.mdx +++ b/api_docs/kbn_es_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-es-types title: "@kbn/es-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/es-types plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/es-types'] --- import kbnEsTypesObj from './kbn_es_types.devdocs.json'; diff --git a/api_docs/kbn_eslint_plugin_imports.mdx b/api_docs/kbn_eslint_plugin_imports.mdx index 0ec2f9a16e53a..643a6c7091f59 100644 --- a/api_docs/kbn_eslint_plugin_imports.mdx +++ b/api_docs/kbn_eslint_plugin_imports.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-eslint-plugin-imports title: "@kbn/eslint-plugin-imports" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/eslint-plugin-imports plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/eslint-plugin-imports'] --- import kbnEslintPluginImportsObj from './kbn_eslint_plugin_imports.devdocs.json'; diff --git a/api_docs/kbn_event_annotation_common.mdx b/api_docs/kbn_event_annotation_common.mdx index 607707da0611d..56e39d86973cd 100644 --- a/api_docs/kbn_event_annotation_common.mdx +++ b/api_docs/kbn_event_annotation_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-event-annotation-common title: "@kbn/event-annotation-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/event-annotation-common plugin -date: 2023-12-06 +date: 2023-12-19 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 a85d588fc531a..4563355191be4 100644 --- a/api_docs/kbn_event_annotation_components.mdx +++ b/api_docs/kbn_event_annotation_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-event-annotation-components title: "@kbn/event-annotation-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/event-annotation-components plugin -date: 2023-12-06 +date: 2023-12-19 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 77c61336b9c8b..e74d01f85b3db 100644 --- a/api_docs/kbn_expandable_flyout.mdx +++ b/api_docs/kbn_expandable_flyout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-expandable-flyout title: "@kbn/expandable-flyout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/expandable-flyout plugin -date: 2023-12-06 +date: 2023-12-19 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 cfbe2477e7332..094eb66a2d12c 100644 --- a/api_docs/kbn_field_types.mdx +++ b/api_docs/kbn_field_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-field-types title: "@kbn/field-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/field-types plugin -date: 2023-12-06 +date: 2023-12-19 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 51bd26fcff794..99bcb0504e83a 100644 --- a/api_docs/kbn_field_utils.mdx +++ b/api_docs/kbn_field_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-field-utils title: "@kbn/field-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/field-utils plugin -date: 2023-12-06 +date: 2023-12-19 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 66f50048c137d..750922c91ce4c 100644 --- a/api_docs/kbn_find_used_node_modules.mdx +++ b/api_docs/kbn_find_used_node_modules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-find-used-node-modules title: "@kbn/find-used-node-modules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/find-used-node-modules plugin -date: 2023-12-06 +date: 2023-12-19 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 d9a99b30c002a..ab3dbe081ea1c 100644 --- a/api_docs/kbn_ftr_common_functional_services.mdx +++ b/api_docs/kbn_ftr_common_functional_services.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ftr-common-functional-services title: "@kbn/ftr-common-functional-services" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ftr-common-functional-services plugin -date: 2023-12-06 +date: 2023-12-19 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.devdocs.json b/api_docs/kbn_ftr_common_functional_ui_services.devdocs.json new file mode 100644 index 0000000000000..79a57d84974d8 --- /dev/null +++ b/api_docs/kbn_ftr_common_functional_ui_services.devdocs.json @@ -0,0 +1,143 @@ +{ + "id": "@kbn/ftr-common-functional-ui-services", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/ftr-common-functional-ui-services", + "id": "def-common.FtrProviderContext", + "type": "Type", + "tags": [], + "label": "FtrProviderContext", + "description": [], + "signature": [ + { + "pluginId": "@kbn/test", + "scope": "common", + "docId": "kibKbnTestPluginApi", + "section": "def-common.GenericFtrProviderContext", + "text": "GenericFtrProviderContext" + }, + "<{ retryOnStale: ({ getService }: ", + { + "pluginId": "@kbn/ftr-common-functional-ui-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalUiServicesPluginApi", + "section": "def-common.FtrProviderContext", + "text": "FtrProviderContext" + }, + ") => { (fn: () => Promise): Promise; wrap(fn: (...args: Args) => Promise): (...args: Args) => Promise; }; }, {}, ProvidedTypeMap<{ retryOnStale: ({ getService }: ", + { + "pluginId": "@kbn/ftr-common-functional-ui-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalUiServicesPluginApi", + "section": "def-common.FtrProviderContext", + "text": "FtrProviderContext" + }, + ") => { (fn: () => Promise): Promise; wrap(fn: (...args: Args) => Promise): (...args: Args) => Promise; }; }>, ProvidedTypeMap<{}>>" + ], + "path": "packages/kbn-ftr-common-functional-ui-services/services/ftr_provider_context.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [ + { + "parentPluginId": "@kbn/ftr-common-functional-ui-services", + "id": "def-common.services", + "type": "Object", + "tags": [], + "label": "services", + "description": [], + "path": "packages/kbn-ftr-common-functional-ui-services/services/all.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/ftr-common-functional-ui-services", + "id": "def-common.services.retryOnStale", + "type": "Function", + "tags": [], + "label": "retryOnStale", + "description": [], + "signature": [ + "({ getService }: ", + { + "pluginId": "@kbn/ftr-common-functional-ui-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalUiServicesPluginApi", + "section": "def-common.FtrProviderContext", + "text": "FtrProviderContext" + }, + ") => { (fn: () => Promise): Promise; wrap(fn: (...args: Args) => Promise): (...args: Args) => Promise; }" + ], + "path": "packages/kbn-ftr-common-functional-ui-services/services/all.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "@kbn/ftr-common-functional-ui-services", + "id": "def-common.services.retryOnStale.$1", + "type": "Object", + "tags": [], + "label": "__0", + "description": [], + "signature": [ + { + "pluginId": "@kbn/test", + "scope": "common", + "docId": "kibKbnTestPluginApi", + "section": "def-common.GenericFtrProviderContext", + "text": "GenericFtrProviderContext" + }, + "<{ retryOnStale: ({ getService }: ", + { + "pluginId": "@kbn/ftr-common-functional-ui-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalUiServicesPluginApi", + "section": "def-common.FtrProviderContext", + "text": "FtrProviderContext" + }, + ") => { (fn: () => Promise): Promise; wrap(fn: (...args: Args) => Promise): (...args: Args) => Promise; }; }, {}, ProvidedTypeMap<{ retryOnStale: ({ getService }: ", + { + "pluginId": "@kbn/ftr-common-functional-ui-services", + "scope": "common", + "docId": "kibKbnFtrCommonFunctionalUiServicesPluginApi", + "section": "def-common.FtrProviderContext", + "text": "FtrProviderContext" + }, + ") => { (fn: () => Promise): Promise; wrap(fn: (...args: Args) => Promise): (...args: Args) => Promise; }; }>, ProvidedTypeMap<{}>>" + ], + "path": "packages/kbn-ftr-common-functional-ui-services/services/retry_on_stale.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "initialIsOpen": false + } + ] + } +} \ No newline at end of file diff --git a/api_docs/kbn_ftr_common_functional_ui_services.mdx b/api_docs/kbn_ftr_common_functional_ui_services.mdx new file mode 100644 index 0000000000000..ddd5dd02a4b45 --- /dev/null +++ b/api_docs/kbn_ftr_common_functional_ui_services.mdx @@ -0,0 +1,33 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnFtrCommonFunctionalUiServicesPluginApi +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: 2023-12-19 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ftr-common-functional-ui-services'] +--- +import kbnFtrCommonFunctionalUiServicesObj from './kbn_ftr_common_functional_ui_services.devdocs.json'; + + + +Contact [@elastic/appex-qa](https://github.com/orgs/elastic/teams/appex-qa) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 4 | 0 | 4 | 0 | + +## Common + +### Objects + + +### Consts, variables and types + + diff --git a/api_docs/kbn_generate.mdx b/api_docs/kbn_generate.mdx index b235635e152a3..a6ddea7c9f3f5 100644 --- a/api_docs/kbn_generate.mdx +++ b/api_docs/kbn_generate.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate title: "@kbn/generate" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate plugin -date: 2023-12-06 +date: 2023-12-19 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 9923a9648641a..707af0569dc8c 100644 --- a/api_docs/kbn_generate_console_definitions.mdx +++ b/api_docs/kbn_generate_console_definitions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-console-definitions title: "@kbn/generate-console-definitions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-console-definitions plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-console-definitions'] --- import kbnGenerateConsoleDefinitionsObj from './kbn_generate_console_definitions.devdocs.json'; diff --git a/api_docs/kbn_generate_csv.mdx b/api_docs/kbn_generate_csv.mdx index 10d98dc458223..1f6d69bf32d87 100644 --- a/api_docs/kbn_generate_csv.mdx +++ b/api_docs/kbn_generate_csv.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-generate-csv title: "@kbn/generate-csv" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/generate-csv plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/generate-csv'] --- import kbnGenerateCsvObj from './kbn_generate_csv.devdocs.json'; diff --git a/api_docs/kbn_guided_onboarding.mdx b/api_docs/kbn_guided_onboarding.mdx index d455ed35fcd8c..5c56eb1d7bcee 100644 --- a/api_docs/kbn_guided_onboarding.mdx +++ b/api_docs/kbn_guided_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-guided-onboarding title: "@kbn/guided-onboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/guided-onboarding plugin -date: 2023-12-06 +date: 2023-12-19 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 58507c7ae78fd..0dfb572f2a3eb 100644 --- a/api_docs/kbn_handlebars.mdx +++ b/api_docs/kbn_handlebars.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-handlebars title: "@kbn/handlebars" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/handlebars plugin -date: 2023-12-06 +date: 2023-12-19 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 9a9e28c6be19e..1af65e34b65df 100644 --- a/api_docs/kbn_hapi_mocks.mdx +++ b/api_docs/kbn_hapi_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-hapi-mocks title: "@kbn/hapi-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/hapi-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 8a409375c7574..8eec5c23269fc 100644 --- a/api_docs/kbn_health_gateway_server.mdx +++ b/api_docs/kbn_health_gateway_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-health-gateway-server title: "@kbn/health-gateway-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/health-gateway-server plugin -date: 2023-12-06 +date: 2023-12-19 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 8c01bd91e00b2..de0cfb3103d9d 100644 --- a/api_docs/kbn_home_sample_data_card.mdx +++ b/api_docs/kbn_home_sample_data_card.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-card title: "@kbn/home-sample-data-card" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-card plugin -date: 2023-12-06 +date: 2023-12-19 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 c3032d3696953..cb585056dac1f 100644 --- a/api_docs/kbn_home_sample_data_tab.mdx +++ b/api_docs/kbn_home_sample_data_tab.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-home-sample-data-tab title: "@kbn/home-sample-data-tab" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/home-sample-data-tab plugin -date: 2023-12-06 +date: 2023-12-19 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 0f084b08e4b12..27a1db78d7022 100644 --- a/api_docs/kbn_i18n.mdx +++ b/api_docs/kbn_i18n.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n title: "@kbn/i18n" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n plugin -date: 2023-12-06 +date: 2023-12-19 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 563ca5456858c..8bf4b62c49c27 100644 --- a/api_docs/kbn_i18n_react.mdx +++ b/api_docs/kbn_i18n_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-i18n-react title: "@kbn/i18n-react" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/i18n-react plugin -date: 2023-12-06 +date: 2023-12-19 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 1f9429f181a5e..f3a990652d5e4 100644 --- a/api_docs/kbn_import_resolver.mdx +++ b/api_docs/kbn_import_resolver.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-import-resolver title: "@kbn/import-resolver" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/import-resolver plugin -date: 2023-12-06 +date: 2023-12-19 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 7deecaf572e35..32748299fda9c 100644 --- a/api_docs/kbn_infra_forge.mdx +++ b/api_docs/kbn_infra_forge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-infra-forge title: "@kbn/infra-forge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/infra-forge plugin -date: 2023-12-06 +date: 2023-12-19 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 21a1140054461..879922e915389 100644 --- a/api_docs/kbn_interpreter.mdx +++ b/api_docs/kbn_interpreter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-interpreter title: "@kbn/interpreter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/interpreter plugin -date: 2023-12-06 +date: 2023-12-19 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 63926835c6a1d..a84c759b48ac6 100644 --- a/api_docs/kbn_io_ts_utils.mdx +++ b/api_docs/kbn_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-io-ts-utils title: "@kbn/io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/io-ts-utils plugin -date: 2023-12-06 +date: 2023-12-19 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 e54269fe5f369..5974f9bac8231 100644 --- a/api_docs/kbn_jest_serializers.mdx +++ b/api_docs/kbn_jest_serializers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-jest-serializers title: "@kbn/jest-serializers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/jest-serializers plugin -date: 2023-12-06 +date: 2023-12-19 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 db6edfb40d24e..a26e26c22622a 100644 --- a/api_docs/kbn_journeys.mdx +++ b/api_docs/kbn_journeys.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-journeys title: "@kbn/journeys" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/journeys plugin -date: 2023-12-06 +date: 2023-12-19 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 88314c7bdf245..d42b61c393e20 100644 --- a/api_docs/kbn_json_ast.mdx +++ b/api_docs/kbn_json_ast.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-json-ast title: "@kbn/json-ast" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/json-ast plugin -date: 2023-12-06 +date: 2023-12-19 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 6527ab3fb2ff9..cc4940d02ea80 100644 --- a/api_docs/kbn_kibana_manifest_schema.mdx +++ b/api_docs/kbn_kibana_manifest_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-kibana-manifest-schema title: "@kbn/kibana-manifest-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/kibana-manifest-schema plugin -date: 2023-12-06 +date: 2023-12-19 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 417c0b4cc0d94..61d980daa5ab2 100644 --- a/api_docs/kbn_language_documentation_popover.mdx +++ b/api_docs/kbn_language_documentation_popover.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-language-documentation-popover title: "@kbn/language-documentation-popover" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/language-documentation-popover plugin -date: 2023-12-06 +date: 2023-12-19 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 b246a370cb081..cd34b3605e894 100644 --- a/api_docs/kbn_lens_embeddable_utils.mdx +++ b/api_docs/kbn_lens_embeddable_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-lens-embeddable-utils title: "@kbn/lens-embeddable-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/lens-embeddable-utils plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/lens-embeddable-utils'] --- import kbnLensEmbeddableUtilsObj from './kbn_lens_embeddable_utils.devdocs.json'; diff --git a/api_docs/kbn_logging.mdx b/api_docs/kbn_logging.mdx index 569ecac3545a8..4d39edf82167b 100644 --- a/api_docs/kbn_logging.mdx +++ b/api_docs/kbn_logging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging title: "@kbn/logging" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging plugin -date: 2023-12-06 +date: 2023-12-19 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 0a6c2e1127eb0..9e59b4eaa4544 100644 --- a/api_docs/kbn_logging_mocks.mdx +++ b/api_docs/kbn_logging_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-logging-mocks title: "@kbn/logging-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/logging-mocks plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/logging-mocks'] --- import kbnLoggingMocksObj from './kbn_logging_mocks.devdocs.json'; diff --git a/api_docs/kbn_managed_vscode_config.mdx b/api_docs/kbn_managed_vscode_config.mdx index de702c06e673b..a415806e2b624 100644 --- a/api_docs/kbn_managed_vscode_config.mdx +++ b/api_docs/kbn_managed_vscode_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-managed-vscode-config title: "@kbn/managed-vscode-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/managed-vscode-config plugin -date: 2023-12-06 +date: 2023-12-19 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 1b1f634d51a4f..8ff85097e9b62 100644 --- a/api_docs/kbn_management_cards_navigation.mdx +++ b/api_docs/kbn_management_cards_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-cards-navigation title: "@kbn/management-cards-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-cards-navigation plugin -date: 2023-12-06 +date: 2023-12-19 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 629470fdf81e5..60ac8fdc3af92 100644 --- a/api_docs/kbn_management_settings_application.mdx +++ b/api_docs/kbn_management_settings_application.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-application title: "@kbn/management-settings-application" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-application plugin -date: 2023-12-06 +date: 2023-12-19 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 dacbff8b9bffc..e2ef4ba774813 100644 --- a/api_docs/kbn_management_settings_components_field_category.mdx +++ b/api_docs/kbn_management_settings_components_field_category.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-category title: "@kbn/management-settings-components-field-category" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-category plugin -date: 2023-12-06 +date: 2023-12-19 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 022808c37e0a8..9cbeb04b5d450 100644 --- a/api_docs/kbn_management_settings_components_field_input.mdx +++ b/api_docs/kbn_management_settings_components_field_input.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-input title: "@kbn/management-settings-components-field-input" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-input plugin -date: 2023-12-06 +date: 2023-12-19 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 6f8b36f2a2cb0..a8359188b8cb3 100644 --- a/api_docs/kbn_management_settings_components_field_row.mdx +++ b/api_docs/kbn_management_settings_components_field_row.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-field-row title: "@kbn/management-settings-components-field-row" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-field-row plugin -date: 2023-12-06 +date: 2023-12-19 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 5f084177eb05f..e911f3598e4a3 100644 --- a/api_docs/kbn_management_settings_components_form.mdx +++ b/api_docs/kbn_management_settings_components_form.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-components-form title: "@kbn/management-settings-components-form" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-components-form plugin -date: 2023-12-06 +date: 2023-12-19 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 9056f8f0388e9..224c22d8ba304 100644 --- a/api_docs/kbn_management_settings_field_definition.mdx +++ b/api_docs/kbn_management_settings_field_definition.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-field-definition title: "@kbn/management-settings-field-definition" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-field-definition plugin -date: 2023-12-06 +date: 2023-12-19 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 295602bc30032..79f89734866b6 100644 --- a/api_docs/kbn_management_settings_ids.mdx +++ b/api_docs/kbn_management_settings_ids.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-ids title: "@kbn/management-settings-ids" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-ids plugin -date: 2023-12-06 +date: 2023-12-19 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 f2d9579db11f8..516894009b711 100644 --- a/api_docs/kbn_management_settings_section_registry.mdx +++ b/api_docs/kbn_management_settings_section_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-section-registry title: "@kbn/management-settings-section-registry" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-section-registry plugin -date: 2023-12-06 +date: 2023-12-19 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 c196246928dd3..a790b4a305bef 100644 --- a/api_docs/kbn_management_settings_types.mdx +++ b/api_docs/kbn_management_settings_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-types title: "@kbn/management-settings-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-types plugin -date: 2023-12-06 +date: 2023-12-19 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 b350aa5a253f3..f5e4a2148d822 100644 --- a/api_docs/kbn_management_settings_utilities.mdx +++ b/api_docs/kbn_management_settings_utilities.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-settings-utilities title: "@kbn/management-settings-utilities" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-settings-utilities plugin -date: 2023-12-06 +date: 2023-12-19 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 5e3d524ca77c8..0c4d38b67b5ea 100644 --- a/api_docs/kbn_management_storybook_config.mdx +++ b/api_docs/kbn_management_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-management-storybook-config title: "@kbn/management-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/management-storybook-config plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/management-storybook-config'] --- import kbnManagementStorybookConfigObj from './kbn_management_storybook_config.devdocs.json'; diff --git a/api_docs/kbn_mapbox_gl.devdocs.json b/api_docs/kbn_mapbox_gl.devdocs.json index 838e1f65579ce..b3517dd00c74b 100644 --- a/api_docs/kbn_mapbox_gl.devdocs.json +++ b/api_docs/kbn_mapbox_gl.devdocs.json @@ -9592,7 +9592,7 @@ "label": "MapEvent", "description": [], "signature": [ - "\"error\" | \"remove\" | \"data\" | \"render\" | \"rotate\" | \"resize\" | \"zoom\" | \"load\" | \"move\" | \"idle\" | \"mousedown\" | \"mouseup\" | \"mouseover\" | \"mousemove\" | \"click\" | \"dblclick\" | \"mouseenter\" | \"mouseleave\" | \"mouseout\" | \"contextmenu\" | \"wheel\" | \"touchstart\" | \"touchend\" | \"touchmove\" | \"touchcancel\" | \"movestart\" | \"moveend\" | \"dragstart\" | \"drag\" | \"dragend\" | \"zoomstart\" | \"zoomend\" | \"rotatestart\" | \"rotateend\" | \"pitchstart\" | \"pitch\" | \"pitchend\" | \"boxzoomstart\" | \"boxzoomend\" | \"boxzoomcancel\" | \"webglcontextlost\" | \"webglcontextrestored\" | \"styledata\" | \"sourcedata\" | \"dataloading\" | \"styledataloading\" | \"sourcedataloading\" | \"styleimagemissing\" | \"style.load\" | \"terrain\" | \"dataabort\" | \"sourcedataabort\"" + "\"error\" | \"remove\" | \"data\" | \"render\" | \"rotate\" | \"resize\" | \"idle\" | \"zoom\" | \"load\" | \"move\" | \"mousedown\" | \"mouseup\" | \"mouseover\" | \"mousemove\" | \"click\" | \"dblclick\" | \"mouseenter\" | \"mouseleave\" | \"mouseout\" | \"contextmenu\" | \"wheel\" | \"touchstart\" | \"touchend\" | \"touchmove\" | \"touchcancel\" | \"movestart\" | \"moveend\" | \"dragstart\" | \"drag\" | \"dragend\" | \"zoomstart\" | \"zoomend\" | \"rotatestart\" | \"rotateend\" | \"pitchstart\" | \"pitch\" | \"pitchend\" | \"boxzoomstart\" | \"boxzoomend\" | \"boxzoomcancel\" | \"webglcontextlost\" | \"webglcontextrestored\" | \"styledata\" | \"sourcedata\" | \"dataloading\" | \"styledataloading\" | \"sourcedataloading\" | \"styleimagemissing\" | \"style.load\" | \"terrain\" | \"dataabort\" | \"sourcedataabort\"" ], "path": "node_modules/maplibre-gl/dist/maplibre-gl.d.ts", "deprecated": false, diff --git a/api_docs/kbn_mapbox_gl.mdx b/api_docs/kbn_mapbox_gl.mdx index 8df9159e606df..d2a931b730d5e 100644 --- a/api_docs/kbn_mapbox_gl.mdx +++ b/api_docs/kbn_mapbox_gl.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-mapbox-gl title: "@kbn/mapbox-gl" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/mapbox-gl plugin -date: 2023-12-06 +date: 2023-12-19 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 32dba348a6dc7..953b186f4766d 100644 --- a/api_docs/kbn_maps_vector_tile_utils.mdx +++ b/api_docs/kbn_maps_vector_tile_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-maps-vector-tile-utils title: "@kbn/maps-vector-tile-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/maps-vector-tile-utils plugin -date: 2023-12-06 +date: 2023-12-19 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 94d2d5f96b435..f44fe6ad4d1aa 100644 --- a/api_docs/kbn_ml_agg_utils.mdx +++ b/api_docs/kbn_ml_agg_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-agg-utils title: "@kbn/ml-agg-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-agg-utils plugin -date: 2023-12-06 +date: 2023-12-19 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 c9989a723995e..059033e032f94 100644 --- a/api_docs/kbn_ml_anomaly_utils.mdx +++ b/api_docs/kbn_ml_anomaly_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-anomaly-utils title: "@kbn/ml-anomaly-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-anomaly-utils plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-anomaly-utils'] --- import kbnMlAnomalyUtilsObj from './kbn_ml_anomaly_utils.devdocs.json'; diff --git a/api_docs/kbn_ml_category_validator.mdx b/api_docs/kbn_ml_category_validator.mdx index 599108b3a6a47..60892c79dc3c1 100644 --- a/api_docs/kbn_ml_category_validator.mdx +++ b/api_docs/kbn_ml_category_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-category-validator title: "@kbn/ml-category-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-category-validator plugin -date: 2023-12-06 +date: 2023-12-19 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 3626455766411..a13bbfd0ef990 100644 --- a/api_docs/kbn_ml_chi2test.mdx +++ b/api_docs/kbn_ml_chi2test.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-chi2test title: "@kbn/ml-chi2test" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-chi2test plugin -date: 2023-12-06 +date: 2023-12-19 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 35615577a1b53..92d9facafe82d 100644 --- a/api_docs/kbn_ml_data_frame_analytics_utils.mdx +++ b/api_docs/kbn_ml_data_frame_analytics_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-data-frame-analytics-utils title: "@kbn/ml-data-frame-analytics-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-data-frame-analytics-utils plugin -date: 2023-12-06 +date: 2023-12-19 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 0f553e6dc5890..43d8172c0b13d 100644 --- a/api_docs/kbn_ml_data_grid.mdx +++ b/api_docs/kbn_ml_data_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-data-grid title: "@kbn/ml-data-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-data-grid plugin -date: 2023-12-06 +date: 2023-12-19 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 f0271376a2c51..d88c6395d4c16 100644 --- a/api_docs/kbn_ml_date_picker.mdx +++ b/api_docs/kbn_ml_date_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-picker title: "@kbn/ml-date-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-picker plugin -date: 2023-12-06 +date: 2023-12-19 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 e7f552f209df0..efea2c91a3da7 100644 --- a/api_docs/kbn_ml_date_utils.mdx +++ b/api_docs/kbn_ml_date_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-date-utils title: "@kbn/ml-date-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-date-utils plugin -date: 2023-12-06 +date: 2023-12-19 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 f5699159196c1..ea604aa86091d 100644 --- a/api_docs/kbn_ml_error_utils.mdx +++ b/api_docs/kbn_ml_error_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-error-utils title: "@kbn/ml-error-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-error-utils plugin -date: 2023-12-06 +date: 2023-12-19 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 104d279c2973e..7a1a2aaa9cb15 100644 --- a/api_docs/kbn_ml_in_memory_table.mdx +++ b/api_docs/kbn_ml_in_memory_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-in-memory-table title: "@kbn/ml-in-memory-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-in-memory-table plugin -date: 2023-12-06 +date: 2023-12-19 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 114cb70ecf0cf..7ac72b537a8b3 100644 --- a/api_docs/kbn_ml_is_defined.mdx +++ b/api_docs/kbn_ml_is_defined.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-defined title: "@kbn/ml-is-defined" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-defined plugin -date: 2023-12-06 +date: 2023-12-19 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 c6d7efc40755b..5921482756730 100644 --- a/api_docs/kbn_ml_is_populated_object.mdx +++ b/api_docs/kbn_ml_is_populated_object.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-is-populated-object title: "@kbn/ml-is-populated-object" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-is-populated-object plugin -date: 2023-12-06 +date: 2023-12-19 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 3d1b321f8a744..52b2bb6e96424 100644 --- a/api_docs/kbn_ml_kibana_theme.mdx +++ b/api_docs/kbn_ml_kibana_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-kibana-theme title: "@kbn/ml-kibana-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-kibana-theme plugin -date: 2023-12-06 +date: 2023-12-19 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 81784d60b2cc7..eb27f2dea70fa 100644 --- a/api_docs/kbn_ml_local_storage.mdx +++ b/api_docs/kbn_ml_local_storage.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-local-storage title: "@kbn/ml-local-storage" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-local-storage plugin -date: 2023-12-06 +date: 2023-12-19 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 5964131ccd30c..01f2bdfb887b7 100644 --- a/api_docs/kbn_ml_nested_property.mdx +++ b/api_docs/kbn_ml_nested_property.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-nested-property title: "@kbn/ml-nested-property" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-nested-property plugin -date: 2023-12-06 +date: 2023-12-19 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 620237f3797eb..ce90be4172809 100644 --- a/api_docs/kbn_ml_number_utils.mdx +++ b/api_docs/kbn_ml_number_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-number-utils title: "@kbn/ml-number-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-number-utils plugin -date: 2023-12-06 +date: 2023-12-19 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 00afc0f576c4c..0ef6cbb135388 100644 --- a/api_docs/kbn_ml_query_utils.mdx +++ b/api_docs/kbn_ml_query_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-query-utils title: "@kbn/ml-query-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-query-utils plugin -date: 2023-12-06 +date: 2023-12-19 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 5f8c886ebc307..eeb354c220342 100644 --- a/api_docs/kbn_ml_random_sampler_utils.mdx +++ b/api_docs/kbn_ml_random_sampler_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-random-sampler-utils title: "@kbn/ml-random-sampler-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-random-sampler-utils plugin -date: 2023-12-06 +date: 2023-12-19 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 9ddb57988c8a5..bdc87845ab126 100644 --- a/api_docs/kbn_ml_route_utils.mdx +++ b/api_docs/kbn_ml_route_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-route-utils title: "@kbn/ml-route-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-route-utils plugin -date: 2023-12-06 +date: 2023-12-19 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 6f44576089296..1374b48496a2f 100644 --- a/api_docs/kbn_ml_runtime_field_utils.mdx +++ b/api_docs/kbn_ml_runtime_field_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-runtime-field-utils title: "@kbn/ml-runtime-field-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-runtime-field-utils plugin -date: 2023-12-06 +date: 2023-12-19 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 82e18313c7c03..0db3b18eabec3 100644 --- a/api_docs/kbn_ml_string_hash.mdx +++ b/api_docs/kbn_ml_string_hash.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-string-hash title: "@kbn/ml-string-hash" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-string-hash plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-string-hash'] --- import kbnMlStringHashObj from './kbn_ml_string_hash.devdocs.json'; diff --git a/api_docs/kbn_ml_trained_models_utils.devdocs.json b/api_docs/kbn_ml_trained_models_utils.devdocs.json index f0cfb869a14f1..6a08397fd948d 100644 --- a/api_docs/kbn_ml_trained_models_utils.devdocs.json +++ b/api_docs/kbn_ml_trained_models_utils.devdocs.json @@ -168,7 +168,9 @@ "type": "CompoundType", "tags": [], "label": "recommended", - "description": [], + "description": [ + "Indicates if model version is recommended for deployment based on the cluster configuration" + ], "signature": [ "boolean | undefined" ], @@ -196,7 +198,25 @@ "type": "string", "tags": [], "label": "license", - "description": [], + "description": [ + "Software license of a model, e.g. MIT" + ], + "signature": [ + "string | undefined" + ], + "path": "x-pack/packages/ml/trained_models_utils/src/constants/trained_models.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/ml-trained-models-utils", + "id": "def-common.ModelDefinition.licenseUrl", + "type": "string", + "tags": [], + "label": "licenseUrl", + "description": [ + "Link to the external license/documentation page" + ], "signature": [ "string | undefined" ], diff --git a/api_docs/kbn_ml_trained_models_utils.mdx b/api_docs/kbn_ml_trained_models_utils.mdx index f5a253ff34d6d..687ecbb5086cb 100644 --- a/api_docs/kbn_ml_trained_models_utils.mdx +++ b/api_docs/kbn_ml_trained_models_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-trained-models-utils title: "@kbn/ml-trained-models-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-trained-models-utils plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-trained-models-utils'] --- import kbnMlTrainedModelsUtilsObj from './kbn_ml_trained_models_utils.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) for questi | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 32 | 0 | 30 | 0 | +| 33 | 0 | 28 | 0 | ## Common diff --git a/api_docs/kbn_ml_ui_actions.mdx b/api_docs/kbn_ml_ui_actions.mdx index 3ab9e935785c4..07fa3f0f692f4 100644 --- a/api_docs/kbn_ml_ui_actions.mdx +++ b/api_docs/kbn_ml_ui_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-ui-actions title: "@kbn/ml-ui-actions" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-ui-actions plugin -date: 2023-12-06 +date: 2023-12-19 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 8274ea596bfa3..8758bc9d405cf 100644 --- a/api_docs/kbn_ml_url_state.mdx +++ b/api_docs/kbn_ml_url_state.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ml-url-state title: "@kbn/ml-url-state" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ml-url-state plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ml-url-state'] --- import kbnMlUrlStateObj from './kbn_ml_url_state.devdocs.json'; diff --git a/api_docs/kbn_monaco.devdocs.json b/api_docs/kbn_monaco.devdocs.json index f15a94ffd5541..ced93d066699f 100644 --- a/api_docs/kbn_monaco.devdocs.json +++ b/api_docs/kbn_monaco.devdocs.json @@ -43,7 +43,7 @@ "section": "def-common.CustomLangModuleType", "text": "CustomLangModuleType" }, - ") => void" + ") => void" ], "path": "packages/kbn-monaco/src/helpers.ts", "deprecated": false, @@ -71,7 +71,8 @@ "docId": "kibKbnMonacoPluginApi", "section": "def-common.CustomLangModuleType", "text": "CustomLangModuleType" - } + }, + "" ], "path": "packages/kbn-monaco/src/helpers.ts", "deprecated": false, @@ -253,14 +254,23 @@ "section": "def-common.CustomLangModuleType", "text": "CustomLangModuleType" }, - " extends ", + " extends Omit<", { "pluginId": "@kbn/monaco", "scope": "common", "docId": "kibKbnMonacoPluginApi", "section": "def-common.LangModuleType", "text": "LangModuleType" - } + }, + ", \"getSuggestionProvider\">,", + { + "pluginId": "@kbn/monaco", + "scope": "common", + "docId": "kibKbnMonacoPluginApi", + "section": "def-common.LanguageProvidersModule", + "text": "LanguageProvidersModule" + }, + "" ], "path": "packages/kbn-monaco/src/types.ts", "deprecated": false, @@ -296,6 +306,20 @@ "deprecated": false, "trackAdoption": false, "children": [ + { + "parentPluginId": "@kbn/monaco", + "id": "def-common.EditorError.severity", + "type": "Enum", + "tags": [], + "label": "severity", + "description": [], + "signature": [ + "MarkerSeverity" + ], + "path": "packages/kbn-monaco/src/types.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "@kbn/monaco", "id": "def-common.EditorError.startLineNumber", @@ -356,82 +380,82 @@ }, { "parentPluginId": "@kbn/monaco", - "id": "def-common.ESQLCustomAutocompleteCallbacks", + "id": "def-common.ESQLCallbacks", "type": "Interface", "tags": [], - "label": "ESQLCustomAutocompleteCallbacks", + "label": "ESQLCallbacks", "description": [], - "path": "packages/kbn-monaco/src/esql/lib/autocomplete/types.ts", + "path": "packages/kbn-monaco/src/esql/lib/ast/shared/types.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "@kbn/monaco", - "id": "def-common.ESQLCustomAutocompleteCallbacks.getSourceIdentifiers", + "id": "def-common.ESQLCallbacks.getSources", "type": "Function", "tags": [], - "label": "getSourceIdentifiers", + "label": "getSources", "description": [], "signature": [ - "CallbackFn | undefined" + "CallbackFn<{}, { name: string; hidden: boolean; }> | undefined" ], - "path": "packages/kbn-monaco/src/esql/lib/autocomplete/types.ts", + "path": "packages/kbn-monaco/src/esql/lib/ast/shared/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/monaco", - "id": "def-common.ESQLCustomAutocompleteCallbacks.getFieldsIdentifiers", + "id": "def-common.ESQLCallbacks.getFieldsFor", "type": "Function", "tags": [], - "label": "getFieldsIdentifiers", + "label": "getFieldsFor", "description": [], "signature": [ - "CallbackFn | undefined" + "CallbackFn<{ query: string; }, { name: string; type: string; }> | undefined" ], - "path": "packages/kbn-monaco/src/esql/lib/autocomplete/types.ts", + "path": "packages/kbn-monaco/src/esql/lib/ast/shared/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/monaco", - "id": "def-common.ESQLCustomAutocompleteCallbacks.getPoliciesIdentifiers", + "id": "def-common.ESQLCallbacks.getPolicies", "type": "Function", "tags": [], - "label": "getPoliciesIdentifiers", + "label": "getPolicies", "description": [], "signature": [ - "CallbackFn<{ name: string; indices: string[]; }> | undefined" + "CallbackFn<{}, { name: string; sourceIndices: string[]; matchField: string; enrichFields: string[]; }> | undefined" ], - "path": "packages/kbn-monaco/src/esql/lib/autocomplete/types.ts", + "path": "packages/kbn-monaco/src/esql/lib/ast/shared/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/monaco", - "id": "def-common.ESQLCustomAutocompleteCallbacks.getPolicyFieldsIdentifiers", + "id": "def-common.ESQLCallbacks.getPolicyFields", "type": "Function", "tags": [], - "label": "getPolicyFieldsIdentifiers", + "label": "getPolicyFields", "description": [], "signature": [ - "CallbackFn | undefined" + "CallbackFn<{}, string> | undefined" ], - "path": "packages/kbn-monaco/src/esql/lib/autocomplete/types.ts", + "path": "packages/kbn-monaco/src/esql/lib/ast/shared/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/monaco", - "id": "def-common.ESQLCustomAutocompleteCallbacks.getPolicyMatchingFieldIdentifiers", + "id": "def-common.ESQLCallbacks.getPolicyMatchingField", "type": "Function", "tags": [], - "label": "getPolicyMatchingFieldIdentifiers", + "label": "getPolicyMatchingField", "description": [], "signature": [ - "CallbackFn | undefined" + "CallbackFn<{}, string> | undefined" ], - "path": "packages/kbn-monaco/src/esql/lib/autocomplete/types.ts", + "path": "packages/kbn-monaco/src/esql/lib/ast/shared/types.ts", "deprecated": false, "trackAdoption": false } @@ -507,6 +531,201 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/monaco", + "id": "def-common.LanguageProvidersModule", + "type": "Interface", + "tags": [], + "label": "LanguageProvidersModule", + "description": [], + "signature": [ + { + "pluginId": "@kbn/monaco", + "scope": "common", + "docId": "kibKbnMonacoPluginApi", + "section": "def-common.LanguageProvidersModule", + "text": "LanguageProvidersModule" + }, + "" + ], + "path": "packages/kbn-monaco/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/monaco", + "id": "def-common.LanguageProvidersModule.validate", + "type": "Function", + "tags": [], + "label": "validate", + "description": [], + "signature": [ + "(model: ", + "editor", + ".ITextModel, code: string, callbacks?: Deps | undefined) => Promise<{ errors: ", + "editor", + ".IMarkerData[]; warnings: ", + "editor", + ".IMarkerData[]; }>" + ], + "path": "packages/kbn-monaco/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/monaco", + "id": "def-common.LanguageProvidersModule.validate.$1", + "type": "Object", + "tags": [], + "label": "model", + "description": [], + "signature": [ + "editor", + ".ITextModel" + ], + "path": "packages/kbn-monaco/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/monaco", + "id": "def-common.LanguageProvidersModule.validate.$2", + "type": "string", + "tags": [], + "label": "code", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-monaco/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/monaco", + "id": "def-common.LanguageProvidersModule.validate.$3", + "type": "Uncategorized", + "tags": [], + "label": "callbacks", + "description": [], + "signature": [ + "Deps | undefined" + ], + "path": "packages/kbn-monaco/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/monaco", + "id": "def-common.LanguageProvidersModule.getSuggestionProvider", + "type": "Function", + "tags": [], + "label": "getSuggestionProvider", + "description": [], + "signature": [ + "(callbacks?: Deps | undefined) => ", + "languages", + ".CompletionItemProvider" + ], + "path": "packages/kbn-monaco/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/monaco", + "id": "def-common.LanguageProvidersModule.getSuggestionProvider.$1", + "type": "Uncategorized", + "tags": [], + "label": "callbacks", + "description": [], + "signature": [ + "Deps | undefined" + ], + "path": "packages/kbn-monaco/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/monaco", + "id": "def-common.LanguageProvidersModule.getSignatureProvider", + "type": "Function", + "tags": [], + "label": "getSignatureProvider", + "description": [], + "signature": [ + "((callbacks?: Deps | undefined) => ", + "languages", + ".SignatureHelpProvider) | undefined" + ], + "path": "packages/kbn-monaco/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/monaco", + "id": "def-common.LanguageProvidersModule.getSignatureProvider.$1", + "type": "Uncategorized", + "tags": [], + "label": "callbacks", + "description": [], + "signature": [ + "Deps | undefined" + ], + "path": "packages/kbn-monaco/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/monaco", + "id": "def-common.LanguageProvidersModule.getHoverProvider", + "type": "Function", + "tags": [], + "label": "getHoverProvider", + "description": [], + "signature": [ + "((callbacks?: Deps | undefined) => ", + "languages", + ".HoverProvider) | undefined" + ], + "path": "packages/kbn-monaco/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/monaco", + "id": "def-common.LanguageProvidersModule.getHoverProvider.$1", + "type": "Uncategorized", + "tags": [], + "label": "callbacks", + "description": [], + "signature": [ + "Deps | undefined" + ], + "path": "packages/kbn-monaco/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/monaco", "id": "def-common.LangValidation", @@ -947,6 +1166,200 @@ } ] }, + { + "parentPluginId": "@kbn/monaco", + "id": "def-common.ESQLLang.validate", + "type": "Function", + "tags": [], + "label": "validate", + "description": [], + "signature": [ + "(model: ", + "editor", + ".ITextModel, code: string, callbacks?: ", + { + "pluginId": "@kbn/monaco", + "scope": "common", + "docId": "kibKbnMonacoPluginApi", + "section": "def-common.ESQLCallbacks", + "text": "ESQLCallbacks" + }, + " | undefined) => Promise<{ errors: ", + { + "pluginId": "@kbn/monaco", + "scope": "common", + "docId": "kibKbnMonacoPluginApi", + "section": "def-common.EditorError", + "text": "EditorError" + }, + "[]; warnings: ", + { + "pluginId": "@kbn/monaco", + "scope": "common", + "docId": "kibKbnMonacoPluginApi", + "section": "def-common.EditorError", + "text": "EditorError" + }, + "[]; }>" + ], + "path": "packages/kbn-monaco/src/esql/language.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/monaco", + "id": "def-common.ESQLLang.validate.$1", + "type": "Object", + "tags": [], + "label": "model", + "description": [], + "signature": [ + "editor", + ".ITextModel" + ], + "path": "packages/kbn-monaco/src/esql/language.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/monaco", + "id": "def-common.ESQLLang.validate.$2", + "type": "string", + "tags": [], + "label": "code", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-monaco/src/esql/language.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/monaco", + "id": "def-common.ESQLLang.validate.$3", + "type": "Object", + "tags": [], + "label": "callbacks", + "description": [], + "signature": [ + { + "pluginId": "@kbn/monaco", + "scope": "common", + "docId": "kibKbnMonacoPluginApi", + "section": "def-common.ESQLCallbacks", + "text": "ESQLCallbacks" + }, + " | undefined" + ], + "path": "packages/kbn-monaco/src/esql/language.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/monaco", + "id": "def-common.ESQLLang.getSignatureProvider", + "type": "Function", + "tags": [], + "label": "getSignatureProvider", + "description": [], + "signature": [ + "(callbacks?: ", + { + "pluginId": "@kbn/monaco", + "scope": "common", + "docId": "kibKbnMonacoPluginApi", + "section": "def-common.ESQLCallbacks", + "text": "ESQLCallbacks" + }, + " | undefined) => ", + "languages", + ".SignatureHelpProvider" + ], + "path": "packages/kbn-monaco/src/esql/language.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/monaco", + "id": "def-common.ESQLLang.getSignatureProvider.$1", + "type": "Object", + "tags": [], + "label": "callbacks", + "description": [], + "signature": [ + { + "pluginId": "@kbn/monaco", + "scope": "common", + "docId": "kibKbnMonacoPluginApi", + "section": "def-common.ESQLCallbacks", + "text": "ESQLCallbacks" + }, + " | undefined" + ], + "path": "packages/kbn-monaco/src/esql/language.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/monaco", + "id": "def-common.ESQLLang.getHoverProvider", + "type": "Function", + "tags": [], + "label": "getHoverProvider", + "description": [], + "signature": [ + "(callbacks?: ", + { + "pluginId": "@kbn/monaco", + "scope": "common", + "docId": "kibKbnMonacoPluginApi", + "section": "def-common.ESQLCallbacks", + "text": "ESQLCallbacks" + }, + " | undefined) => ", + "languages", + ".HoverProvider" + ], + "path": "packages/kbn-monaco/src/esql/language.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/monaco", + "id": "def-common.ESQLLang.getHoverProvider.$1", + "type": "Object", + "tags": [], + "label": "callbacks", + "description": [], + "signature": [ + { + "pluginId": "@kbn/monaco", + "scope": "common", + "docId": "kibKbnMonacoPluginApi", + "section": "def-common.ESQLCallbacks", + "text": "ESQLCallbacks" + }, + " | undefined" + ], + "path": "packages/kbn-monaco/src/esql/language.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + }, { "parentPluginId": "@kbn/monaco", "id": "def-common.ESQLLang.getSuggestionProvider", @@ -960,11 +1373,12 @@ "pluginId": "@kbn/monaco", "scope": "common", "docId": "kibKbnMonacoPluginApi", - "section": "def-common.ESQLCustomAutocompleteCallbacks", - "text": "ESQLCustomAutocompleteCallbacks" + "section": "def-common.ESQLCallbacks", + "text": "ESQLCallbacks" }, " | undefined) => ", - "ESQLCompletionAdapter" + "languages", + ".CompletionItemProvider" ], "path": "packages/kbn-monaco/src/esql/language.ts", "deprecated": false, @@ -982,8 +1396,8 @@ "pluginId": "@kbn/monaco", "scope": "common", "docId": "kibKbnMonacoPluginApi", - "section": "def-common.ESQLCustomAutocompleteCallbacks", - "text": "ESQLCustomAutocompleteCallbacks" + "section": "def-common.ESQLCallbacks", + "text": "ESQLCallbacks" }, " | undefined" ], diff --git a/api_docs/kbn_monaco.mdx b/api_docs/kbn_monaco.mdx index 1bacd69685bec..097f6158a271a 100644 --- a/api_docs/kbn_monaco.mdx +++ b/api_docs/kbn_monaco.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-monaco title: "@kbn/monaco" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/monaco plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/monaco'] --- import kbnMonacoObj from './kbn_monaco.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 | |-------------------|-----------|------------------------|-----------------| -| 78 | 0 | 76 | 3 | +| 98 | 0 | 96 | 2 | ## Common diff --git a/api_docs/kbn_object_versioning.mdx b/api_docs/kbn_object_versioning.mdx index 7c496eb0f1da5..889b89f58b713 100644 --- a/api_docs/kbn_object_versioning.mdx +++ b/api_docs/kbn_object_versioning.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-object-versioning title: "@kbn/object-versioning" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/object-versioning plugin -date: 2023-12-06 +date: 2023-12-19 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 a144e56d5d425..5267c6518f50c 100644 --- a/api_docs/kbn_observability_alert_details.mdx +++ b/api_docs/kbn_observability_alert_details.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alert-details title: "@kbn/observability-alert-details" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alert-details plugin -date: 2023-12-06 +date: 2023-12-19 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 a7d649ca97d61..3534a0032efe7 100644 --- a/api_docs/kbn_observability_alerting_test_data.mdx +++ b/api_docs/kbn_observability_alerting_test_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-observability-alerting-test-data title: "@kbn/observability-alerting-test-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/observability-alerting-test-data plugin -date: 2023-12-06 +date: 2023-12-19 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 234916630b450..18baac91738b2 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: 2023-12-06 +date: 2023-12-19 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 f311c93319993..1fe4c05337fc8 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: 2023-12-06 +date: 2023-12-19 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 19e9796495a2b..e91eb491c8099 100644 --- a/api_docs/kbn_openapi_generator.mdx +++ b/api_docs/kbn_openapi_generator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-openapi-generator title: "@kbn/openapi-generator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/openapi-generator plugin -date: 2023-12-06 +date: 2023-12-19 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 e305b2393987b..e03fda66b922f 100644 --- a/api_docs/kbn_optimizer.mdx +++ b/api_docs/kbn_optimizer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer title: "@kbn/optimizer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer plugin -date: 2023-12-06 +date: 2023-12-19 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 2af4c4eb061cf..a4d93490f23a5 100644 --- a/api_docs/kbn_optimizer_webpack_helpers.mdx +++ b/api_docs/kbn_optimizer_webpack_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-optimizer-webpack-helpers title: "@kbn/optimizer-webpack-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/optimizer-webpack-helpers plugin -date: 2023-12-06 +date: 2023-12-19 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 fb344e1e64422..0fc96187e5d1e 100644 --- a/api_docs/kbn_osquery_io_ts_types.mdx +++ b/api_docs/kbn_osquery_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-osquery-io-ts-types title: "@kbn/osquery-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/osquery-io-ts-types plugin -date: 2023-12-06 +date: 2023-12-19 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 71658fa40b4c4..95758f643a33d 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: 2023-12-06 +date: 2023-12-19 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 43a166b020c3b..e485d9ce30585 100644 --- a/api_docs/kbn_performance_testing_dataset_extractor.mdx +++ b/api_docs/kbn_performance_testing_dataset_extractor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-performance-testing-dataset-extractor title: "@kbn/performance-testing-dataset-extractor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/performance-testing-dataset-extractor plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/performance-testing-dataset-extractor'] --- import kbnPerformanceTestingDatasetExtractorObj from './kbn_performance_testing_dataset_extractor.devdocs.json'; diff --git a/api_docs/kbn_plugin_generator.mdx b/api_docs/kbn_plugin_generator.mdx index fa880a54ec12b..8e1fc68be9bf3 100644 --- a/api_docs/kbn_plugin_generator.mdx +++ b/api_docs/kbn_plugin_generator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-generator title: "@kbn/plugin-generator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-generator plugin -date: 2023-12-06 +date: 2023-12-19 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 37932047ce1a6..d4868ebec7e74 100644 --- a/api_docs/kbn_plugin_helpers.mdx +++ b/api_docs/kbn_plugin_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-plugin-helpers title: "@kbn/plugin-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/plugin-helpers plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/plugin-helpers'] --- import kbnPluginHelpersObj from './kbn_plugin_helpers.devdocs.json'; diff --git a/api_docs/kbn_profiling_utils.devdocs.json b/api_docs/kbn_profiling_utils.devdocs.json index e07794efc29ba..6026ec7c4382e 100644 --- a/api_docs/kbn_profiling_utils.devdocs.json +++ b/api_docs/kbn_profiling_utils.devdocs.json @@ -1276,50 +1276,6 @@ "path": "packages/kbn-profiling-utils/common/flamegraph.ts", "deprecated": false, "trackAdoption": false - }, - { - "parentPluginId": "@kbn/profiling-utils", - "id": "def-common.BaseFlameGraph.SelfAnnualCO2Tons", - "type": "number", - "tags": [], - "label": "SelfAnnualCO2Tons", - "description": [], - "path": "packages/kbn-profiling-utils/common/flamegraph.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/profiling-utils", - "id": "def-common.BaseFlameGraph.TotalAnnualCO2Tons", - "type": "number", - "tags": [], - "label": "TotalAnnualCO2Tons", - "description": [], - "path": "packages/kbn-profiling-utils/common/flamegraph.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/profiling-utils", - "id": "def-common.BaseFlameGraph.SelfAnnualCostsUSD", - "type": "number", - "tags": [], - "label": "SelfAnnualCostsUSD", - "description": [], - "path": "packages/kbn-profiling-utils/common/flamegraph.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/profiling-utils", - "id": "def-common.BaseFlameGraph.TotalAnnualCostsUSD", - "type": "number", - "tags": [], - "label": "TotalAnnualCostsUSD", - "description": [], - "path": "packages/kbn-profiling-utils/common/flamegraph.ts", - "deprecated": false, - "trackAdoption": false } ], "initialIsOpen": false @@ -1572,28 +1528,6 @@ "path": "packages/kbn-profiling-utils/common/flamegraph.ts", "deprecated": false, "trackAdoption": false - }, - { - "parentPluginId": "@kbn/profiling-utils", - "id": "def-common.ElasticFlameGraph.SelfAnnualCO2Kgs", - "type": "number", - "tags": [], - "label": "SelfAnnualCO2Kgs", - "description": [], - "path": "packages/kbn-profiling-utils/common/flamegraph.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/profiling-utils", - "id": "def-common.ElasticFlameGraph.TotalAnnualCO2Kgs", - "type": "number", - "tags": [], - "label": "TotalAnnualCO2Kgs", - "description": [], - "path": "packages/kbn-profiling-utils/common/flamegraph.ts", - "deprecated": false, - "trackAdoption": false } ], "initialIsOpen": false diff --git a/api_docs/kbn_profiling_utils.mdx b/api_docs/kbn_profiling_utils.mdx index a9a3c2b42a7e5..75843c6fe0ce5 100644 --- a/api_docs/kbn_profiling_utils.mdx +++ b/api_docs/kbn_profiling_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-profiling-utils title: "@kbn/profiling-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/profiling-utils plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/profiling-utils'] --- import kbnProfilingUtilsObj from './kbn_profiling_utils.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/te | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 169 | 0 | 51 | 0 | +| 163 | 0 | 45 | 0 | ## Common diff --git a/api_docs/kbn_random_sampling.mdx b/api_docs/kbn_random_sampling.mdx index 23c1b2f8fe5dd..235821608a0d2 100644 --- a/api_docs/kbn_random_sampling.mdx +++ b/api_docs/kbn_random_sampling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-random-sampling title: "@kbn/random-sampling" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/random-sampling plugin -date: 2023-12-06 +date: 2023-12-19 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 575e46d3876d1..ecf8bf46eaac3 100644 --- a/api_docs/kbn_react_field.mdx +++ b/api_docs/kbn_react_field.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-field title: "@kbn/react-field" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-field plugin -date: 2023-12-06 +date: 2023-12-19 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 0ee4bc75b7ca1..938283f794947 100644 --- a/api_docs/kbn_react_kibana_context_common.mdx +++ b/api_docs/kbn_react_kibana_context_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-common title: "@kbn/react-kibana-context-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-common plugin -date: 2023-12-06 +date: 2023-12-19 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 a940e2c6ae5b6..961a2c2a84c4a 100644 --- a/api_docs/kbn_react_kibana_context_render.mdx +++ b/api_docs/kbn_react_kibana_context_render.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-render title: "@kbn/react-kibana-context-render" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-render plugin -date: 2023-12-06 +date: 2023-12-19 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 58ab11c8c1aaf..c25271e33c22b 100644 --- a/api_docs/kbn_react_kibana_context_root.mdx +++ b/api_docs/kbn_react_kibana_context_root.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-root title: "@kbn/react-kibana-context-root" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-root plugin -date: 2023-12-06 +date: 2023-12-19 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 5a4ff328e101e..dde8a04b2e3e8 100644 --- a/api_docs/kbn_react_kibana_context_styled.mdx +++ b/api_docs/kbn_react_kibana_context_styled.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-styled title: "@kbn/react-kibana-context-styled" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-styled plugin -date: 2023-12-06 +date: 2023-12-19 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 5f8c7b6013132..d2cd397f9af52 100644 --- a/api_docs/kbn_react_kibana_context_theme.mdx +++ b/api_docs/kbn_react_kibana_context_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-context-theme title: "@kbn/react-kibana-context-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-context-theme plugin -date: 2023-12-06 +date: 2023-12-19 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 6a18539c11112..0b90295b71a37 100644 --- a/api_docs/kbn_react_kibana_mount.mdx +++ b/api_docs/kbn_react_kibana_mount.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-react-kibana-mount title: "@kbn/react-kibana-mount" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/react-kibana-mount plugin -date: 2023-12-06 +date: 2023-12-19 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 4345e76e8afac..00009a0d0ad8a 100644 --- a/api_docs/kbn_repo_file_maps.mdx +++ b/api_docs/kbn_repo_file_maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-file-maps title: "@kbn/repo-file-maps" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-file-maps plugin -date: 2023-12-06 +date: 2023-12-19 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 43ad35f4b1c6c..2ff0f304b2fe7 100644 --- a/api_docs/kbn_repo_linter.mdx +++ b/api_docs/kbn_repo_linter.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-linter title: "@kbn/repo-linter" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-linter plugin -date: 2023-12-06 +date: 2023-12-19 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 5a83db86a7165..82ec79a9e1db6 100644 --- a/api_docs/kbn_repo_path.mdx +++ b/api_docs/kbn_repo_path.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-path title: "@kbn/repo-path" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-path plugin -date: 2023-12-06 +date: 2023-12-19 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 7da9101e0b4d5..6fb8c317359bc 100644 --- a/api_docs/kbn_repo_source_classifier.mdx +++ b/api_docs/kbn_repo_source_classifier.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-repo-source-classifier title: "@kbn/repo-source-classifier" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/repo-source-classifier plugin -date: 2023-12-06 +date: 2023-12-19 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 064fe89454af4..562b5c4ef320a 100644 --- a/api_docs/kbn_reporting_common.mdx +++ b/api_docs/kbn_reporting_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-common title: "@kbn/reporting-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-common plugin -date: 2023-12-06 +date: 2023-12-19 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 e15e4333b7af4..9e40723964917 100644 --- a/api_docs/kbn_reporting_export_types_csv.mdx +++ b/api_docs/kbn_reporting_export_types_csv.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-csv title: "@kbn/reporting-export-types-csv" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-csv plugin -date: 2023-12-06 +date: 2023-12-19 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.devdocs.json b/api_docs/kbn_reporting_export_types_csv_common.devdocs.json index 4e6ff0c91e22c..b8e38bafbb458 100644 --- a/api_docs/kbn_reporting_export_types_csv_common.devdocs.json +++ b/api_docs/kbn_reporting_export_types_csv_common.devdocs.json @@ -18,7 +18,94 @@ }, "common": { "classes": [], - "functions": [], + "functions": [ + { + "parentPluginId": "@kbn/reporting-export-types-csv-common", + "id": "def-common.getQueryFromCsvJob", + "type": "Function", + "tags": [], + "label": "getQueryFromCsvJob", + "description": [ + "\nA utility to get the query from a CSV reporting job to inspect or analyze" + ], + "signature": [ + "(searchSource: ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataSearchPluginApi", + "section": "def-common.ISearchSource", + "text": "ISearchSource" + }, + ", { scroll: config }: CsvConfigType, pitId?: string | undefined) => ", + { + "pluginId": "@kbn/reporting-export-types-csv-common", + "scope": "common", + "docId": "kibKbnReportingExportTypesCsvCommonPluginApi", + "section": "def-common.QueryInspection", + "text": "QueryInspection" + } + ], + "path": "packages/kbn-reporting/export_types/csv_common/lib/get_query_from_job.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/reporting-export-types-csv-common", + "id": "def-common.getQueryFromCsvJob.$1", + "type": "Object", + "tags": [], + "label": "searchSource", + "description": [], + "signature": [ + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataSearchPluginApi", + "section": "def-common.ISearchSource", + "text": "ISearchSource" + } + ], + "path": "packages/kbn-reporting/export_types/csv_common/lib/get_query_from_job.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/reporting-export-types-csv-common", + "id": "def-common.getQueryFromCsvJob.$2", + "type": "Object", + "tags": [], + "label": "{ scroll: config }", + "description": [], + "signature": [ + "CsvConfigType" + ], + "path": "packages/kbn-reporting/export_types/csv_common/lib/get_query_from_job.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/reporting-export-types-csv-common", + "id": "def-common.getQueryFromCsvJob.$3", + "type": "string", + "tags": [], + "label": "pitId", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-reporting/export_types/csv_common/lib/get_query_from_job.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], "interfaces": [ { "parentPluginId": "@kbn/reporting-export-types-csv-common", @@ -161,6 +248,36 @@ } ], "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/reporting-export-types-csv-common", + "id": "def-common.QueryInspection", + "type": "Interface", + "tags": [], + "label": "QueryInspection", + "description": [ + "\nType to wrap the untyped object returned when\ngetting the query from SearchSource service" + ], + "path": "packages/kbn-reporting/export_types/csv_common/lib/get_query_from_job.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/reporting-export-types-csv-common", + "id": "def-common.QueryInspection.requestBody", + "type": "Object", + "tags": [], + "label": "requestBody", + "description": [], + "signature": [ + "SearchRequest" + ], + "path": "packages/kbn-reporting/export_types/csv_common/lib/get_query_from_job.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false } ], "enums": [], diff --git a/api_docs/kbn_reporting_export_types_csv_common.mdx b/api_docs/kbn_reporting_export_types_csv_common.mdx index 711a8a6ee756a..8257679d30537 100644 --- a/api_docs/kbn_reporting_export_types_csv_common.mdx +++ b/api_docs/kbn_reporting_export_types_csv_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-csv-common title: "@kbn/reporting-export-types-csv-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-csv-common plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-export-types-csv-common'] --- import kbnReportingExportTypesCsvCommonObj from './kbn_reporting_export_types_csv_common.devdocs.json'; @@ -21,10 +21,13 @@ Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sh | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 17 | 0 | 16 | 0 | +| 23 | 0 | 20 | 0 | ## Common +### Functions + + ### Interfaces diff --git a/api_docs/kbn_reporting_export_types_pdf.mdx b/api_docs/kbn_reporting_export_types_pdf.mdx index 97c4aa3b59cc3..f8513ca012ff7 100644 --- a/api_docs/kbn_reporting_export_types_pdf.mdx +++ b/api_docs/kbn_reporting_export_types_pdf.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-pdf title: "@kbn/reporting-export-types-pdf" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-pdf plugin -date: 2023-12-06 +date: 2023-12-19 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 ac70e46fa81bd..5ff8178af6b2c 100644 --- a/api_docs/kbn_reporting_export_types_pdf_common.mdx +++ b/api_docs/kbn_reporting_export_types_pdf_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-pdf-common title: "@kbn/reporting-export-types-pdf-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-pdf-common plugin -date: 2023-12-06 +date: 2023-12-19 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 cd3d83654cdd4..7be5c17cb9668 100644 --- a/api_docs/kbn_reporting_export_types_png.mdx +++ b/api_docs/kbn_reporting_export_types_png.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-png title: "@kbn/reporting-export-types-png" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-png plugin -date: 2023-12-06 +date: 2023-12-19 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 f600c6294d23b..4d149b32aed94 100644 --- a/api_docs/kbn_reporting_export_types_png_common.mdx +++ b/api_docs/kbn_reporting_export_types_png_common.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-export-types-png-common title: "@kbn/reporting-export-types-png-common" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-export-types-png-common plugin -date: 2023-12-06 +date: 2023-12-19 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 c75e4401b50c9..21d70be5c4e41 100644 --- a/api_docs/kbn_reporting_mocks_server.mdx +++ b/api_docs/kbn_reporting_mocks_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-mocks-server title: "@kbn/reporting-mocks-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-mocks-server plugin -date: 2023-12-06 +date: 2023-12-19 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.devdocs.json b/api_docs/kbn_reporting_public.devdocs.json index 5acb381f4e2a3..c41f423ba40c9 100644 --- a/api_docs/kbn_reporting_public.devdocs.json +++ b/api_docs/kbn_reporting_public.devdocs.json @@ -15,6 +15,20 @@ "deprecated": false, "trackAdoption": false, "children": [ + { + "parentPluginId": "@kbn/reporting-public", + "id": "def-public.ClientConfigType.csv", + "type": "Object", + "tags": [], + "label": "csv", + "description": [], + "signature": [ + "{ scroll: { duration: string; size: number; }; }" + ], + "path": "packages/kbn-reporting/public/types.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "@kbn/reporting-public", "id": "def-public.ClientConfigType.poll", diff --git a/api_docs/kbn_reporting_public.mdx b/api_docs/kbn_reporting_public.mdx index ded37cb4226c2..a3378f717c3ca 100644 --- a/api_docs/kbn_reporting_public.mdx +++ b/api_docs/kbn_reporting_public.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-public title: "@kbn/reporting-public" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-public plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/reporting-public'] --- import kbnReportingPublicObj from './kbn_reporting_public.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 | |-------------------|-----------|------------------------|-----------------| -| 5 | 0 | 5 | 0 | +| 6 | 0 | 6 | 0 | ## Client diff --git a/api_docs/kbn_reporting_server.mdx b/api_docs/kbn_reporting_server.mdx index b78fd5dcacd8b..ab219bdc011fd 100644 --- a/api_docs/kbn_reporting_server.mdx +++ b/api_docs/kbn_reporting_server.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-reporting-server title: "@kbn/reporting-server" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/reporting-server plugin -date: 2023-12-06 +date: 2023-12-19 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 2dc3fb5964f35..e4769ec720f4b 100644 --- a/api_docs/kbn_resizable_layout.mdx +++ b/api_docs/kbn_resizable_layout.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-resizable-layout title: "@kbn/resizable-layout" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/resizable-layout plugin -date: 2023-12-06 +date: 2023-12-19 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 e1a7f6be5ea32..25d97621f0756 100644 --- a/api_docs/kbn_rison.mdx +++ b/api_docs/kbn_rison.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rison title: "@kbn/rison" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rison plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rison'] --- import kbnRisonObj from './kbn_rison.devdocs.json'; diff --git a/api_docs/kbn_router_utils.devdocs.json b/api_docs/kbn_router_utils.devdocs.json new file mode 100644 index 0000000000000..a0649c6bc1ea3 --- /dev/null +++ b/api_docs/kbn_router_utils.devdocs.json @@ -0,0 +1,65 @@ +{ + "id": "@kbn/router-utils", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/router-utils", + "id": "def-common.getRouterLinkProps", + "type": "Function", + "tags": [], + "label": "getRouterLinkProps", + "description": [ + "\n\ngetRouterLinkProps is an util that enable HTML elements, such buttons, to\nbehave as links." + ], + "signature": [ + "({ href, onClick }: GetRouterLinkPropsDeps) => { href: string | undefined; onClick: (event: React.MouseEvent) => void; }" + ], + "path": "packages/kbn-router-utils/src/get_router_link_props/index.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/router-utils", + "id": "def-common.getRouterLinkProps.$1", + "type": "Object", + "tags": [], + "label": "{ href, onClick }", + "description": [], + "signature": [ + "GetRouterLinkPropsDeps" + ], + "path": "packages/kbn-router-utils/src/get_router_link_props/index.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [ + "An object that contains an href and a guardedClick handler that will\nmanage behaviours such as leftClickEvent and event with modifiers (Ctrl, Shift, etc)" + ], + "initialIsOpen": false + } + ], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_router_utils.mdx b/api_docs/kbn_router_utils.mdx new file mode 100644 index 0000000000000..eca44a8cf7c24 --- /dev/null +++ b/api_docs/kbn_router_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: kibKbnRouterUtilsPluginApi +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: 2023-12-19 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/router-utils'] +--- +import kbnRouterUtilsObj from './kbn_router_utils.devdocs.json'; + + + +Contact [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 2 | 0 | 1 | 0 | + +## Common + +### Functions + + diff --git a/api_docs/kbn_rrule.mdx b/api_docs/kbn_rrule.mdx index e5b4e5f0d5f64..31ebc7aa11bdd 100644 --- a/api_docs/kbn_rrule.mdx +++ b/api_docs/kbn_rrule.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rrule title: "@kbn/rrule" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rrule plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/rrule'] --- import kbnRruleObj from './kbn_rrule.devdocs.json'; diff --git a/api_docs/kbn_rule_data_utils.devdocs.json b/api_docs/kbn_rule_data_utils.devdocs.json index 70c4b1fb1862d..2dee374f7c572 100644 --- a/api_docs/kbn_rule_data_utils.devdocs.json +++ b/api_docs/kbn_rule_data_utils.devdocs.json @@ -1459,7 +1459,7 @@ "label": "AlertConsumers", "description": [], "signature": [ - "\"ml\" | \"uptime\" | \"siem\" | \"observability\" | \"apm\" | \"logs\" | \"infrastructure\" | \"slo\"" + "\"ml\" | \"uptime\" | \"siem\" | \"observability\" | \"stackAlerts\" | \"apm\" | \"logs\" | \"infrastructure\" | \"slo\"" ], "path": "packages/kbn-rule-data-utils/src/alerts_as_data_rbac.ts", "deprecated": false, @@ -1789,7 +1789,7 @@ "label": "ValidFeatureId", "description": [], "signature": [ - "\"ml\" | \"uptime\" | \"siem\" | \"observability\" | \"apm\" | \"logs\" | \"infrastructure\" | \"slo\"" + "\"ml\" | \"uptime\" | \"siem\" | \"observability\" | \"stackAlerts\" | \"apm\" | \"logs\" | \"infrastructure\" | \"slo\"" ], "path": "packages/kbn-rule-data-utils/src/alerts_as_data_rbac.ts", "deprecated": false, @@ -1838,7 +1838,7 @@ "\nregistering a new instance of the rule data client\nin a new plugin will require updating the below data structure\nto include the index name where the alerts as data will be written to." ], "signature": [ - "{ readonly APM: \"apm\"; readonly LOGS: \"logs\"; readonly INFRASTRUCTURE: \"infrastructure\"; readonly OBSERVABILITY: \"observability\"; readonly SLO: \"slo\"; readonly SIEM: \"siem\"; readonly UPTIME: \"uptime\"; readonly ML: \"ml\"; }" + "{ readonly APM: \"apm\"; readonly LOGS: \"logs\"; readonly INFRASTRUCTURE: \"infrastructure\"; readonly OBSERVABILITY: \"observability\"; readonly SLO: \"slo\"; readonly SIEM: \"siem\"; readonly UPTIME: \"uptime\"; readonly ML: \"ml\"; readonly STACK_ALERTS: \"stackAlerts\"; }" ], "path": "packages/kbn-rule-data-utils/src/alerts_as_data_rbac.ts", "deprecated": false, diff --git a/api_docs/kbn_rule_data_utils.mdx b/api_docs/kbn_rule_data_utils.mdx index e299c8ffaf9fe..5bab0397cb28a 100644 --- a/api_docs/kbn_rule_data_utils.mdx +++ b/api_docs/kbn_rule_data_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-rule-data-utils title: "@kbn/rule-data-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/rule-data-utils plugin -date: 2023-12-06 +date: 2023-12-19 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 70c0939504f5b..d9b21510b3ab5 100644 --- a/api_docs/kbn_saved_objects_settings.mdx +++ b/api_docs/kbn_saved_objects_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-saved-objects-settings title: "@kbn/saved-objects-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/saved-objects-settings plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/saved-objects-settings'] --- import kbnSavedObjectsSettingsObj from './kbn_saved_objects_settings.devdocs.json'; diff --git a/api_docs/kbn_search_api_panels.devdocs.json b/api_docs/kbn_search_api_panels.devdocs.json index 8013e6c49470b..a55ccad88b62e 100644 --- a/api_docs/kbn_search_api_panels.devdocs.json +++ b/api_docs/kbn_search_api_panels.devdocs.json @@ -19,6 +19,53 @@ "common": { "classes": [], "functions": [ + { + "parentPluginId": "@kbn/search-api-panels", + "id": "def-common.CloudDetailsPanel", + "type": "Function", + "tags": [], + "label": "CloudDetailsPanel", + "description": [], + "signature": [ + "({ cloudId, elasticsearchUrl, isPanelLeft, overviewPanelProps, }: ", + { + "pluginId": "@kbn/search-api-panels", + "scope": "common", + "docId": "kibKbnSearchApiPanelsPluginApi", + "section": "def-common.CloudDetailsPanelProps", + "text": "CloudDetailsPanelProps" + }, + ") => JSX.Element" + ], + "path": "packages/kbn-search-api-panels/components/cloud_details.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-api-panels", + "id": "def-common.CloudDetailsPanel.$1", + "type": "Object", + "tags": [], + "label": "{\n cloudId,\n elasticsearchUrl = ELASTICSEARCH_URL_PLACEHOLDER,\n isPanelLeft = true,\n overviewPanelProps,\n}", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-api-panels", + "scope": "common", + "docId": "kibKbnSearchApiPanelsPluginApi", + "section": "def-common.CloudDetailsPanelProps", + "text": "CloudDetailsPanelProps" + } + ], + "path": "packages/kbn-search-api-panels/components/cloud_details.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/search-api-panels", "id": "def-common.CodeBox", @@ -436,6 +483,39 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/search-api-panels", + "id": "def-common.PipelinePanel", + "type": "Function", + "tags": [], + "label": "PipelinePanel", + "description": [], + "signature": [ + "({ clusterImage, cutImage, reporterImage, }: React.PropsWithChildren) => JSX.Element" + ], + "path": "packages/kbn-search-api-panels/components/pipeline_panel.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-api-panels", + "id": "def-common.PipelinePanel.$1", + "type": "CompoundType", + "tags": [], + "label": "{\n clusterImage,\n cutImage,\n reporterImage,\n}", + "description": [], + "signature": [ + "React.PropsWithChildren" + ], + "path": "packages/kbn-search-api-panels/components/pipeline_panel.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/search-api-panels", "id": "def-common.SelectClientPanel", @@ -583,6 +663,92 @@ } ], "interfaces": [ + { + "parentPluginId": "@kbn/search-api-panels", + "id": "def-common.CloudDetailsPanelProps", + "type": "Interface", + "tags": [], + "label": "CloudDetailsPanelProps", + "description": [], + "path": "packages/kbn-search-api-panels/components/cloud_details.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-api-panels", + "id": "def-common.CloudDetailsPanelProps.cloudId", + "type": "string", + "tags": [], + "label": "cloudId", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-search-api-panels/components/cloud_details.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-api-panels", + "id": "def-common.CloudDetailsPanelProps.elasticsearchUrl", + "type": "string", + "tags": [], + "label": "elasticsearchUrl", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-search-api-panels/components/cloud_details.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-api-panels", + "id": "def-common.CloudDetailsPanelProps.isPanelLeft", + "type": "CompoundType", + "tags": [], + "label": "isPanelLeft", + "description": [], + "signature": [ + "boolean | undefined" + ], + "path": "packages/kbn-search-api-panels/components/cloud_details.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-api-panels", + "id": "def-common.CloudDetailsPanelProps.overviewPanelProps", + "type": "CompoundType", + "tags": [], + "label": "overviewPanelProps", + "description": [], + "signature": [ + "Partial<(", + "DisambiguateSet", + "<", + "_EuiPanelButtonlike", + ", ", + "_EuiPanelDivlike", + "> & ", + "_EuiPanelDivlike", + ") | (", + "DisambiguateSet", + "<", + "_EuiPanelDivlike", + ", ", + "_EuiPanelButtonlike", + "> & ", + "_EuiPanelButtonlike", + ")> | undefined" + ], + "path": "packages/kbn-search-api-panels/components/cloud_details.tsx", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/search-api-panels", "id": "def-common.LanguageDefinition", diff --git a/api_docs/kbn_search_api_panels.mdx b/api_docs/kbn_search_api_panels.mdx index fd345773a7927..7f271aac8b80e 100644 --- a/api_docs/kbn_search_api_panels.mdx +++ b/api_docs/kbn_search_api_panels.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-api-panels title: "@kbn/search-api-panels" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-api-panels plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-api-panels'] --- import kbnSearchApiPanelsObj from './kbn_search_api_panels.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/te | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 66 | 0 | 66 | 0 | +| 75 | 0 | 75 | 0 | ## Common diff --git a/api_docs/kbn_search_connectors.devdocs.json b/api_docs/kbn_search_connectors.devdocs.json index 423fd37bd2848..f3377ab693b7f 100644 --- a/api_docs/kbn_search_connectors.devdocs.json +++ b/api_docs/kbn_search_connectors.devdocs.json @@ -4922,7 +4922,7 @@ "section": "def-common.ConnectorStatus", "text": "ConnectorStatus" }, - "; language: string | null; index_name: string | null; configuration: ", + "; language: string | null; configuration: ", { "pluginId": "@kbn/search-connectors", "scope": "common", @@ -4930,7 +4930,7 @@ "section": "def-common.ConnectorConfiguration", "text": "ConnectorConfiguration" }, - "; pipeline?: ", + "; index_name: string | null; pipeline?: ", { "pluginId": "@kbn/search-connectors", "scope": "common", @@ -9321,6 +9321,33 @@ ] } ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.confluence.features.FeatureName.DOCUMENT_LEVEL_SECURITY", + "type": "Object", + "tags": [], + "label": "[FeatureName.DOCUMENT_LEVEL_SECURITY]", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.confluence.features.FeatureName.DOCUMENT_LEVEL_SECURITY.enabled", + "type": "boolean", + "tags": [], + "label": "enabled", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] } ] }, @@ -12540,10 +12567,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage", "type": "Object", "tags": [], - "label": "google_drive", + "label": "google_cloud_storage", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -12551,7 +12578,19 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.features", + "type": "Object", + "tags": [], + "label": "features", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration", "type": "Object", "tags": [], "label": "configuration", @@ -12562,7 +12601,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.service_account_credentials", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.service_account_credentials", "type": "Object", "tags": [], "label": "service_account_credentials", @@ -12573,7 +12612,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.service_account_credentials.default_value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.service_account_credentials.default_value", "type": "Uncategorized", "tags": [], "label": "default_value", @@ -12587,7 +12626,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.service_account_credentials.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.service_account_credentials.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -12601,7 +12640,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.service_account_credentials.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.service_account_credentials.display", "type": "string", "tags": [], "label": "display", @@ -12622,7 +12661,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.service_account_credentials.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.service_account_credentials.label", "type": "string", "tags": [], "label": "label", @@ -12633,7 +12672,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.service_account_credentials.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.service_account_credentials.options", "type": "Array", "tags": [], "label": "options", @@ -12647,7 +12686,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.service_account_credentials.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.service_account_credentials.order", "type": "number", "tags": [], "label": "order", @@ -12658,7 +12697,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.service_account_credentials.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.service_account_credentials.required", "type": "boolean", "tags": [], "label": "required", @@ -12672,7 +12711,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.service_account_credentials.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.service_account_credentials.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -12686,18 +12725,21 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.service_account_credentials.tooltip", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.service_account_credentials.tooltip", + "type": "Uncategorized", "tags": [], "label": "tooltip", "description": [], + "signature": [ + "null" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.service_account_credentials.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.service_account_credentials.type", "type": "string", "tags": [], "label": "type", @@ -12718,7 +12760,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.service_account_credentials.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.service_account_credentials.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -12732,7 +12774,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.service_account_credentials.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.service_account_credentials.validations", "type": "Array", "tags": [], "label": "validations", @@ -12746,7 +12788,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.service_account_credentials.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.service_account_credentials.value", "type": "string", "tags": [], "label": "value", @@ -12759,10 +12801,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_domain_wide_delegation_for_sync", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.retry_count", "type": "Object", "tags": [], - "label": "use_domain_wide_delegation_for_sync", + "label": "retry_count", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -12770,21 +12812,18 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_domain_wide_delegation_for_sync.default_value", - "type": "Uncategorized", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.retry_count.default_value", + "type": "number", "tags": [], "label": "default_value", "description": [], - "signature": [ - "null" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_domain_wide_delegation_for_sync.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.retry_count.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -12798,7 +12837,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_domain_wide_delegation_for_sync.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.retry_count.display", "type": "string", "tags": [], "label": "display", @@ -12811,7 +12850,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TOGGLE" + ".NUMERIC" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -12819,7 +12858,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_domain_wide_delegation_for_sync.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.retry_count.label", "type": "string", "tags": [], "label": "label", @@ -12830,7 +12869,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_domain_wide_delegation_for_sync.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.retry_count.options", "type": "Array", "tags": [], "label": "options", @@ -12844,7 +12883,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_domain_wide_delegation_for_sync.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.retry_count.order", "type": "number", "tags": [], "label": "order", @@ -12855,13 +12894,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_domain_wide_delegation_for_sync.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.retry_count.required", "type": "boolean", "tags": [], "label": "required", "description": [], "signature": [ - "true" + "false" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -12869,7 +12908,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_domain_wide_delegation_for_sync.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.retry_count.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -12883,18 +12922,21 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_domain_wide_delegation_for_sync.tooltip", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.retry_count.tooltip", + "type": "Uncategorized", "tags": [], "label": "tooltip", "description": [], + "signature": [ + "null" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_domain_wide_delegation_for_sync.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.retry_count.type", "type": "string", "tags": [], "label": "type", @@ -12907,7 +12949,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".BOOLEAN" + ".INTEGER" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -12915,13 +12957,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_domain_wide_delegation_for_sync.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.retry_count.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", "description": [], "signature": [ - "never[]" + "string[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -12929,7 +12971,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_domain_wide_delegation_for_sync.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.retry_count.validations", "type": "Array", "tags": [], "label": "validations", @@ -12943,14 +12985,11 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_domain_wide_delegation_for_sync.value", - "type": "boolean", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.retry_count.value", + "type": "string", "tags": [], "label": "value", "description": [], - "signature": [ - "false" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false @@ -12959,10 +12998,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email_for_data_sync", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.use_text_extraction_service", "type": "Object", "tags": [], - "label": "google_workspace_admin_email_for_data_sync", + "label": "use_text_extraction_service", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -12970,7 +13009,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email_for_data_sync.default_value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.use_text_extraction_service.default_value", "type": "Uncategorized", "tags": [], "label": "default_value", @@ -12984,13 +13023,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email_for_data_sync.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.use_text_extraction_service.depends_on", "type": "Array", "tags": [], "label": "depends_on", "description": [], "signature": [ - "{ field: string; value: true; }[]" + "never[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -12998,7 +13037,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email_for_data_sync.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.use_text_extraction_service.display", "type": "string", "tags": [], "label": "display", @@ -13011,7 +13050,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TEXTBOX" + ".TOGGLE" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -13019,7 +13058,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email_for_data_sync.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.use_text_extraction_service.label", "type": "string", "tags": [], "label": "label", @@ -13030,7 +13069,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email_for_data_sync.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.use_text_extraction_service.options", "type": "Array", "tags": [], "label": "options", @@ -13044,7 +13083,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email_for_data_sync.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.use_text_extraction_service.order", "type": "number", "tags": [], "label": "order", @@ -13055,7 +13094,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email_for_data_sync.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.use_text_extraction_service.required", "type": "boolean", "tags": [], "label": "required", @@ -13069,7 +13108,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email_for_data_sync.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.use_text_extraction_service.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -13083,7 +13122,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email_for_data_sync.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.use_text_extraction_service.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -13094,7 +13133,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email_for_data_sync.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.use_text_extraction_service.type", "type": "string", "tags": [], "label": "type", @@ -13107,7 +13146,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".STRING" + ".BOOLEAN" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -13115,13 +13154,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email_for_data_sync.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.use_text_extraction_service.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", "description": [], "signature": [ - "never[]" + "string[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -13129,13 +13168,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email_for_data_sync.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.use_text_extraction_service.validations", "type": "Array", "tags": [], "label": "validations", "description": [], "signature": [ - "{ type: string; constraint: string; }[]" + "never[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -13143,23 +13182,74 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email_for_data_sync.value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.configuration.use_text_extraction_service.value", + "type": "boolean", "tags": [], "label": "value", "description": [], + "signature": [ + "false" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false } ] - }, + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_cloud_storage.serviceType", + "type": "string", + "tags": [], + "label": "serviceType", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive", + "type": "Object", + "tags": [], + "label": "google_drive", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration", + "type": "Object", + "tags": [], + "label": "configuration", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_email_for_shared_drives_sync", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.service_account_credentials", "type": "Object", "tags": [], - "label": "google_workspace_email_for_shared_drives_sync", + "label": "service_account_credentials", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -13167,7 +13257,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_email_for_shared_drives_sync.default_value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.service_account_credentials.default_value", "type": "Uncategorized", "tags": [], "label": "default_value", @@ -13181,13 +13271,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_email_for_shared_drives_sync.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.service_account_credentials.depends_on", "type": "Array", "tags": [], "label": "depends_on", "description": [], "signature": [ - "{ field: string; value: true; }[]" + "never[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -13195,7 +13285,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_email_for_shared_drives_sync.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.service_account_credentials.display", "type": "string", "tags": [], "label": "display", @@ -13208,7 +13298,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TEXTBOX" + ".TEXTAREA" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -13216,7 +13306,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_email_for_shared_drives_sync.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.service_account_credentials.label", "type": "string", "tags": [], "label": "label", @@ -13227,7 +13317,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_email_for_shared_drives_sync.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.service_account_credentials.options", "type": "Array", "tags": [], "label": "options", @@ -13241,7 +13331,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_email_for_shared_drives_sync.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.service_account_credentials.order", "type": "number", "tags": [], "label": "order", @@ -13252,7 +13342,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_email_for_shared_drives_sync.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.service_account_credentials.required", "type": "boolean", "tags": [], "label": "required", @@ -13266,13 +13356,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_email_for_shared_drives_sync.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.service_account_credentials.sensitive", "type": "boolean", "tags": [], "label": "sensitive", "description": [], "signature": [ - "false" + "true" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -13280,7 +13370,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_email_for_shared_drives_sync.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.service_account_credentials.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -13291,7 +13381,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_email_for_shared_drives_sync.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.service_account_credentials.type", "type": "string", "tags": [], "label": "type", @@ -13312,7 +13402,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_email_for_shared_drives_sync.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.service_account_credentials.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -13326,13 +13416,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_email_for_shared_drives_sync.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.service_account_credentials.validations", "type": "Array", "tags": [], "label": "validations", "description": [], "signature": [ - "{ type: string; constraint: string; }[]" + "never[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -13340,7 +13430,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_email_for_shared_drives_sync.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.service_account_credentials.value", "type": "string", "tags": [], "label": "value", @@ -13353,10 +13443,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_document_level_security", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_domain_wide_delegation_for_sync", "type": "Object", "tags": [], - "label": "use_document_level_security", + "label": "use_domain_wide_delegation_for_sync", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -13364,7 +13454,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_document_level_security.default_value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_domain_wide_delegation_for_sync.default_value", "type": "Uncategorized", "tags": [], "label": "default_value", @@ -13378,7 +13468,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_document_level_security.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_domain_wide_delegation_for_sync.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -13392,7 +13482,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_document_level_security.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_domain_wide_delegation_for_sync.display", "type": "string", "tags": [], "label": "display", @@ -13413,7 +13503,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_document_level_security.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_domain_wide_delegation_for_sync.label", "type": "string", "tags": [], "label": "label", @@ -13424,7 +13514,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_document_level_security.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_domain_wide_delegation_for_sync.options", "type": "Array", "tags": [], "label": "options", @@ -13438,7 +13528,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_document_level_security.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_domain_wide_delegation_for_sync.order", "type": "number", "tags": [], "label": "order", @@ -13449,7 +13539,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_document_level_security.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_domain_wide_delegation_for_sync.required", "type": "boolean", "tags": [], "label": "required", @@ -13463,7 +13553,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_document_level_security.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_domain_wide_delegation_for_sync.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -13477,7 +13567,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_document_level_security.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_domain_wide_delegation_for_sync.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -13488,7 +13578,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_document_level_security.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_domain_wide_delegation_for_sync.type", "type": "string", "tags": [], "label": "type", @@ -13509,7 +13599,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_document_level_security.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_domain_wide_delegation_for_sync.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -13523,7 +13613,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_document_level_security.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_domain_wide_delegation_for_sync.validations", "type": "Array", "tags": [], "label": "validations", @@ -13537,7 +13627,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_document_level_security.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_domain_wide_delegation_for_sync.value", "type": "boolean", "tags": [], "label": "value", @@ -13553,10 +13643,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email_for_data_sync", "type": "Object", "tags": [], - "label": "google_workspace_admin_email", + "label": "google_workspace_admin_email_for_data_sync", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -13564,7 +13654,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email.default_value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email_for_data_sync.default_value", "type": "Uncategorized", "tags": [], "label": "default_value", @@ -13578,13 +13668,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email_for_data_sync.depends_on", "type": "Array", "tags": [], "label": "depends_on", "description": [], "signature": [ - "({ field: string; value: true; } | { field: string; value: false; })[]" + "{ field: string; value: true; }[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -13592,7 +13682,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email_for_data_sync.display", "type": "string", "tags": [], "label": "display", @@ -13613,7 +13703,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email_for_data_sync.label", "type": "string", "tags": [], "label": "label", @@ -13624,7 +13714,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email_for_data_sync.options", "type": "Array", "tags": [], "label": "options", @@ -13638,7 +13728,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email_for_data_sync.order", "type": "number", "tags": [], "label": "order", @@ -13649,7 +13739,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email_for_data_sync.required", "type": "boolean", "tags": [], "label": "required", @@ -13663,7 +13753,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email_for_data_sync.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -13677,7 +13767,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email_for_data_sync.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -13688,7 +13778,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email_for_data_sync.type", "type": "string", "tags": [], "label": "type", @@ -13709,7 +13799,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email_for_data_sync.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -13723,7 +13813,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email_for_data_sync.validations", "type": "Array", "tags": [], "label": "validations", @@ -13737,7 +13827,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email_for_data_sync.value", "type": "string", "tags": [], "label": "value", @@ -13750,10 +13840,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.max_concurrency", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_email_for_shared_drives_sync", "type": "Object", "tags": [], - "label": "max_concurrency", + "label": "google_workspace_email_for_shared_drives_sync", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -13761,24 +13851,27 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.max_concurrency.default_value", - "type": "number", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_email_for_shared_drives_sync.default_value", + "type": "Uncategorized", "tags": [], "label": "default_value", "description": [], + "signature": [ + "null" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.max_concurrency.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_email_for_shared_drives_sync.depends_on", "type": "Array", "tags": [], "label": "depends_on", "description": [], "signature": [ - "never[]" + "{ field: string; value: true; }[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -13786,7 +13879,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.max_concurrency.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_email_for_shared_drives_sync.display", "type": "string", "tags": [], "label": "display", @@ -13799,7 +13892,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".NUMERIC" + ".TEXTBOX" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -13807,7 +13900,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.max_concurrency.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_email_for_shared_drives_sync.label", "type": "string", "tags": [], "label": "label", @@ -13818,7 +13911,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.max_concurrency.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_email_for_shared_drives_sync.options", "type": "Array", "tags": [], "label": "options", @@ -13832,7 +13925,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.max_concurrency.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_email_for_shared_drives_sync.order", "type": "number", "tags": [], "label": "order", @@ -13843,13 +13936,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.max_concurrency.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_email_for_shared_drives_sync.required", "type": "boolean", "tags": [], "label": "required", "description": [], "signature": [ - "false" + "true" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -13857,7 +13950,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.max_concurrency.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_email_for_shared_drives_sync.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -13871,7 +13964,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.max_concurrency.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_email_for_shared_drives_sync.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -13882,7 +13975,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.max_concurrency.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_email_for_shared_drives_sync.type", "type": "string", "tags": [], "label": "type", @@ -13895,7 +13988,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".INTEGER" + ".STRING" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -13903,13 +13996,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.max_concurrency.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_email_for_shared_drives_sync.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", "description": [], "signature": [ - "string[]" + "never[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -13917,13 +14010,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.max_concurrency.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_email_for_shared_drives_sync.validations", "type": "Array", "tags": [], "label": "validations", "description": [], "signature": [ - "{ type: string; constraint: number; }[]" + "{ type: string; constraint: string; }[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -13931,7 +14024,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.max_concurrency.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_email_for_shared_drives_sync.value", "type": "string", "tags": [], "label": "value", @@ -13944,10 +14037,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_text_extraction_service", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_document_level_security", "type": "Object", "tags": [], - "label": "use_text_extraction_service", + "label": "use_document_level_security", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -13955,7 +14048,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_text_extraction_service.default_value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_document_level_security.default_value", "type": "Uncategorized", "tags": [], "label": "default_value", @@ -13969,7 +14062,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_text_extraction_service.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_document_level_security.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -13983,7 +14076,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_text_extraction_service.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_document_level_security.display", "type": "string", "tags": [], "label": "display", @@ -14004,7 +14097,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_text_extraction_service.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_document_level_security.label", "type": "string", "tags": [], "label": "label", @@ -14015,7 +14108,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_text_extraction_service.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_document_level_security.options", "type": "Array", "tags": [], "label": "options", @@ -14029,7 +14122,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_text_extraction_service.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_document_level_security.order", "type": "number", "tags": [], "label": "order", @@ -14040,7 +14133,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_text_extraction_service.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_document_level_security.required", "type": "boolean", "tags": [], "label": "required", @@ -14054,7 +14147,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_text_extraction_service.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_document_level_security.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -14068,7 +14161,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_text_extraction_service.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_document_level_security.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -14079,7 +14172,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_text_extraction_service.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_document_level_security.type", "type": "string", "tags": [], "label": "type", @@ -14100,13 +14193,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_text_extraction_service.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_document_level_security.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", "description": [], "signature": [ - "string[]" + "never[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -14114,7 +14207,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_text_extraction_service.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_document_level_security.validations", "type": "Array", "tags": [], "label": "validations", @@ -14128,7 +14221,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_text_extraction_service.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_document_level_security.value", "type": "boolean", "tags": [], "label": "value", @@ -14141,101 +14234,13 @@ "trackAdoption": false } ] - } - ] - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.features", - "type": "Object", - "tags": [], - "label": "features", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.features.FeatureName.DOCUMENT_LEVEL_SECURITY", - "type": "Object", - "tags": [], - "label": "[FeatureName.DOCUMENT_LEVEL_SECURITY]", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.features.FeatureName.DOCUMENT_LEVEL_SECURITY.enabled", - "type": "boolean", - "tags": [], - "label": "enabled", - "description": [], - "signature": [ - "true" - ], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false - } - ] - } - ] - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.name", - "type": "string", - "tags": [], - "label": "name", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.serviceType", - "type": "string", - "tags": [], - "label": "serviceType", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false - } - ] - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira", - "type": "Object", - "tags": [], - "label": "jira", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration", - "type": "Object", - "tags": [], - "label": "configuration", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ + }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.data_source", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email", "type": "Object", "tags": [], - "label": "data_source", + "label": "google_workspace_admin_email", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -14243,7 +14248,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.data_source.default_value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email.default_value", "type": "Uncategorized", "tags": [], "label": "default_value", @@ -14257,13 +14262,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.data_source.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email.depends_on", "type": "Array", "tags": [], "label": "depends_on", "description": [], "signature": [ - "never[]" + "({ field: string; value: true; } | { field: string; value: false; })[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -14271,7 +14276,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.data_source.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email.display", "type": "string", "tags": [], "label": "display", @@ -14284,7 +14289,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".DROPDOWN" + ".TEXTBOX" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -14292,7 +14297,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.data_source.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email.label", "type": "string", "tags": [], "label": "label", @@ -14303,13 +14308,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.data_source.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email.options", "type": "Array", "tags": [], "label": "options", "description": [], "signature": [ - "{ label: string; value: string; }[]" + "never[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -14317,7 +14322,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.data_source.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email.order", "type": "number", "tags": [], "label": "order", @@ -14328,7 +14333,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.data_source.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email.required", "type": "boolean", "tags": [], "label": "required", @@ -14342,7 +14347,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.data_source.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -14356,21 +14361,18 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.data_source.tooltip", - "type": "Uncategorized", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email.tooltip", + "type": "string", "tags": [], "label": "tooltip", "description": [], - "signature": [ - "null" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.data_source.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email.type", "type": "string", "tags": [], "label": "type", @@ -14391,7 +14393,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.data_source.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -14405,13 +14407,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.data_source.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email.validations", "type": "Array", "tags": [], "label": "validations", "description": [], "signature": [ - "never[]" + "{ type: string; constraint: string; }[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -14419,7 +14421,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.data_source.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.google_workspace_admin_email.value", "type": "string", "tags": [], "label": "value", @@ -14432,10 +14434,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.username", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.max_concurrency", "type": "Object", "tags": [], - "label": "username", + "label": "max_concurrency", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -14443,27 +14445,24 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.username.default_value", - "type": "Uncategorized", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.max_concurrency.default_value", + "type": "number", "tags": [], "label": "default_value", "description": [], - "signature": [ - "null" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.username.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.max_concurrency.depends_on", "type": "Array", "tags": [], "label": "depends_on", "description": [], "signature": [ - "{ field: string; value: string; }[]" + "never[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -14471,7 +14470,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.username.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.max_concurrency.display", "type": "string", "tags": [], "label": "display", @@ -14484,7 +14483,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TEXTBOX" + ".NUMERIC" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -14492,7 +14491,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.username.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.max_concurrency.label", "type": "string", "tags": [], "label": "label", @@ -14503,7 +14502,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.username.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.max_concurrency.options", "type": "Array", "tags": [], "label": "options", @@ -14517,7 +14516,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.username.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.max_concurrency.order", "type": "number", "tags": [], "label": "order", @@ -14528,13 +14527,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.username.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.max_concurrency.required", "type": "boolean", "tags": [], "label": "required", "description": [], "signature": [ - "true" + "false" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -14542,7 +14541,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.username.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.max_concurrency.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -14556,21 +14555,18 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.username.tooltip", - "type": "Uncategorized", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.max_concurrency.tooltip", + "type": "string", "tags": [], "label": "tooltip", "description": [], - "signature": [ - "null" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.username.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.max_concurrency.type", "type": "string", "tags": [], "label": "type", @@ -14583,7 +14579,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".STRING" + ".INTEGER" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -14591,13 +14587,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.username.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.max_concurrency.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", "description": [], "signature": [ - "never[]" + "string[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -14605,13 +14601,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.username.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.max_concurrency.validations", "type": "Array", "tags": [], "label": "validations", "description": [], "signature": [ - "never[]" + "{ type: string; constraint: number; }[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -14619,7 +14615,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.username.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.max_concurrency.value", "type": "string", "tags": [], "label": "value", @@ -14632,10 +14628,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.password", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_text_extraction_service", "type": "Object", "tags": [], - "label": "password", + "label": "use_text_extraction_service", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -14643,7 +14639,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.password.default_value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_text_extraction_service.default_value", "type": "Uncategorized", "tags": [], "label": "default_value", @@ -14657,13 +14653,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.password.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_text_extraction_service.depends_on", "type": "Array", "tags": [], "label": "depends_on", "description": [], "signature": [ - "{ field: string; value: string; }[]" + "never[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -14671,7 +14667,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.password.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_text_extraction_service.display", "type": "string", "tags": [], "label": "display", @@ -14684,7 +14680,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TEXTBOX" + ".TOGGLE" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -14692,7 +14688,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.password.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_text_extraction_service.label", "type": "string", "tags": [], "label": "label", @@ -14703,7 +14699,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.password.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_text_extraction_service.options", "type": "Array", "tags": [], "label": "options", @@ -14717,7 +14713,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.password.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_text_extraction_service.order", "type": "number", "tags": [], "label": "order", @@ -14728,7 +14724,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.password.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_text_extraction_service.required", "type": "boolean", "tags": [], "label": "required", @@ -14742,13 +14738,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.password.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_text_extraction_service.sensitive", "type": "boolean", "tags": [], "label": "sensitive", "description": [], "signature": [ - "true" + "false" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -14756,21 +14752,18 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.password.tooltip", - "type": "Uncategorized", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_text_extraction_service.tooltip", + "type": "string", "tags": [], "label": "tooltip", "description": [], - "signature": [ - "null" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.password.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_text_extraction_service.type", "type": "string", "tags": [], "label": "type", @@ -14783,7 +14776,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".STRING" + ".BOOLEAN" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -14791,13 +14784,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.password.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_text_extraction_service.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", "description": [], "signature": [ - "never[]" + "string[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -14805,7 +14798,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.password.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_text_extraction_service.validations", "type": "Array", "tags": [], "label": "validations", @@ -14819,23 +14812,39 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.password.value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.configuration.use_text_extraction_service.value", + "type": "boolean", "tags": [], "label": "value", "description": [], + "signature": [ + "false" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false } ] - }, + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.features", + "type": "Object", + "tags": [], + "label": "features", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.account_email", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.features.FeatureName.DOCUMENT_LEVEL_SECURITY", "type": "Object", "tags": [], - "label": "account_email", + "label": "[FeatureName.DOCUMENT_LEVEL_SECURITY]", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -14843,7 +14852,82 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.account_email.default_value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.features.FeatureName.DOCUMENT_LEVEL_SECURITY.enabled", + "type": "boolean", + "tags": [], + "label": "enabled", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.google_drive.serviceType", + "type": "string", + "tags": [], + "label": "serviceType", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira", + "type": "Object", + "tags": [], + "label": "jira", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration", + "type": "Object", + "tags": [], + "label": "configuration", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.data_source", + "type": "Object", + "tags": [], + "label": "data_source", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.data_source.default_value", "type": "Uncategorized", "tags": [], "label": "default_value", @@ -14857,13 +14941,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.account_email.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.data_source.depends_on", "type": "Array", "tags": [], "label": "depends_on", "description": [], "signature": [ - "{ field: string; value: string; }[]" + "never[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -14871,7 +14955,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.account_email.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.data_source.display", "type": "string", "tags": [], "label": "display", @@ -14884,7 +14968,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TEXTBOX" + ".DROPDOWN" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -14892,7 +14976,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.account_email.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.data_source.label", "type": "string", "tags": [], "label": "label", @@ -14903,13 +14987,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.account_email.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.data_source.options", "type": "Array", "tags": [], "label": "options", "description": [], "signature": [ - "never[]" + "{ label: string; value: string; }[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -14917,7 +15001,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.account_email.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.data_source.order", "type": "number", "tags": [], "label": "order", @@ -14928,18 +15012,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.account_email.placeholder", - "type": "string", - "tags": [], - "label": "placeholder", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.account_email.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.data_source.required", "type": "boolean", "tags": [], "label": "required", @@ -14953,7 +15026,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.account_email.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.data_source.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -14967,7 +15040,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.account_email.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.data_source.tooltip", "type": "Uncategorized", "tags": [], "label": "tooltip", @@ -14981,7 +15054,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.account_email.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.data_source.type", "type": "string", "tags": [], "label": "type", @@ -15002,7 +15075,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.account_email.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.data_source.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -15016,7 +15089,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.account_email.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.data_source.validations", "type": "Array", "tags": [], "label": "validations", @@ -15030,7 +15103,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.account_email.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.data_source.value", "type": "string", "tags": [], "label": "value", @@ -15043,10 +15116,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.api_token", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.username", "type": "Object", "tags": [], - "label": "api_token", + "label": "username", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -15054,7 +15127,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.api_token.default_value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.username.default_value", "type": "Uncategorized", "tags": [], "label": "default_value", @@ -15068,7 +15141,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.api_token.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.username.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -15082,7 +15155,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.api_token.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.username.display", "type": "string", "tags": [], "label": "display", @@ -15103,7 +15176,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.api_token.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.username.label", "type": "string", "tags": [], "label": "label", @@ -15114,7 +15187,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.api_token.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.username.options", "type": "Array", "tags": [], "label": "options", @@ -15128,7 +15201,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.api_token.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.username.order", "type": "number", "tags": [], "label": "order", @@ -15139,7 +15212,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.api_token.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.username.required", "type": "boolean", "tags": [], "label": "required", @@ -15153,13 +15226,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.api_token.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.username.sensitive", "type": "boolean", "tags": [], "label": "sensitive", "description": [], "signature": [ - "true" + "false" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -15167,7 +15240,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.api_token.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.username.tooltip", "type": "Uncategorized", "tags": [], "label": "tooltip", @@ -15181,7 +15254,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.api_token.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.username.type", "type": "string", "tags": [], "label": "type", @@ -15202,7 +15275,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.api_token.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.username.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -15216,7 +15289,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.api_token.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.username.validations", "type": "Array", "tags": [], "label": "validations", @@ -15230,7 +15303,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.api_token.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.username.value", "type": "string", "tags": [], "label": "value", @@ -15243,10 +15316,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.jira_url", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.password", "type": "Object", "tags": [], - "label": "jira_url", + "label": "password", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -15254,7 +15327,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.jira_url.default_value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.password.default_value", "type": "Uncategorized", "tags": [], "label": "default_value", @@ -15268,13 +15341,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.jira_url.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.password.depends_on", "type": "Array", "tags": [], "label": "depends_on", "description": [], "signature": [ - "never[]" + "{ field: string; value: string; }[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -15282,7 +15355,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.jira_url.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.password.display", "type": "string", "tags": [], "label": "display", @@ -15303,7 +15376,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.jira_url.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.password.label", "type": "string", "tags": [], "label": "label", @@ -15314,7 +15387,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.jira_url.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.password.options", "type": "Array", "tags": [], "label": "options", @@ -15328,7 +15401,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.jira_url.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.password.order", "type": "number", "tags": [], "label": "order", @@ -15339,18 +15412,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.jira_url.placeholder", - "type": "string", - "tags": [], - "label": "placeholder", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.jira_url.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.password.required", "type": "boolean", "tags": [], "label": "required", @@ -15364,13 +15426,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.jira_url.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.password.sensitive", "type": "boolean", "tags": [], "label": "sensitive", "description": [], "signature": [ - "false" + "true" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -15378,7 +15440,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.jira_url.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.password.tooltip", "type": "Uncategorized", "tags": [], "label": "tooltip", @@ -15392,7 +15454,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.jira_url.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.password.type", "type": "string", "tags": [], "label": "type", @@ -15413,7 +15475,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.jira_url.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.password.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -15427,7 +15489,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.jira_url.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.password.validations", "type": "Array", "tags": [], "label": "validations", @@ -15441,7 +15503,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.jira_url.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.password.value", "type": "string", "tags": [], "label": "value", @@ -15454,10 +15516,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.projects", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.account_email", "type": "Object", "tags": [], - "label": "projects", + "label": "account_email", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -15465,7 +15527,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.projects.default_value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.account_email.default_value", "type": "Uncategorized", "tags": [], "label": "default_value", @@ -15479,13 +15541,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.projects.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.account_email.depends_on", "type": "Array", "tags": [], "label": "depends_on", "description": [], "signature": [ - "never[]" + "{ field: string; value: string; }[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -15493,7 +15555,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.projects.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.account_email.display", "type": "string", "tags": [], "label": "display", @@ -15506,7 +15568,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TEXTAREA" + ".TEXTBOX" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -15514,7 +15576,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.projects.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.account_email.label", "type": "string", "tags": [], "label": "label", @@ -15525,7 +15587,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.projects.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.account_email.options", "type": "Array", "tags": [], "label": "options", @@ -15539,7 +15601,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.projects.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.account_email.order", "type": "number", "tags": [], "label": "order", @@ -15550,7 +15612,18 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.projects.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.account_email.placeholder", + "type": "string", + "tags": [], + "label": "placeholder", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.account_email.required", "type": "boolean", "tags": [], "label": "required", @@ -15564,7 +15637,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.projects.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.account_email.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -15578,18 +15651,21 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.projects.tooltip", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.account_email.tooltip", + "type": "Uncategorized", "tags": [], "label": "tooltip", "description": [], + "signature": [ + "null" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.projects.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.account_email.type", "type": "string", "tags": [], "label": "type", @@ -15602,7 +15678,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".LIST" + ".STRING" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -15610,7 +15686,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.projects.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.account_email.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -15624,7 +15700,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.projects.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.account_email.validations", "type": "Array", "tags": [], "label": "validations", @@ -15638,7 +15714,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.projects.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.account_email.value", "type": "string", "tags": [], "label": "value", @@ -15651,10 +15727,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_enabled", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.api_token", "type": "Object", "tags": [], - "label": "ssl_enabled", + "label": "api_token", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -15662,7 +15738,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_enabled.default_value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.api_token.default_value", "type": "Uncategorized", "tags": [], "label": "default_value", @@ -15676,13 +15752,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_enabled.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.api_token.depends_on", "type": "Array", "tags": [], "label": "depends_on", "description": [], "signature": [ - "never[]" + "{ field: string; value: string; }[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -15690,7 +15766,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_enabled.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.api_token.display", "type": "string", "tags": [], "label": "display", @@ -15703,7 +15779,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TOGGLE" + ".TEXTBOX" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -15711,7 +15787,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_enabled.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.api_token.label", "type": "string", "tags": [], "label": "label", @@ -15722,7 +15798,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_enabled.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.api_token.options", "type": "Array", "tags": [], "label": "options", @@ -15736,7 +15812,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_enabled.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.api_token.order", "type": "number", "tags": [], "label": "order", @@ -15747,7 +15823,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_enabled.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.api_token.required", "type": "boolean", "tags": [], "label": "required", @@ -15761,13 +15837,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_enabled.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.api_token.sensitive", "type": "boolean", "tags": [], "label": "sensitive", "description": [], "signature": [ - "false" + "true" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -15775,7 +15851,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_enabled.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.api_token.tooltip", "type": "Uncategorized", "tags": [], "label": "tooltip", @@ -15789,7 +15865,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_enabled.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.api_token.type", "type": "string", "tags": [], "label": "type", @@ -15802,7 +15878,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".BOOLEAN" + ".STRING" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -15810,7 +15886,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_enabled.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.api_token.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -15824,7 +15900,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_enabled.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.api_token.validations", "type": "Array", "tags": [], "label": "validations", @@ -15838,14 +15914,11 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_enabled.value", - "type": "boolean", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.api_token.value", + "type": "string", "tags": [], "label": "value", "description": [], - "signature": [ - "false" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false @@ -15854,10 +15927,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_ca", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.jira_url", "type": "Object", "tags": [], - "label": "ssl_ca", + "label": "jira_url", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -15865,7 +15938,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_ca.default_value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.jira_url.default_value", "type": "Uncategorized", "tags": [], "label": "default_value", @@ -15879,13 +15952,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_ca.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.jira_url.depends_on", "type": "Array", "tags": [], "label": "depends_on", "description": [], "signature": [ - "{ field: string; value: true; }[]" + "never[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -15893,7 +15966,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_ca.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.jira_url.display", "type": "string", "tags": [], "label": "display", @@ -15914,7 +15987,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_ca.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.jira_url.label", "type": "string", "tags": [], "label": "label", @@ -15925,7 +15998,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_ca.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.jira_url.options", "type": "Array", "tags": [], "label": "options", @@ -15939,7 +16012,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_ca.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.jira_url.order", "type": "number", "tags": [], "label": "order", @@ -15950,7 +16023,18 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_ca.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.jira_url.placeholder", + "type": "string", + "tags": [], + "label": "placeholder", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.jira_url.required", "type": "boolean", "tags": [], "label": "required", @@ -15964,7 +16048,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_ca.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.jira_url.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -15978,7 +16062,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_ca.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.jira_url.tooltip", "type": "Uncategorized", "tags": [], "label": "tooltip", @@ -15992,7 +16076,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_ca.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.jira_url.type", "type": "string", "tags": [], "label": "type", @@ -16013,7 +16097,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_ca.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.jira_url.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -16027,7 +16111,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_ca.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.jira_url.validations", "type": "Array", "tags": [], "label": "validations", @@ -16041,7 +16125,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_ca.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.jira_url.value", "type": "string", "tags": [], "label": "value", @@ -16054,10 +16138,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.retry_count", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.projects", "type": "Object", "tags": [], - "label": "retry_count", + "label": "projects", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -16065,18 +16149,21 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.retry_count.default_value", - "type": "number", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.projects.default_value", + "type": "Uncategorized", "tags": [], "label": "default_value", "description": [], + "signature": [ + "null" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.retry_count.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.projects.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -16090,7 +16177,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.retry_count.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.projects.display", "type": "string", "tags": [], "label": "display", @@ -16103,7 +16190,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".NUMERIC" + ".TEXTAREA" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -16111,7 +16198,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.retry_count.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.projects.label", "type": "string", "tags": [], "label": "label", @@ -16122,7 +16209,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.retry_count.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.projects.options", "type": "Array", "tags": [], "label": "options", @@ -16136,7 +16223,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.retry_count.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.projects.order", "type": "number", "tags": [], "label": "order", @@ -16147,13 +16234,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.retry_count.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.projects.required", "type": "boolean", "tags": [], "label": "required", "description": [], "signature": [ - "false" + "true" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -16161,7 +16248,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.retry_count.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.projects.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -16175,21 +16262,18 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.retry_count.tooltip", - "type": "Uncategorized", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.projects.tooltip", + "type": "string", "tags": [], "label": "tooltip", "description": [], - "signature": [ - "null" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.retry_count.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.projects.type", "type": "string", "tags": [], "label": "type", @@ -16202,7 +16286,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".INTEGER" + ".LIST" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -16210,13 +16294,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.retry_count.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.projects.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", "description": [], "signature": [ - "string[]" + "never[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -16224,7 +16308,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.retry_count.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.projects.validations", "type": "Array", "tags": [], "label": "validations", @@ -16238,8 +16322,8 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.retry_count.value", - "type": "number", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.projects.value", + "type": "string", "tags": [], "label": "value", "description": [], @@ -16251,10 +16335,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.concurrent_downloads", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_enabled", "type": "Object", "tags": [], - "label": "concurrent_downloads", + "label": "ssl_enabled", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -16262,18 +16346,21 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.concurrent_downloads.default_value", - "type": "number", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_enabled.default_value", + "type": "Uncategorized", "tags": [], "label": "default_value", "description": [], + "signature": [ + "null" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.concurrent_downloads.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_enabled.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -16287,7 +16374,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.concurrent_downloads.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_enabled.display", "type": "string", "tags": [], "label": "display", @@ -16300,7 +16387,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".NUMERIC" + ".TOGGLE" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -16308,7 +16395,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.concurrent_downloads.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_enabled.label", "type": "string", "tags": [], "label": "label", @@ -16319,7 +16406,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.concurrent_downloads.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_enabled.options", "type": "Array", "tags": [], "label": "options", @@ -16333,7 +16420,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.concurrent_downloads.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_enabled.order", "type": "number", "tags": [], "label": "order", @@ -16344,13 +16431,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.concurrent_downloads.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_enabled.required", "type": "boolean", "tags": [], "label": "required", "description": [], "signature": [ - "false" + "true" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -16358,7 +16445,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.concurrent_downloads.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_enabled.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -16372,7 +16459,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.concurrent_downloads.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_enabled.tooltip", "type": "Uncategorized", "tags": [], "label": "tooltip", @@ -16386,7 +16473,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.concurrent_downloads.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_enabled.type", "type": "string", "tags": [], "label": "type", @@ -16399,7 +16486,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".INTEGER" + ".BOOLEAN" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -16407,13 +16494,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.concurrent_downloads.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_enabled.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", "description": [], "signature": [ - "string[]" + "never[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -16421,13 +16508,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.concurrent_downloads.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_enabled.validations", "type": "Array", "tags": [], "label": "validations", "description": [], "signature": [ - "{ type: string; constraint: number; }[]" + "never[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -16435,11 +16522,14 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.concurrent_downloads.value", - "type": "number", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_enabled.value", + "type": "boolean", "tags": [], "label": "value", "description": [], + "signature": [ + "false" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false @@ -16448,10 +16538,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_document_level_security", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_ca", "type": "Object", "tags": [], - "label": "use_document_level_security", + "label": "ssl_ca", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -16459,7 +16549,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_document_level_security.default_value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_ca.default_value", "type": "Uncategorized", "tags": [], "label": "default_value", @@ -16473,13 +16563,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_document_level_security.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_ca.depends_on", "type": "Array", "tags": [], "label": "depends_on", "description": [], "signature": [ - "{ field: string; value: string; }[]" + "{ field: string; value: true; }[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -16487,7 +16577,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_document_level_security.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_ca.display", "type": "string", "tags": [], "label": "display", @@ -16500,7 +16590,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TOGGLE" + ".TEXTBOX" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -16508,7 +16598,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_document_level_security.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_ca.label", "type": "string", "tags": [], "label": "label", @@ -16519,7 +16609,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_document_level_security.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_ca.options", "type": "Array", "tags": [], "label": "options", @@ -16533,7 +16623,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_document_level_security.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_ca.order", "type": "number", "tags": [], "label": "order", @@ -16544,7 +16634,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_document_level_security.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_ca.required", "type": "boolean", "tags": [], "label": "required", @@ -16558,7 +16648,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_document_level_security.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_ca.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -16572,18 +16662,21 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_document_level_security.tooltip", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_ca.tooltip", + "type": "Uncategorized", "tags": [], "label": "tooltip", "description": [], + "signature": [ + "null" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_document_level_security.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_ca.type", "type": "string", "tags": [], "label": "type", @@ -16596,7 +16689,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".BOOLEAN" + ".STRING" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -16604,7 +16697,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_document_level_security.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_ca.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -16618,7 +16711,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_document_level_security.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_ca.validations", "type": "Array", "tags": [], "label": "validations", @@ -16632,14 +16725,11 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_document_level_security.value", - "type": "boolean", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.ssl_ca.value", + "type": "string", "tags": [], "label": "value", "description": [], - "signature": [ - "false" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false @@ -16648,10 +16738,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_text_extraction_service", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.retry_count", "type": "Object", "tags": [], - "label": "use_text_extraction_service", + "label": "retry_count", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -16659,21 +16749,18 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_text_extraction_service.default_value", - "type": "boolean", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.retry_count.default_value", + "type": "number", "tags": [], "label": "default_value", "description": [], - "signature": [ - "false" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_text_extraction_service.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.retry_count.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -16687,7 +16774,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_text_extraction_service.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.retry_count.display", "type": "string", "tags": [], "label": "display", @@ -16700,7 +16787,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TOGGLE" + ".NUMERIC" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -16708,7 +16795,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_text_extraction_service.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.retry_count.label", "type": "string", "tags": [], "label": "label", @@ -16719,7 +16806,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_text_extraction_service.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.retry_count.options", "type": "Array", "tags": [], "label": "options", @@ -16733,7 +16820,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_text_extraction_service.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.retry_count.order", "type": "number", "tags": [], "label": "order", @@ -16744,13 +16831,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_text_extraction_service.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.retry_count.required", "type": "boolean", "tags": [], "label": "required", "description": [], "signature": [ - "true" + "false" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -16758,7 +16845,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_text_extraction_service.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.retry_count.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -16772,18 +16859,21 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_text_extraction_service.tooltip", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.retry_count.tooltip", + "type": "Uncategorized", "tags": [], "label": "tooltip", "description": [], + "signature": [ + "null" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_text_extraction_service.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.retry_count.type", "type": "string", "tags": [], "label": "type", @@ -16796,7 +16886,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".BOOLEAN" + ".INTEGER" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -16804,7 +16894,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_text_extraction_service.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.retry_count.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -16818,7 +16908,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_text_extraction_service.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.retry_count.validations", "type": "Array", "tags": [], "label": "validations", @@ -16832,154 +16922,23 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_text_extraction_service.value", - "type": "boolean", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.retry_count.value", + "type": "number", "tags": [], "label": "value", "description": [], - "signature": [ - "false" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false } ] - } - ] - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.features", - "type": "Object", - "tags": [], - "label": "features", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.features.FeatureName.SYNC_RULES", - "type": "Object", - "tags": [], - "label": "[FeatureName.SYNC_RULES]", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.features.FeatureName.SYNC_RULES.advanced", - "type": "Object", - "tags": [], - "label": "advanced", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.features.FeatureName.SYNC_RULES.advanced.enabled", - "type": "boolean", - "tags": [], - "label": "enabled", - "description": [], - "signature": [ - "true" - ], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false - } - ] - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.features.FeatureName.SYNC_RULES.basic", - "type": "Object", - "tags": [], - "label": "basic", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.features.FeatureName.SYNC_RULES.basic.enabled", - "type": "boolean", - "tags": [], - "label": "enabled", - "description": [], - "signature": [ - "true" - ], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false - } - ] - } - ] - } - ] - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.name", - "type": "string", - "tags": [], - "label": "name", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.serviceType", - "type": "string", - "tags": [], - "label": "serviceType", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false - } - ] - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb", - "type": "Object", - "tags": [], - "label": "mongodb", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration", - "type": "Object", - "tags": [], - "label": "configuration", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ + }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.host", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.concurrent_downloads", "type": "Object", "tags": [], - "label": "host", + "label": "concurrent_downloads", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -16987,8 +16946,8 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.host.default_value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.concurrent_downloads.default_value", + "type": "number", "tags": [], "label": "default_value", "description": [], @@ -16998,7 +16957,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.host.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.concurrent_downloads.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -17012,7 +16971,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.host.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.concurrent_downloads.display", "type": "string", "tags": [], "label": "display", @@ -17025,7 +16984,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TEXTBOX" + ".NUMERIC" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -17033,7 +16992,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.host.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.concurrent_downloads.label", "type": "string", "tags": [], "label": "label", @@ -17044,7 +17003,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.host.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.concurrent_downloads.options", "type": "Array", "tags": [], "label": "options", @@ -17058,7 +17017,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.host.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.concurrent_downloads.order", "type": "number", "tags": [], "label": "order", @@ -17069,13 +17028,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.host.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.concurrent_downloads.required", "type": "boolean", "tags": [], "label": "required", "description": [], "signature": [ - "true" + "false" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -17083,7 +17042,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.host.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.concurrent_downloads.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -17097,18 +17056,21 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.host.tooltip", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.concurrent_downloads.tooltip", + "type": "Uncategorized", "tags": [], "label": "tooltip", "description": [], + "signature": [ + "null" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.host.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.concurrent_downloads.type", "type": "string", "tags": [], "label": "type", @@ -17121,7 +17083,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".STRING" + ".INTEGER" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -17129,13 +17091,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.host.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.concurrent_downloads.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", "description": [], "signature": [ - "never[]" + "string[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -17143,13 +17105,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.host.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.concurrent_downloads.validations", "type": "Array", "tags": [], "label": "validations", "description": [], "signature": [ - "never[]" + "{ type: string; constraint: number; }[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -17157,8 +17119,8 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.host.value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.concurrent_downloads.value", + "type": "number", "tags": [], "label": "value", "description": [], @@ -17170,10 +17132,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.user", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_document_level_security", "type": "Object", "tags": [], - "label": "user", + "label": "use_document_level_security", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -17181,24 +17143,27 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.user.default_value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_document_level_security.default_value", + "type": "Uncategorized", "tags": [], "label": "default_value", "description": [], + "signature": [ + "null" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.user.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_document_level_security.depends_on", "type": "Array", "tags": [], "label": "depends_on", "description": [], "signature": [ - "never[]" + "{ field: string; value: string; }[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -17206,7 +17171,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.user.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_document_level_security.display", "type": "string", "tags": [], "label": "display", @@ -17219,7 +17184,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TEXTBOX" + ".TOGGLE" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -17227,7 +17192,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.user.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_document_level_security.label", "type": "string", "tags": [], "label": "label", @@ -17238,7 +17203,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.user.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_document_level_security.options", "type": "Array", "tags": [], "label": "options", @@ -17252,7 +17217,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.user.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_document_level_security.order", "type": "number", "tags": [], "label": "order", @@ -17263,13 +17228,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.user.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_document_level_security.required", "type": "boolean", "tags": [], "label": "required", "description": [], "signature": [ - "false" + "true" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -17277,7 +17242,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.user.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_document_level_security.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -17291,7 +17256,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.user.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_document_level_security.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -17302,7 +17267,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.user.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_document_level_security.type", "type": "string", "tags": [], "label": "type", @@ -17315,7 +17280,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".STRING" + ".BOOLEAN" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -17323,7 +17288,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.user.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_document_level_security.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -17337,7 +17302,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.user.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_document_level_security.validations", "type": "Array", "tags": [], "label": "validations", @@ -17351,11 +17316,14 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.user.value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_document_level_security.value", + "type": "boolean", "tags": [], "label": "value", "description": [], + "signature": [ + "false" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false @@ -17364,10 +17332,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.password", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_text_extraction_service", "type": "Object", "tags": [], - "label": "password", + "label": "use_text_extraction_service", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -17375,18 +17343,21 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.password.default_value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_text_extraction_service.default_value", + "type": "boolean", "tags": [], "label": "default_value", "description": [], + "signature": [ + "false" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.password.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_text_extraction_service.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -17400,7 +17371,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.password.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_text_extraction_service.display", "type": "string", "tags": [], "label": "display", @@ -17413,7 +17384,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TEXTBOX" + ".TOGGLE" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -17421,7 +17392,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.password.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_text_extraction_service.label", "type": "string", "tags": [], "label": "label", @@ -17432,7 +17403,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.password.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_text_extraction_service.options", "type": "Array", "tags": [], "label": "options", @@ -17446,7 +17417,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.password.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_text_extraction_service.order", "type": "number", "tags": [], "label": "order", @@ -17457,13 +17428,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.password.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_text_extraction_service.required", "type": "boolean", "tags": [], "label": "required", "description": [], "signature": [ - "false" + "true" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -17471,13 +17442,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.password.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_text_extraction_service.sensitive", "type": "boolean", "tags": [], "label": "sensitive", "description": [], "signature": [ - "true" + "false" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -17485,7 +17456,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.password.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_text_extraction_service.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -17496,7 +17467,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.password.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_text_extraction_service.type", "type": "string", "tags": [], "label": "type", @@ -17509,7 +17480,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".STRING" + ".BOOLEAN" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -17517,13 +17488,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.password.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_text_extraction_service.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", "description": [], "signature": [ - "never[]" + "string[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -17531,7 +17502,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.password.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_text_extraction_service.validations", "type": "Array", "tags": [], "label": "validations", @@ -17545,23 +17516,39 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.password.value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.configuration.use_text_extraction_service.value", + "type": "boolean", "tags": [], "label": "value", "description": [], + "signature": [ + "false" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false } ] - }, + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.features", + "type": "Object", + "tags": [], + "label": "features", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.database", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.features.FeatureName.SYNC_RULES", "type": "Object", "tags": [], - "label": "database", + "label": "[FeatureName.SYNC_RULES]", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -17569,78 +17556,220 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.database.default_value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.features.FeatureName.SYNC_RULES.advanced", + "type": "Object", "tags": [], - "label": "default_value", + "label": "advanced", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, - "trackAdoption": false + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.features.FeatureName.SYNC_RULES.advanced.enabled", + "type": "boolean", + "tags": [], + "label": "enabled", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.database.depends_on", - "type": "Array", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.features.FeatureName.SYNC_RULES.basic", + "type": "Object", "tags": [], - "label": "depends_on", + "label": "basic", "description": [], - "signature": [ - "never[]" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, - "trackAdoption": false - }, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.features.FeatureName.SYNC_RULES.basic.enabled", + "type": "boolean", + "tags": [], + "label": "enabled", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.features.FeatureName.DOCUMENT_LEVEL_SECURITY", + "type": "Object", + "tags": [], + "label": "[FeatureName.DOCUMENT_LEVEL_SECURITY]", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.database.display", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.features.FeatureName.DOCUMENT_LEVEL_SECURITY.enabled", + "type": "boolean", "tags": [], - "label": "display", + "label": "enabled", "description": [], "signature": [ - { - "pluginId": "@kbn/search-connectors", - "scope": "common", - "docId": "kibKbnSearchConnectorsPluginApi", - "section": "def-common.DisplayType", - "text": "DisplayType" - }, - ".TEXTBOX" + "true" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.database.label", - "type": "string", - "tags": [], - "label": "label", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.database.options", - "type": "Array", - "tags": [], - "label": "options", - "description": [], - "signature": [ - "never[]" - ], + } + ] + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.jira.serviceType", + "type": "string", + "tags": [], + "label": "serviceType", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb", + "type": "Object", + "tags": [], + "label": "mongodb", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration", + "type": "Object", + "tags": [], + "label": "configuration", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.host", + "type": "Object", + "tags": [], + "label": "host", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.host.default_value", + "type": "string", + "tags": [], + "label": "default_value", + "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.database.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.host.depends_on", + "type": "Array", + "tags": [], + "label": "depends_on", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.host.display", + "type": "string", + "tags": [], + "label": "display", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.DisplayType", + "text": "DisplayType" + }, + ".TEXTBOX" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.host.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.host.options", + "type": "Array", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.host.order", "type": "number", "tags": [], "label": "order", @@ -17651,7 +17780,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.database.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.host.required", "type": "boolean", "tags": [], "label": "required", @@ -17665,7 +17794,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.database.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.host.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -17679,7 +17808,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.database.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.host.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -17690,7 +17819,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.database.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.host.type", "type": "string", "tags": [], "label": "type", @@ -17711,7 +17840,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.database.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.host.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -17725,7 +17854,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.database.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.host.validations", "type": "Array", "tags": [], "label": "validations", @@ -17739,7 +17868,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.database.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.host.value", "type": "string", "tags": [], "label": "value", @@ -17752,10 +17881,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.collection", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.user", "type": "Object", "tags": [], - "label": "collection", + "label": "user", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -17763,7 +17892,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.collection.default_value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.user.default_value", "type": "string", "tags": [], "label": "default_value", @@ -17774,7 +17903,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.collection.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.user.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -17788,7 +17917,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.collection.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.user.display", "type": "string", "tags": [], "label": "display", @@ -17809,7 +17938,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.collection.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.user.label", "type": "string", "tags": [], "label": "label", @@ -17820,7 +17949,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.collection.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.user.options", "type": "Array", "tags": [], "label": "options", @@ -17834,7 +17963,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.collection.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.user.order", "type": "number", "tags": [], "label": "order", @@ -17845,13 +17974,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.collection.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.user.required", "type": "boolean", "tags": [], "label": "required", "description": [], "signature": [ - "true" + "false" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -17859,7 +17988,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.collection.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.user.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -17873,7 +18002,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.collection.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.user.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -17884,7 +18013,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.collection.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.user.type", "type": "string", "tags": [], "label": "type", @@ -17905,7 +18034,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.collection.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.user.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -17919,7 +18048,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.collection.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.user.validations", "type": "Array", "tags": [], "label": "validations", @@ -17933,7 +18062,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.collection.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.user.value", "type": "string", "tags": [], "label": "value", @@ -17946,10 +18075,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.direct_connection", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.password", "type": "Object", "tags": [], - "label": "direct_connection", + "label": "password", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -17957,21 +18086,18 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.direct_connection.default_value", - "type": "boolean", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.password.default_value", + "type": "string", "tags": [], "label": "default_value", "description": [], - "signature": [ - "false" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.direct_connection.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.password.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -17985,7 +18111,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.direct_connection.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.password.display", "type": "string", "tags": [], "label": "display", @@ -17998,7 +18124,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TOGGLE" + ".TEXTBOX" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -18006,7 +18132,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.direct_connection.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.password.label", "type": "string", "tags": [], "label": "label", @@ -18017,7 +18143,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.direct_connection.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.password.options", "type": "Array", "tags": [], "label": "options", @@ -18031,7 +18157,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.direct_connection.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.password.order", "type": "number", "tags": [], "label": "order", @@ -18042,13 +18168,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.direct_connection.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.password.required", "type": "boolean", "tags": [], "label": "required", "description": [], "signature": [ - "true" + "false" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -18056,13 +18182,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.direct_connection.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.password.sensitive", "type": "boolean", "tags": [], "label": "sensitive", "description": [], "signature": [ - "false" + "true" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -18070,7 +18196,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.direct_connection.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.password.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -18081,7 +18207,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.direct_connection.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.password.type", "type": "string", "tags": [], "label": "type", @@ -18094,7 +18220,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".BOOLEAN" + ".STRING" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -18102,7 +18228,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.direct_connection.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.password.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -18116,7 +18242,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.direct_connection.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.password.validations", "type": "Array", "tags": [], "label": "validations", @@ -18130,182 +18256,23 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.direct_connection.value", - "type": "boolean", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.password.value", + "type": "string", "tags": [], "label": "value", "description": [], - "signature": [ - "false" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false } ] - } - ] - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.features", - "type": "Object", - "tags": [], - "label": "features", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ + }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.features.FeatureName.FILTERING_ADVANCED_CONFIG", - "type": "boolean", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.database", + "type": "Object", "tags": [], - "label": "[FeatureName.FILTERING_ADVANCED_CONFIG]", - "description": [], - "signature": [ - "true" - ], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.features.FeatureName.FILTERING_RULES", - "type": "boolean", - "tags": [], - "label": "[FeatureName.FILTERING_RULES]", - "description": [], - "signature": [ - "true" - ], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.features.FeatureName.SYNC_RULES", - "type": "Object", - "tags": [], - "label": "[FeatureName.SYNC_RULES]", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.features.FeatureName.SYNC_RULES.advanced", - "type": "Object", - "tags": [], - "label": "advanced", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.features.FeatureName.SYNC_RULES.advanced.enabled", - "type": "boolean", - "tags": [], - "label": "enabled", - "description": [], - "signature": [ - "true" - ], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false - } - ] - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.features.FeatureName.SYNC_RULES.basic", - "type": "Object", - "tags": [], - "label": "basic", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.features.FeatureName.SYNC_RULES.basic.enabled", - "type": "boolean", - "tags": [], - "label": "enabled", - "description": [], - "signature": [ - "true" - ], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false - } - ] - } - ] - } - ] - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.name", - "type": "string", - "tags": [], - "label": "name", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.serviceType", - "type": "string", - "tags": [], - "label": "serviceType", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false - } - ] - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql", - "type": "Object", - "tags": [], - "label": "mssql", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration", - "type": "Object", - "tags": [], - "label": "configuration", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.host", - "type": "Object", - "tags": [], - "label": "host", + "label": "database", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -18313,7 +18280,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.host.default_value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.database.default_value", "type": "string", "tags": [], "label": "default_value", @@ -18324,7 +18291,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.host.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.database.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -18338,7 +18305,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.host.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.database.display", "type": "string", "tags": [], "label": "display", @@ -18359,7 +18326,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.host.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.database.label", "type": "string", "tags": [], "label": "label", @@ -18370,7 +18337,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.host.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.database.options", "type": "Array", "tags": [], "label": "options", @@ -18384,7 +18351,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.host.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.database.order", "type": "number", "tags": [], "label": "order", @@ -18395,7 +18362,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.host.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.database.required", "type": "boolean", "tags": [], "label": "required", @@ -18409,7 +18376,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.host.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.database.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -18423,7 +18390,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.host.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.database.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -18434,7 +18401,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.host.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.database.type", "type": "string", "tags": [], "label": "type", @@ -18455,7 +18422,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.host.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.database.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -18469,7 +18436,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.host.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.database.validations", "type": "Array", "tags": [], "label": "validations", @@ -18483,7 +18450,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.host.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.database.value", "type": "string", "tags": [], "label": "value", @@ -18496,10 +18463,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.port", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.collection", "type": "Object", "tags": [], - "label": "port", + "label": "collection", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -18507,21 +18474,18 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.port.default_value", - "type": "Uncategorized", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.collection.default_value", + "type": "string", "tags": [], "label": "default_value", "description": [], - "signature": [ - "null" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.port.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.collection.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -18535,7 +18499,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.port.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.collection.display", "type": "string", "tags": [], "label": "display", @@ -18548,7 +18512,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".NUMERIC" + ".TEXTBOX" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -18556,7 +18520,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.port.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.collection.label", "type": "string", "tags": [], "label": "label", @@ -18567,7 +18531,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.port.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.collection.options", "type": "Array", "tags": [], "label": "options", @@ -18581,7 +18545,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.port.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.collection.order", "type": "number", "tags": [], "label": "order", @@ -18592,7 +18556,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.port.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.collection.required", "type": "boolean", "tags": [], "label": "required", @@ -18606,7 +18570,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.port.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.collection.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -18620,7 +18584,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.port.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.collection.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -18631,7 +18595,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.port.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.collection.type", "type": "string", "tags": [], "label": "type", @@ -18644,7 +18608,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".INTEGER" + ".STRING" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -18652,7 +18616,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.port.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.collection.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -18666,7 +18630,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.port.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.collection.validations", "type": "Array", "tags": [], "label": "validations", @@ -18680,8 +18644,8 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.port.value", - "type": "number", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.collection.value", + "type": "string", "tags": [], "label": "value", "description": [], @@ -18693,10 +18657,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.username", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.direct_connection", "type": "Object", "tags": [], - "label": "username", + "label": "direct_connection", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -18704,18 +18668,21 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.username.default_value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.direct_connection.default_value", + "type": "boolean", "tags": [], "label": "default_value", "description": [], + "signature": [ + "false" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.username.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.direct_connection.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -18729,7 +18696,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.username.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.direct_connection.display", "type": "string", "tags": [], "label": "display", @@ -18742,7 +18709,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TEXTBOX" + ".TOGGLE" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -18750,7 +18717,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.username.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.direct_connection.label", "type": "string", "tags": [], "label": "label", @@ -18761,7 +18728,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.username.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.direct_connection.options", "type": "Array", "tags": [], "label": "options", @@ -18775,7 +18742,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.username.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.direct_connection.order", "type": "number", "tags": [], "label": "order", @@ -18786,7 +18753,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.username.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.direct_connection.required", "type": "boolean", "tags": [], "label": "required", @@ -18800,7 +18767,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.username.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.direct_connection.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -18814,7 +18781,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.username.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.direct_connection.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -18825,7 +18792,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.username.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.direct_connection.type", "type": "string", "tags": [], "label": "type", @@ -18838,7 +18805,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".STRING" + ".BOOLEAN" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -18846,7 +18813,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.username.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.direct_connection.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -18860,7 +18827,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.username.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.direct_connection.validations", "type": "Array", "tags": [], "label": "validations", @@ -18874,11 +18841,14 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.username.value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.direct_connection.value", + "type": "boolean", "tags": [], "label": "value", "description": [], + "signature": [ + "false" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false @@ -18887,10 +18857,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.password", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.ssl_enabled", "type": "Object", "tags": [], - "label": "password", + "label": "ssl_enabled", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -18898,18 +18868,21 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.password.default_value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.ssl_enabled.default_value", + "type": "boolean", "tags": [], "label": "default_value", "description": [], + "signature": [ + "false" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.password.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.ssl_enabled.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -18923,7 +18896,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.password.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.ssl_enabled.display", "type": "string", "tags": [], "label": "display", @@ -18936,7 +18909,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TEXTBOX" + ".TOGGLE" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -18944,7 +18917,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.password.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.ssl_enabled.label", "type": "string", "tags": [], "label": "label", @@ -18955,7 +18928,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.password.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.ssl_enabled.options", "type": "Array", "tags": [], "label": "options", @@ -18969,7 +18942,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.password.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.ssl_enabled.order", "type": "number", "tags": [], "label": "order", @@ -18980,7 +18953,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.password.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.ssl_enabled.required", "type": "boolean", "tags": [], "label": "required", @@ -18994,13 +18967,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.password.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.ssl_enabled.sensitive", "type": "boolean", "tags": [], "label": "sensitive", "description": [], "signature": [ - "true" + "false" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -19008,7 +18981,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.password.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.ssl_enabled.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -19019,7 +18992,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.password.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.ssl_enabled.type", "type": "string", "tags": [], "label": "type", @@ -19032,7 +19005,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".STRING" + ".BOOLEAN" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -19040,7 +19013,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.password.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.ssl_enabled.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -19054,7 +19027,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.password.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.ssl_enabled.validations", "type": "Array", "tags": [], "label": "validations", @@ -19068,11 +19041,14 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.password.value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.ssl_enabled.value", + "type": "boolean", "tags": [], "label": "value", "description": [], + "signature": [ + "false" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false @@ -19081,10 +19057,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.database", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.ssl_ca", "type": "Object", "tags": [], - "label": "database", + "label": "ssl_ca", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -19092,7 +19068,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.database.default_value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.ssl_ca.default_value", "type": "string", "tags": [], "label": "default_value", @@ -19103,13 +19079,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.database.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.ssl_ca.depends_on", "type": "Array", "tags": [], "label": "depends_on", "description": [], "signature": [ - "never[]" + "{ field: string; value: true; }[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -19117,7 +19093,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.database.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.ssl_ca.display", "type": "string", "tags": [], "label": "display", @@ -19138,7 +19114,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.database.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.ssl_ca.label", "type": "string", "tags": [], "label": "label", @@ -19149,7 +19125,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.database.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.ssl_ca.options", "type": "Array", "tags": [], "label": "options", @@ -19163,7 +19139,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.database.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.ssl_ca.order", "type": "number", "tags": [], "label": "order", @@ -19174,13 +19150,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.database.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.ssl_ca.required", "type": "boolean", "tags": [], "label": "required", "description": [], "signature": [ - "true" + "false" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -19188,7 +19164,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.database.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.ssl_ca.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -19202,7 +19178,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.database.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.ssl_ca.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -19213,7 +19189,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.database.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.ssl_ca.type", "type": "string", "tags": [], "label": "type", @@ -19234,7 +19210,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.database.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.ssl_ca.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -19248,7 +19224,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.database.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.ssl_ca.validations", "type": "Array", "tags": [], "label": "validations", @@ -19262,7 +19238,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.database.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.ssl_ca.value", "type": "string", "tags": [], "label": "value", @@ -19275,10 +19251,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.tables", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.tls_insecure", "type": "Object", "tags": [], - "label": "tables", + "label": "tls_insecure", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -19286,24 +19262,27 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.tables.default_value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.tls_insecure.default_value", + "type": "boolean", "tags": [], "label": "default_value", "description": [], + "signature": [ + "false" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.tables.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.tls_insecure.depends_on", "type": "Array", "tags": [], "label": "depends_on", "description": [], "signature": [ - "never[]" + "{ field: string; value: true; }[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -19311,7 +19290,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.tables.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.tls_insecure.display", "type": "string", "tags": [], "label": "display", @@ -19324,7 +19303,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TEXTAREA" + ".TOGGLE" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -19332,7 +19311,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.tables.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.tls_insecure.label", "type": "string", "tags": [], "label": "label", @@ -19343,7 +19322,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.tables.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.tls_insecure.options", "type": "Array", "tags": [], "label": "options", @@ -19357,7 +19336,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.tables.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.tls_insecure.order", "type": "number", "tags": [], "label": "order", @@ -19368,7 +19347,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.tables.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.tls_insecure.required", "type": "boolean", "tags": [], "label": "required", @@ -19382,7 +19361,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.tables.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.tls_insecure.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -19396,7 +19375,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.tables.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.tls_insecure.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -19407,7 +19386,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.tables.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.tls_insecure.type", "type": "string", "tags": [], "label": "type", @@ -19420,7 +19399,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".LIST" + ".BOOLEAN" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -19428,13 +19407,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.tables.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.tls_insecure.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", "description": [], "signature": [ - "never[]" + "string[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -19442,7 +19421,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.tables.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.tls_insecure.validations", "type": "Array", "tags": [], "label": "validations", @@ -19456,23 +19435,67 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.tables.value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.configuration.tls_insecure.value", + "type": "boolean", "tags": [], "label": "value", "description": [], + "signature": [ + "false" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false } ] + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.features", + "type": "Object", + "tags": [], + "label": "features", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.features.FeatureName.FILTERING_ADVANCED_CONFIG", + "type": "boolean", + "tags": [], + "label": "[FeatureName.FILTERING_ADVANCED_CONFIG]", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_enabled", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.features.FeatureName.FILTERING_RULES", + "type": "boolean", + "tags": [], + "label": "[FeatureName.FILTERING_RULES]", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.features.FeatureName.SYNC_RULES", "type": "Object", "tags": [], - "label": "ssl_enabled", + "label": "[FeatureName.SYNC_RULES]", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -19480,21 +19503,133 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_enabled.default_value", - "type": "boolean", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.features.FeatureName.SYNC_RULES.advanced", + "type": "Object", + "tags": [], + "label": "advanced", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.features.FeatureName.SYNC_RULES.advanced.enabled", + "type": "boolean", + "tags": [], + "label": "enabled", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.features.FeatureName.SYNC_RULES.basic", + "type": "Object", + "tags": [], + "label": "basic", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.features.FeatureName.SYNC_RULES.basic.enabled", + "type": "boolean", + "tags": [], + "label": "enabled", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ] + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mongodb.serviceType", + "type": "string", + "tags": [], + "label": "serviceType", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql", + "type": "Object", + "tags": [], + "label": "mssql", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration", + "type": "Object", + "tags": [], + "label": "configuration", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.host", + "type": "Object", + "tags": [], + "label": "host", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.host.default_value", + "type": "string", "tags": [], "label": "default_value", "description": [], - "signature": [ - "false" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_enabled.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.host.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -19508,7 +19643,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_enabled.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.host.display", "type": "string", "tags": [], "label": "display", @@ -19521,7 +19656,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TOGGLE" + ".TEXTBOX" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -19529,7 +19664,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_enabled.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.host.label", "type": "string", "tags": [], "label": "label", @@ -19540,7 +19675,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_enabled.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.host.options", "type": "Array", "tags": [], "label": "options", @@ -19554,7 +19689,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_enabled.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.host.order", "type": "number", "tags": [], "label": "order", @@ -19565,7 +19700,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_enabled.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.host.required", "type": "boolean", "tags": [], "label": "required", @@ -19579,7 +19714,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_enabled.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.host.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -19593,7 +19728,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_enabled.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.host.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -19604,7 +19739,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_enabled.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.host.type", "type": "string", "tags": [], "label": "type", @@ -19617,7 +19752,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".BOOLEAN" + ".STRING" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -19625,7 +19760,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_enabled.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.host.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -19639,7 +19774,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_enabled.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.host.validations", "type": "Array", "tags": [], "label": "validations", @@ -19653,14 +19788,11 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_enabled.value", - "type": "boolean", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.host.value", + "type": "string", "tags": [], "label": "value", "description": [], - "signature": [ - "false" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false @@ -19669,10 +19801,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_ca", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.port", "type": "Object", "tags": [], - "label": "ssl_ca", + "label": "port", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -19680,24 +19812,27 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_ca.default_value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.port.default_value", + "type": "Uncategorized", "tags": [], "label": "default_value", "description": [], + "signature": [ + "null" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_ca.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.port.depends_on", "type": "Array", "tags": [], "label": "depends_on", "description": [], "signature": [ - "{ field: string; value: true; }[]" + "never[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -19705,7 +19840,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_ca.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.port.display", "type": "string", "tags": [], "label": "display", @@ -19718,7 +19853,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TEXTBOX" + ".NUMERIC" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -19726,7 +19861,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_ca.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.port.label", "type": "string", "tags": [], "label": "label", @@ -19737,7 +19872,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_ca.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.port.options", "type": "Array", "tags": [], "label": "options", @@ -19751,7 +19886,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_ca.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.port.order", "type": "number", "tags": [], "label": "order", @@ -19762,7 +19897,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_ca.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.port.required", "type": "boolean", "tags": [], "label": "required", @@ -19776,7 +19911,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_ca.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.port.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -19790,7 +19925,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_ca.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.port.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -19801,7 +19936,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_ca.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.port.type", "type": "string", "tags": [], "label": "type", @@ -19814,7 +19949,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".STRING" + ".INTEGER" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -19822,7 +19957,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_ca.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.port.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -19836,7 +19971,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_ca.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.port.validations", "type": "Array", "tags": [], "label": "validations", @@ -19850,8 +19985,8 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_ca.value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.port.value", + "type": "number", "tags": [], "label": "value", "description": [], @@ -19863,10 +19998,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.schema", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.username", "type": "Object", "tags": [], - "label": "schema", + "label": "username", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -19874,7 +20009,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.schema.default_value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.username.default_value", "type": "string", "tags": [], "label": "default_value", @@ -19885,7 +20020,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.schema.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.username.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -19899,7 +20034,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.schema.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.username.display", "type": "string", "tags": [], "label": "display", @@ -19920,7 +20055,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.schema.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.username.label", "type": "string", "tags": [], "label": "label", @@ -19931,7 +20066,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.schema.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.username.options", "type": "Array", "tags": [], "label": "options", @@ -19945,7 +20080,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.schema.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.username.order", "type": "number", "tags": [], "label": "order", @@ -19956,7 +20091,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.schema.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.username.required", "type": "boolean", "tags": [], "label": "required", @@ -19970,7 +20105,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.schema.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.username.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -19984,7 +20119,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.schema.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.username.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -19995,7 +20130,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.schema.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.username.type", "type": "string", "tags": [], "label": "type", @@ -20016,7 +20151,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.schema.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.username.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -20030,7 +20165,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.schema.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.username.validations", "type": "Array", "tags": [], "label": "validations", @@ -20044,7 +20179,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.schema.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.username.value", "type": "string", "tags": [], "label": "value", @@ -20057,10 +20192,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.fetch_size", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.password", "type": "Object", "tags": [], - "label": "fetch_size", + "label": "password", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -20068,8 +20203,8 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.fetch_size.default_value", - "type": "number", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.password.default_value", + "type": "string", "tags": [], "label": "default_value", "description": [], @@ -20079,7 +20214,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.fetch_size.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.password.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -20093,7 +20228,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.fetch_size.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.password.display", "type": "string", "tags": [], "label": "display", @@ -20106,7 +20241,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".NUMERIC" + ".TEXTBOX" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -20114,7 +20249,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.fetch_size.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.password.label", "type": "string", "tags": [], "label": "label", @@ -20125,7 +20260,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.fetch_size.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.password.options", "type": "Array", "tags": [], "label": "options", @@ -20139,7 +20274,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.fetch_size.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.password.order", "type": "number", "tags": [], "label": "order", @@ -20150,13 +20285,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.fetch_size.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.password.required", "type": "boolean", "tags": [], "label": "required", "description": [], "signature": [ - "false" + "true" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -20164,13 +20299,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.fetch_size.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.password.sensitive", "type": "boolean", "tags": [], "label": "sensitive", "description": [], "signature": [ - "false" + "true" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -20178,7 +20313,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.fetch_size.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.password.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -20189,7 +20324,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.fetch_size.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.password.type", "type": "string", "tags": [], "label": "type", @@ -20202,7 +20337,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".INTEGER" + ".STRING" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -20210,13 +20345,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.fetch_size.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.password.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", "description": [], "signature": [ - "string[]" + "never[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -20224,7 +20359,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.fetch_size.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.password.validations", "type": "Array", "tags": [], "label": "validations", @@ -20238,8 +20373,8 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.fetch_size.value", - "type": "number", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.password.value", + "type": "string", "tags": [], "label": "value", "description": [], @@ -20251,10 +20386,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.retry_count", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.database", "type": "Object", "tags": [], - "label": "retry_count", + "label": "database", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -20262,8 +20397,8 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.retry_count.default_value", - "type": "number", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.database.default_value", + "type": "string", "tags": [], "label": "default_value", "description": [], @@ -20273,7 +20408,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.retry_count.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.database.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -20287,7 +20422,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.retry_count.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.database.display", "type": "string", "tags": [], "label": "display", @@ -20300,7 +20435,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".NUMERIC" + ".TEXTBOX" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -20308,7 +20443,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.retry_count.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.database.label", "type": "string", "tags": [], "label": "label", @@ -20319,7 +20454,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.retry_count.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.database.options", "type": "Array", "tags": [], "label": "options", @@ -20333,7 +20468,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.retry_count.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.database.order", "type": "number", "tags": [], "label": "order", @@ -20344,13 +20479,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.retry_count.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.database.required", "type": "boolean", "tags": [], "label": "required", "description": [], "signature": [ - "false" + "true" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -20358,7 +20493,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.retry_count.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.database.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -20372,7 +20507,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.retry_count.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.database.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -20383,7 +20518,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.retry_count.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.database.type", "type": "string", "tags": [], "label": "type", @@ -20396,7 +20531,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".INTEGER" + ".STRING" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -20404,13 +20539,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.retry_count.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.database.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", "description": [], "signature": [ - "string[]" + "never[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -20418,7 +20553,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.retry_count.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.database.validations", "type": "Array", "tags": [], "label": "validations", @@ -20432,8 +20567,8 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.retry_count.value", - "type": "number", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.database.value", + "type": "string", "tags": [], "label": "value", "description": [], @@ -20445,10 +20580,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.validate_host", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.tables", "type": "Object", "tags": [], - "label": "validate_host", + "label": "tables", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -20456,21 +20591,18 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.validate_host.default_value", - "type": "boolean", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.tables.default_value", + "type": "string", "tags": [], "label": "default_value", "description": [], - "signature": [ - "false" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.validate_host.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.tables.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -20484,7 +20616,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.validate_host.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.tables.display", "type": "string", "tags": [], "label": "display", @@ -20497,7 +20629,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TOGGLE" + ".TEXTAREA" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -20505,7 +20637,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.validate_host.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.tables.label", "type": "string", "tags": [], "label": "label", @@ -20516,7 +20648,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.validate_host.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.tables.options", "type": "Array", "tags": [], "label": "options", @@ -20530,7 +20662,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.validate_host.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.tables.order", "type": "number", "tags": [], "label": "order", @@ -20541,7 +20673,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.validate_host.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.tables.required", "type": "boolean", "tags": [], "label": "required", @@ -20555,7 +20687,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.validate_host.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.tables.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -20569,7 +20701,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.validate_host.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.tables.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -20580,7 +20712,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.validate_host.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.tables.type", "type": "string", "tags": [], "label": "type", @@ -20593,7 +20725,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".BOOLEAN" + ".LIST" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -20601,7 +20733,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.validate_host.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.tables.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -20615,7 +20747,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.validate_host.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.tables.validations", "type": "Array", "tags": [], "label": "validations", @@ -20629,39 +20761,23 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.validate_host.value", - "type": "boolean", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.tables.value", + "type": "string", "tags": [], "label": "value", "description": [], - "signature": [ - "false" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false } ] - } - ] - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.features", - "type": "Object", - "tags": [], - "label": "features", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ + }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.features.FeatureName.SYNC_RULES", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_enabled", "type": "Object", "tags": [], - "label": "[FeatureName.SYNC_RULES]", + "label": "ssl_enabled", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -20669,133 +20785,21 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.features.FeatureName.SYNC_RULES.advanced", - "type": "Object", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_enabled.default_value", + "type": "boolean", "tags": [], - "label": "advanced", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.features.FeatureName.SYNC_RULES.advanced.enabled", - "type": "boolean", - "tags": [], - "label": "enabled", - "description": [], - "signature": [ - "false" - ], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false - } - ] - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.features.FeatureName.SYNC_RULES.basic", - "type": "Object", - "tags": [], - "label": "basic", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.features.FeatureName.SYNC_RULES.basic.enabled", - "type": "boolean", - "tags": [], - "label": "enabled", - "description": [], - "signature": [ - "true" - ], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false - } - ] - } - ] - } - ] - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.name", - "type": "string", - "tags": [], - "label": "name", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.serviceType", - "type": "string", - "tags": [], - "label": "serviceType", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false - } - ] - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql", - "type": "Object", - "tags": [], - "label": "mysql", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration", - "type": "Object", - "tags": [], - "label": "configuration", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.host", - "type": "Object", - "tags": [], - "label": "host", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.host.default_value", - "type": "string", - "tags": [], - "label": "default_value", + "label": "default_value", "description": [], + "signature": [ + "false" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.host.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_enabled.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -20809,7 +20813,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.host.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_enabled.display", "type": "string", "tags": [], "label": "display", @@ -20822,7 +20826,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TEXTBOX" + ".TOGGLE" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -20830,7 +20834,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.host.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_enabled.label", "type": "string", "tags": [], "label": "label", @@ -20841,7 +20845,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.host.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_enabled.options", "type": "Array", "tags": [], "label": "options", @@ -20855,7 +20859,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.host.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_enabled.order", "type": "number", "tags": [], "label": "order", @@ -20866,7 +20870,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.host.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_enabled.required", "type": "boolean", "tags": [], "label": "required", @@ -20880,7 +20884,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.host.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_enabled.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -20894,7 +20898,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.host.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_enabled.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -20905,7 +20909,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.host.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_enabled.type", "type": "string", "tags": [], "label": "type", @@ -20918,7 +20922,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".STRING" + ".BOOLEAN" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -20926,7 +20930,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.host.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_enabled.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -20940,7 +20944,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.host.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_enabled.validations", "type": "Array", "tags": [], "label": "validations", @@ -20954,11 +20958,14 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.host.value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_enabled.value", + "type": "boolean", "tags": [], "label": "value", "description": [], + "signature": [ + "false" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false @@ -20967,10 +20974,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.port", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_ca", "type": "Object", "tags": [], - "label": "port", + "label": "ssl_ca", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -20978,27 +20985,24 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.port.default_value", - "type": "Uncategorized", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_ca.default_value", + "type": "string", "tags": [], "label": "default_value", "description": [], - "signature": [ - "null" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.port.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_ca.depends_on", "type": "Array", "tags": [], "label": "depends_on", "description": [], "signature": [ - "never[]" + "{ field: string; value: true; }[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -21006,7 +21010,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.port.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_ca.display", "type": "string", "tags": [], "label": "display", @@ -21019,7 +21023,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".NUMERIC" + ".TEXTBOX" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -21027,7 +21031,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.port.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_ca.label", "type": "string", "tags": [], "label": "label", @@ -21038,7 +21042,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.port.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_ca.options", "type": "Array", "tags": [], "label": "options", @@ -21052,7 +21056,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.port.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_ca.order", "type": "number", "tags": [], "label": "order", @@ -21063,7 +21067,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.port.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_ca.required", "type": "boolean", "tags": [], "label": "required", @@ -21077,7 +21081,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.port.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_ca.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -21091,7 +21095,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.port.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_ca.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -21102,7 +21106,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.port.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_ca.type", "type": "string", "tags": [], "label": "type", @@ -21115,7 +21119,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".INTEGER" + ".STRING" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -21123,7 +21127,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.port.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_ca.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -21137,7 +21141,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.port.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_ca.validations", "type": "Array", "tags": [], "label": "validations", @@ -21151,7 +21155,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.port.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.ssl_ca.value", "type": "string", "tags": [], "label": "value", @@ -21164,10 +21168,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.user", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.schema", "type": "Object", "tags": [], - "label": "user", + "label": "schema", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -21175,7 +21179,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.user.default_value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.schema.default_value", "type": "string", "tags": [], "label": "default_value", @@ -21186,7 +21190,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.user.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.schema.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -21200,7 +21204,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.user.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.schema.display", "type": "string", "tags": [], "label": "display", @@ -21221,7 +21225,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.user.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.schema.label", "type": "string", "tags": [], "label": "label", @@ -21232,7 +21236,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.user.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.schema.options", "type": "Array", "tags": [], "label": "options", @@ -21246,7 +21250,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.user.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.schema.order", "type": "number", "tags": [], "label": "order", @@ -21257,13 +21261,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.user.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.schema.required", "type": "boolean", "tags": [], "label": "required", "description": [], "signature": [ - "false" + "true" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -21271,7 +21275,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.user.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.schema.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -21285,7 +21289,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.user.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.schema.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -21296,7 +21300,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.user.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.schema.type", "type": "string", "tags": [], "label": "type", @@ -21317,7 +21321,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.user.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.schema.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -21331,7 +21335,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.user.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.schema.validations", "type": "Array", "tags": [], "label": "validations", @@ -21345,7 +21349,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.user.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.schema.value", "type": "string", "tags": [], "label": "value", @@ -21358,10 +21362,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.password", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.fetch_size", "type": "Object", "tags": [], - "label": "password", + "label": "fetch_size", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -21369,8 +21373,8 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.password.default_value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.fetch_size.default_value", + "type": "number", "tags": [], "label": "default_value", "description": [], @@ -21380,7 +21384,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.password.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.fetch_size.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -21394,7 +21398,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.password.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.fetch_size.display", "type": "string", "tags": [], "label": "display", @@ -21407,7 +21411,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TEXTBOX" + ".NUMERIC" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -21415,7 +21419,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.password.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.fetch_size.label", "type": "string", "tags": [], "label": "label", @@ -21426,7 +21430,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.password.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.fetch_size.options", "type": "Array", "tags": [], "label": "options", @@ -21440,7 +21444,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.password.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.fetch_size.order", "type": "number", "tags": [], "label": "order", @@ -21451,7 +21455,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.password.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.fetch_size.required", "type": "boolean", "tags": [], "label": "required", @@ -21465,13 +21469,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.password.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.fetch_size.sensitive", "type": "boolean", "tags": [], "label": "sensitive", "description": [], "signature": [ - "true" + "false" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -21479,7 +21483,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.password.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.fetch_size.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -21490,7 +21494,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.password.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.fetch_size.type", "type": "string", "tags": [], "label": "type", @@ -21503,7 +21507,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".STRING" + ".INTEGER" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -21511,13 +21515,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.password.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.fetch_size.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", "description": [], "signature": [ - "never[]" + "string[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -21525,7 +21529,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.password.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.fetch_size.validations", "type": "Array", "tags": [], "label": "validations", @@ -21539,8 +21543,8 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.password.value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.fetch_size.value", + "type": "number", "tags": [], "label": "value", "description": [], @@ -21552,10 +21556,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.database", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.retry_count", "type": "Object", "tags": [], - "label": "database", + "label": "retry_count", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -21563,8 +21567,8 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.database.default_value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.retry_count.default_value", + "type": "number", "tags": [], "label": "default_value", "description": [], @@ -21574,7 +21578,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.database.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.retry_count.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -21588,7 +21592,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.database.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.retry_count.display", "type": "string", "tags": [], "label": "display", @@ -21601,7 +21605,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TEXTBOX" + ".NUMERIC" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -21609,7 +21613,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.database.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.retry_count.label", "type": "string", "tags": [], "label": "label", @@ -21620,7 +21624,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.database.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.retry_count.options", "type": "Array", "tags": [], "label": "options", @@ -21634,7 +21638,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.database.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.retry_count.order", "type": "number", "tags": [], "label": "order", @@ -21645,13 +21649,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.database.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.retry_count.required", "type": "boolean", "tags": [], "label": "required", "description": [], "signature": [ - "true" + "false" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -21659,7 +21663,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.database.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.retry_count.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -21673,7 +21677,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.database.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.retry_count.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -21684,7 +21688,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.database.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.retry_count.type", "type": "string", "tags": [], "label": "type", @@ -21697,7 +21701,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".STRING" + ".INTEGER" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -21705,13 +21709,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.database.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.retry_count.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", "description": [], "signature": [ - "never[]" + "string[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -21719,7 +21723,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.database.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.retry_count.validations", "type": "Array", "tags": [], "label": "validations", @@ -21733,8 +21737,8 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.database.value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.retry_count.value", + "type": "number", "tags": [], "label": "value", "description": [], @@ -21746,10 +21750,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.tables", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.validate_host", "type": "Object", "tags": [], - "label": "tables", + "label": "validate_host", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -21757,18 +21761,21 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.tables.default_value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.validate_host.default_value", + "type": "boolean", "tags": [], "label": "default_value", "description": [], + "signature": [ + "false" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.tables.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.validate_host.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -21782,7 +21789,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.tables.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.validate_host.display", "type": "string", "tags": [], "label": "display", @@ -21795,7 +21802,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TEXTAREA" + ".TOGGLE" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -21803,7 +21810,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.tables.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.validate_host.label", "type": "string", "tags": [], "label": "label", @@ -21814,7 +21821,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.tables.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.validate_host.options", "type": "Array", "tags": [], "label": "options", @@ -21828,7 +21835,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.tables.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.validate_host.order", "type": "number", "tags": [], "label": "order", @@ -21839,7 +21846,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.tables.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.validate_host.required", "type": "boolean", "tags": [], "label": "required", @@ -21853,7 +21860,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.tables.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.validate_host.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -21867,7 +21874,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.tables.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.validate_host.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -21878,7 +21885,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.tables.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.validate_host.type", "type": "string", "tags": [], "label": "type", @@ -21891,7 +21898,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".LIST" + ".BOOLEAN" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -21899,7 +21906,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.tables.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.validate_host.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -21913,7 +21920,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.tables.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.validate_host.validations", "type": "Array", "tags": [], "label": "validations", @@ -21927,23 +21934,39 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.tables.value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.configuration.validate_host.value", + "type": "boolean", "tags": [], "label": "value", "description": [], + "signature": [ + "false" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false } ] - }, + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.features", + "type": "Object", + "tags": [], + "label": "features", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_enabled", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.features.FeatureName.SYNC_RULES", "type": "Object", "tags": [], - "label": "ssl_enabled", + "label": "[FeatureName.SYNC_RULES]", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -21951,21 +21974,133 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_enabled.default_value", - "type": "boolean", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.features.FeatureName.SYNC_RULES.advanced", + "type": "Object", + "tags": [], + "label": "advanced", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.features.FeatureName.SYNC_RULES.advanced.enabled", + "type": "boolean", + "tags": [], + "label": "enabled", + "description": [], + "signature": [ + "false" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.features.FeatureName.SYNC_RULES.basic", + "type": "Object", + "tags": [], + "label": "basic", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.features.FeatureName.SYNC_RULES.basic.enabled", + "type": "boolean", + "tags": [], + "label": "enabled", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ] + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mssql.serviceType", + "type": "string", + "tags": [], + "label": "serviceType", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql", + "type": "Object", + "tags": [], + "label": "mysql", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration", + "type": "Object", + "tags": [], + "label": "configuration", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.host", + "type": "Object", + "tags": [], + "label": "host", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.host.default_value", + "type": "string", "tags": [], "label": "default_value", "description": [], - "signature": [ - "false" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_enabled.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.host.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -21979,7 +22114,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_enabled.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.host.display", "type": "string", "tags": [], "label": "display", @@ -21992,7 +22127,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TOGGLE" + ".TEXTBOX" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -22000,7 +22135,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_enabled.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.host.label", "type": "string", "tags": [], "label": "label", @@ -22011,7 +22146,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_enabled.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.host.options", "type": "Array", "tags": [], "label": "options", @@ -22025,7 +22160,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_enabled.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.host.order", "type": "number", "tags": [], "label": "order", @@ -22036,7 +22171,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_enabled.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.host.required", "type": "boolean", "tags": [], "label": "required", @@ -22050,7 +22185,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_enabled.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.host.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -22064,7 +22199,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_enabled.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.host.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -22075,7 +22210,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_enabled.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.host.type", "type": "string", "tags": [], "label": "type", @@ -22088,7 +22223,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".BOOLEAN" + ".STRING" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -22096,7 +22231,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_enabled.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.host.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -22110,7 +22245,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_enabled.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.host.validations", "type": "Array", "tags": [], "label": "validations", @@ -22124,14 +22259,11 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_enabled.value", - "type": "boolean", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.host.value", + "type": "string", "tags": [], "label": "value", "description": [], - "signature": [ - "false" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false @@ -22140,10 +22272,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_ca", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.port", "type": "Object", "tags": [], - "label": "ssl_ca", + "label": "port", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -22151,24 +22283,27 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_ca.default_value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.port.default_value", + "type": "Uncategorized", "tags": [], "label": "default_value", "description": [], + "signature": [ + "null" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_ca.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.port.depends_on", "type": "Array", "tags": [], "label": "depends_on", "description": [], "signature": [ - "{ field: string; value: true; }[]" + "never[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -22176,7 +22311,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_ca.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.port.display", "type": "string", "tags": [], "label": "display", @@ -22189,7 +22324,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TEXTBOX" + ".NUMERIC" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -22197,7 +22332,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_ca.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.port.label", "type": "string", "tags": [], "label": "label", @@ -22208,7 +22343,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_ca.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.port.options", "type": "Array", "tags": [], "label": "options", @@ -22222,7 +22357,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_ca.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.port.order", "type": "number", "tags": [], "label": "order", @@ -22233,7 +22368,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_ca.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.port.required", "type": "boolean", "tags": [], "label": "required", @@ -22247,7 +22382,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_ca.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.port.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -22261,7 +22396,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_ca.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.port.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -22272,7 +22407,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_ca.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.port.type", "type": "string", "tags": [], "label": "type", @@ -22285,7 +22420,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".STRING" + ".INTEGER" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -22293,7 +22428,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_ca.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.port.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -22307,7 +22442,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_ca.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.port.validations", "type": "Array", "tags": [], "label": "validations", @@ -22321,7 +22456,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_ca.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.port.value", "type": "string", "tags": [], "label": "value", @@ -22334,10 +22469,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.fetch_size", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.user", "type": "Object", "tags": [], - "label": "fetch_size", + "label": "user", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -22345,8 +22480,8 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.fetch_size.default_value", - "type": "number", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.user.default_value", + "type": "string", "tags": [], "label": "default_value", "description": [], @@ -22356,7 +22491,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.fetch_size.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.user.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -22370,7 +22505,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.fetch_size.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.user.display", "type": "string", "tags": [], "label": "display", @@ -22383,7 +22518,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".NUMERIC" + ".TEXTBOX" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -22391,7 +22526,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.fetch_size.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.user.label", "type": "string", "tags": [], "label": "label", @@ -22402,7 +22537,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.fetch_size.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.user.options", "type": "Array", "tags": [], "label": "options", @@ -22416,7 +22551,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.fetch_size.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.user.order", "type": "number", "tags": [], "label": "order", @@ -22427,7 +22562,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.fetch_size.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.user.required", "type": "boolean", "tags": [], "label": "required", @@ -22441,7 +22576,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.fetch_size.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.user.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -22455,7 +22590,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.fetch_size.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.user.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -22466,7 +22601,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.fetch_size.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.user.type", "type": "string", "tags": [], "label": "type", @@ -22479,7 +22614,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".INTEGER" + ".STRING" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -22487,13 +22622,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.fetch_size.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.user.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", "description": [], "signature": [ - "string[]" + "never[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -22501,7 +22636,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.fetch_size.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.user.validations", "type": "Array", "tags": [], "label": "validations", @@ -22515,8 +22650,8 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.fetch_size.value", - "type": "number", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.user.value", + "type": "string", "tags": [], "label": "value", "description": [], @@ -22528,10 +22663,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.retry_count", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.password", "type": "Object", "tags": [], - "label": "retry_count", + "label": "password", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -22539,8 +22674,8 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.retry_count.default_value", - "type": "number", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.password.default_value", + "type": "string", "tags": [], "label": "default_value", "description": [], @@ -22550,7 +22685,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.retry_count.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.password.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -22564,7 +22699,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.retry_count.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.password.display", "type": "string", "tags": [], "label": "display", @@ -22577,7 +22712,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".NUMERIC" + ".TEXTBOX" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -22585,7 +22720,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.retry_count.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.password.label", "type": "string", "tags": [], "label": "label", @@ -22596,7 +22731,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.retry_count.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.password.options", "type": "Array", "tags": [], "label": "options", @@ -22610,7 +22745,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.retry_count.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.password.order", "type": "number", "tags": [], "label": "order", @@ -22621,7 +22756,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.retry_count.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.password.required", "type": "boolean", "tags": [], "label": "required", @@ -22635,13 +22770,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.retry_count.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.password.sensitive", "type": "boolean", "tags": [], "label": "sensitive", "description": [], "signature": [ - "false" + "true" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -22649,7 +22784,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.retry_count.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.password.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -22660,7 +22795,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.retry_count.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.password.type", "type": "string", "tags": [], "label": "type", @@ -22673,7 +22808,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".INTEGER" + ".STRING" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -22681,13 +22816,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.retry_count.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.password.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", "description": [], "signature": [ - "string[]" + "never[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -22695,7 +22830,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.retry_count.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.password.validations", "type": "Array", "tags": [], "label": "validations", @@ -22709,8 +22844,8 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.retry_count.value", - "type": "number", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.password.value", + "type": "string", "tags": [], "label": "value", "description": [], @@ -22719,26 +22854,13 @@ "trackAdoption": false } ] - } - ] - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.features", - "type": "Object", - "tags": [], - "label": "features", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ + }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.features.FeatureName.SYNC_RULES", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.database", "type": "Object", "tags": [], - "label": "[FeatureName.SYNC_RULES]", + "label": "database", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -22746,114 +22868,3626 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.features.FeatureName.SYNC_RULES.advanced", - "type": "Object", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.database.default_value", + "type": "string", "tags": [], - "label": "advanced", + "label": "default_value", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.features.FeatureName.SYNC_RULES.advanced.enabled", - "type": "boolean", - "tags": [], - "label": "enabled", - "description": [], - "signature": [ - "true" - ], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false - } - ] + "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.features.FeatureName.SYNC_RULES.basic", - "type": "Object", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.database.depends_on", + "type": "Array", "tags": [], - "label": "basic", + "label": "depends_on", "description": [], + "signature": [ + "never[]" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, - "trackAdoption": false, - "children": [ + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.database.display", + "type": "string", + "tags": [], + "label": "display", + "description": [], + "signature": [ { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.features.FeatureName.SYNC_RULES.basic.enabled", - "type": "boolean", - "tags": [], - "label": "enabled", - "description": [], - "signature": [ - "true" - ], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false - } - ] - } - ] - } - ] - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.name", - "type": "string", - "tags": [], - "label": "name", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.serviceType", - "type": "string", - "tags": [], - "label": "serviceType", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false - } - ] - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive", - "type": "Object", - "tags": [], - "label": "network_drive", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration", - "type": "Object", - "tags": [], - "label": "configuration", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.DisplayType", + "text": "DisplayType" + }, + ".TEXTBOX" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.database.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.database.options", + "type": "Array", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.database.order", + "type": "number", + "tags": [], + "label": "order", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.database.required", + "type": "boolean", + "tags": [], + "label": "required", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.database.sensitive", + "type": "boolean", + "tags": [], + "label": "sensitive", + "description": [], + "signature": [ + "false" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.database.tooltip", + "type": "string", + "tags": [], + "label": "tooltip", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.database.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.FieldType", + "text": "FieldType" + }, + ".STRING" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.database.ui_restrictions", + "type": "Array", + "tags": [], + "label": "ui_restrictions", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.database.validations", + "type": "Array", + "tags": [], + "label": "validations", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.database.value", + "type": "string", + "tags": [], + "label": "value", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.tables", + "type": "Object", + "tags": [], + "label": "tables", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.tables.default_value", + "type": "string", + "tags": [], + "label": "default_value", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.tables.depends_on", + "type": "Array", + "tags": [], + "label": "depends_on", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.tables.display", + "type": "string", + "tags": [], + "label": "display", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.DisplayType", + "text": "DisplayType" + }, + ".TEXTAREA" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.tables.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.tables.options", + "type": "Array", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.tables.order", + "type": "number", + "tags": [], + "label": "order", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.tables.required", + "type": "boolean", + "tags": [], + "label": "required", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.tables.sensitive", + "type": "boolean", + "tags": [], + "label": "sensitive", + "description": [], + "signature": [ + "false" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.tables.tooltip", + "type": "string", + "tags": [], + "label": "tooltip", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.tables.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.FieldType", + "text": "FieldType" + }, + ".LIST" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.tables.ui_restrictions", + "type": "Array", + "tags": [], + "label": "ui_restrictions", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.tables.validations", + "type": "Array", + "tags": [], + "label": "validations", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.tables.value", + "type": "string", + "tags": [], + "label": "value", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_enabled", + "type": "Object", + "tags": [], + "label": "ssl_enabled", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_enabled.default_value", + "type": "boolean", + "tags": [], + "label": "default_value", + "description": [], + "signature": [ + "false" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_enabled.depends_on", + "type": "Array", + "tags": [], + "label": "depends_on", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_enabled.display", + "type": "string", + "tags": [], + "label": "display", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.DisplayType", + "text": "DisplayType" + }, + ".TOGGLE" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_enabled.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_enabled.options", + "type": "Array", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_enabled.order", + "type": "number", + "tags": [], + "label": "order", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_enabled.required", + "type": "boolean", + "tags": [], + "label": "required", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_enabled.sensitive", + "type": "boolean", + "tags": [], + "label": "sensitive", + "description": [], + "signature": [ + "false" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_enabled.tooltip", + "type": "string", + "tags": [], + "label": "tooltip", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_enabled.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.FieldType", + "text": "FieldType" + }, + ".BOOLEAN" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_enabled.ui_restrictions", + "type": "Array", + "tags": [], + "label": "ui_restrictions", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_enabled.validations", + "type": "Array", + "tags": [], + "label": "validations", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_enabled.value", + "type": "boolean", + "tags": [], + "label": "value", + "description": [], + "signature": [ + "false" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_ca", + "type": "Object", + "tags": [], + "label": "ssl_ca", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_ca.default_value", + "type": "string", + "tags": [], + "label": "default_value", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_ca.depends_on", + "type": "Array", + "tags": [], + "label": "depends_on", + "description": [], + "signature": [ + "{ field: string; value: true; }[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_ca.display", + "type": "string", + "tags": [], + "label": "display", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.DisplayType", + "text": "DisplayType" + }, + ".TEXTBOX" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_ca.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_ca.options", + "type": "Array", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_ca.order", + "type": "number", + "tags": [], + "label": "order", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_ca.required", + "type": "boolean", + "tags": [], + "label": "required", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_ca.sensitive", + "type": "boolean", + "tags": [], + "label": "sensitive", + "description": [], + "signature": [ + "false" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_ca.tooltip", + "type": "string", + "tags": [], + "label": "tooltip", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_ca.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.FieldType", + "text": "FieldType" + }, + ".STRING" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_ca.ui_restrictions", + "type": "Array", + "tags": [], + "label": "ui_restrictions", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_ca.validations", + "type": "Array", + "tags": [], + "label": "validations", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.ssl_ca.value", + "type": "string", + "tags": [], + "label": "value", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.fetch_size", + "type": "Object", + "tags": [], + "label": "fetch_size", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.fetch_size.default_value", + "type": "number", + "tags": [], + "label": "default_value", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.fetch_size.depends_on", + "type": "Array", + "tags": [], + "label": "depends_on", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.fetch_size.display", + "type": "string", + "tags": [], + "label": "display", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.DisplayType", + "text": "DisplayType" + }, + ".NUMERIC" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.fetch_size.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.fetch_size.options", + "type": "Array", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.fetch_size.order", + "type": "number", + "tags": [], + "label": "order", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.fetch_size.required", + "type": "boolean", + "tags": [], + "label": "required", + "description": [], + "signature": [ + "false" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.fetch_size.sensitive", + "type": "boolean", + "tags": [], + "label": "sensitive", + "description": [], + "signature": [ + "false" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.fetch_size.tooltip", + "type": "string", + "tags": [], + "label": "tooltip", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.fetch_size.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.FieldType", + "text": "FieldType" + }, + ".INTEGER" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.fetch_size.ui_restrictions", + "type": "Array", + "tags": [], + "label": "ui_restrictions", + "description": [], + "signature": [ + "string[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.fetch_size.validations", + "type": "Array", + "tags": [], + "label": "validations", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.fetch_size.value", + "type": "number", + "tags": [], + "label": "value", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.retry_count", + "type": "Object", + "tags": [], + "label": "retry_count", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.retry_count.default_value", + "type": "number", + "tags": [], + "label": "default_value", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.retry_count.depends_on", + "type": "Array", + "tags": [], + "label": "depends_on", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.retry_count.display", + "type": "string", + "tags": [], + "label": "display", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.DisplayType", + "text": "DisplayType" + }, + ".NUMERIC" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.retry_count.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.retry_count.options", + "type": "Array", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.retry_count.order", + "type": "number", + "tags": [], + "label": "order", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.retry_count.required", + "type": "boolean", + "tags": [], + "label": "required", + "description": [], + "signature": [ + "false" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.retry_count.sensitive", + "type": "boolean", + "tags": [], + "label": "sensitive", + "description": [], + "signature": [ + "false" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.retry_count.tooltip", + "type": "string", + "tags": [], + "label": "tooltip", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.retry_count.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.FieldType", + "text": "FieldType" + }, + ".INTEGER" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.retry_count.ui_restrictions", + "type": "Array", + "tags": [], + "label": "ui_restrictions", + "description": [], + "signature": [ + "string[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.retry_count.validations", + "type": "Array", + "tags": [], + "label": "validations", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.configuration.retry_count.value", + "type": "number", + "tags": [], + "label": "value", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.features", + "type": "Object", + "tags": [], + "label": "features", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.features.FeatureName.SYNC_RULES", + "type": "Object", + "tags": [], + "label": "[FeatureName.SYNC_RULES]", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.features.FeatureName.SYNC_RULES.advanced", + "type": "Object", + "tags": [], + "label": "advanced", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.features.FeatureName.SYNC_RULES.advanced.enabled", + "type": "boolean", + "tags": [], + "label": "enabled", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.features.FeatureName.SYNC_RULES.basic", + "type": "Object", + "tags": [], + "label": "basic", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.features.FeatureName.SYNC_RULES.basic.enabled", + "type": "boolean", + "tags": [], + "label": "enabled", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ] + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.mysql.serviceType", + "type": "string", + "tags": [], + "label": "serviceType", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive", + "type": "Object", + "tags": [], + "label": "network_drive", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration", + "type": "Object", + "tags": [], + "label": "configuration", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.username", + "type": "Object", + "tags": [], + "label": "username", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.username.default_value", + "type": "Uncategorized", + "tags": [], + "label": "default_value", + "description": [], + "signature": [ + "null" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.username.depends_on", + "type": "Array", + "tags": [], + "label": "depends_on", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.username.display", + "type": "string", + "tags": [], + "label": "display", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.DisplayType", + "text": "DisplayType" + }, + ".TEXTBOX" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.username.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.username.options", + "type": "Array", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.username.order", + "type": "number", + "tags": [], + "label": "order", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.username.required", + "type": "boolean", + "tags": [], + "label": "required", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.username.sensitive", + "type": "boolean", + "tags": [], + "label": "sensitive", + "description": [], + "signature": [ + "false" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.username.tooltip", + "type": "string", + "tags": [], + "label": "tooltip", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.username.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.FieldType", + "text": "FieldType" + }, + ".STRING" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.username.ui_restrictions", + "type": "Array", + "tags": [], + "label": "ui_restrictions", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.username.validations", + "type": "Array", + "tags": [], + "label": "validations", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.username.value", + "type": "string", + "tags": [], + "label": "value", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.password", + "type": "Object", + "tags": [], + "label": "password", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.password.default_value", + "type": "Uncategorized", + "tags": [], + "label": "default_value", + "description": [], + "signature": [ + "null" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.password.depends_on", + "type": "Array", + "tags": [], + "label": "depends_on", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.password.display", + "type": "string", + "tags": [], + "label": "display", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.DisplayType", + "text": "DisplayType" + }, + ".TEXTBOX" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.password.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.password.options", + "type": "Array", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.password.order", + "type": "number", + "tags": [], + "label": "order", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.password.required", + "type": "boolean", + "tags": [], + "label": "required", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.password.sensitive", + "type": "boolean", + "tags": [], + "label": "sensitive", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.password.tooltip", + "type": "string", + "tags": [], + "label": "tooltip", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.password.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.FieldType", + "text": "FieldType" + }, + ".STRING" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.password.ui_restrictions", + "type": "Array", + "tags": [], + "label": "ui_restrictions", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.password.validations", + "type": "Array", + "tags": [], + "label": "validations", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.password.value", + "type": "string", + "tags": [], + "label": "value", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_ip", + "type": "Object", + "tags": [], + "label": "server_ip", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_ip.default_value", + "type": "Uncategorized", + "tags": [], + "label": "default_value", + "description": [], + "signature": [ + "null" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_ip.depends_on", + "type": "Array", + "tags": [], + "label": "depends_on", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_ip.display", + "type": "string", + "tags": [], + "label": "display", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.DisplayType", + "text": "DisplayType" + }, + ".TEXTBOX" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_ip.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_ip.options", + "type": "Array", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_ip.order", + "type": "number", + "tags": [], + "label": "order", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_ip.placeholder", + "type": "string", + "tags": [], + "label": "placeholder", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_ip.required", + "type": "boolean", + "tags": [], + "label": "required", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_ip.sensitive", + "type": "boolean", + "tags": [], + "label": "sensitive", + "description": [], + "signature": [ + "false" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_ip.tooltip", + "type": "string", + "tags": [], + "label": "tooltip", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_ip.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.FieldType", + "text": "FieldType" + }, + ".STRING" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_ip.ui_restrictions", + "type": "Array", + "tags": [], + "label": "ui_restrictions", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_ip.validations", + "type": "Array", + "tags": [], + "label": "validations", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_ip.value", + "type": "string", + "tags": [], + "label": "value", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_port", + "type": "Object", + "tags": [], + "label": "server_port", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_port.default_value", + "type": "Uncategorized", + "tags": [], + "label": "default_value", + "description": [], + "signature": [ + "null" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_port.depends_on", + "type": "Array", + "tags": [], + "label": "depends_on", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_port.display", + "type": "string", + "tags": [], + "label": "display", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.DisplayType", + "text": "DisplayType" + }, + ".NUMERIC" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_port.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_port.options", + "type": "Array", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_port.order", + "type": "number", + "tags": [], + "label": "order", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_port.required", + "type": "boolean", + "tags": [], + "label": "required", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_port.sensitive", + "type": "boolean", + "tags": [], + "label": "sensitive", + "description": [], + "signature": [ + "false" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_port.tooltip", + "type": "string", + "tags": [], + "label": "tooltip", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_port.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.FieldType", + "text": "FieldType" + }, + ".INTEGER" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_port.ui_restrictions", + "type": "Array", + "tags": [], + "label": "ui_restrictions", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_port.validations", + "type": "Array", + "tags": [], + "label": "validations", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_port.value", + "type": "number", + "tags": [], + "label": "value", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.drive_path", + "type": "Object", + "tags": [], + "label": "drive_path", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.drive_path.default_value", + "type": "Uncategorized", + "tags": [], + "label": "default_value", + "description": [], + "signature": [ + "null" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.drive_path.depends_on", + "type": "Array", + "tags": [], + "label": "depends_on", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.drive_path.display", + "type": "string", + "tags": [], + "label": "display", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.DisplayType", + "text": "DisplayType" + }, + ".TEXTBOX" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.drive_path.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.drive_path.options", + "type": "Array", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.drive_path.order", + "type": "number", + "tags": [], + "label": "order", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.drive_path.placeholder", + "type": "string", + "tags": [], + "label": "placeholder", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.drive_path.required", + "type": "boolean", + "tags": [], + "label": "required", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.drive_path.sensitive", + "type": "boolean", + "tags": [], + "label": "sensitive", + "description": [], + "signature": [ + "false" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.drive_path.tooltip", + "type": "string", + "tags": [], + "label": "tooltip", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.drive_path.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.FieldType", + "text": "FieldType" + }, + ".STRING" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.drive_path.ui_restrictions", + "type": "Array", + "tags": [], + "label": "ui_restrictions", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.drive_path.validations", + "type": "Array", + "tags": [], + "label": "validations", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.drive_path.value", + "type": "string", + "tags": [], + "label": "value", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.use_document_level_security", + "type": "Object", + "tags": [], + "label": "use_document_level_security", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.use_document_level_security.default_value", + "type": "Uncategorized", + "tags": [], + "label": "default_value", + "description": [], + "signature": [ + "null" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.use_document_level_security.depends_on", + "type": "Array", + "tags": [], + "label": "depends_on", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.use_document_level_security.display", + "type": "string", + "tags": [], + "label": "display", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.DisplayType", + "text": "DisplayType" + }, + ".TOGGLE" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.use_document_level_security.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.use_document_level_security.options", + "type": "Array", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.use_document_level_security.order", + "type": "number", + "tags": [], + "label": "order", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.use_document_level_security.required", + "type": "boolean", + "tags": [], + "label": "required", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.use_document_level_security.sensitive", + "type": "boolean", + "tags": [], + "label": "sensitive", + "description": [], + "signature": [ + "false" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.use_document_level_security.tooltip", + "type": "string", + "tags": [], + "label": "tooltip", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.use_document_level_security.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.FieldType", + "text": "FieldType" + }, + ".BOOLEAN" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.use_document_level_security.ui_restrictions", + "type": "Array", + "tags": [], + "label": "ui_restrictions", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.use_document_level_security.validations", + "type": "Array", + "tags": [], + "label": "validations", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.use_document_level_security.value", + "type": "boolean", + "tags": [], + "label": "value", + "description": [], + "signature": [ + "false" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.features", + "type": "Object", + "tags": [], + "label": "features", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.features.FeatureName.SYNC_RULES", + "type": "Object", + "tags": [], + "label": "[FeatureName.SYNC_RULES]", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.features.FeatureName.SYNC_RULES.advanced", + "type": "Object", + "tags": [], + "label": "advanced", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.features.FeatureName.SYNC_RULES.advanced.enabled", + "type": "boolean", + "tags": [], + "label": "enabled", + "description": [], + "signature": [ + "false" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.features.FeatureName.SYNC_RULES.basic", + "type": "Object", + "tags": [], + "label": "basic", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.features.FeatureName.SYNC_RULES.basic.enabled", + "type": "boolean", + "tags": [], + "label": "enabled", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ] + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.serviceType", + "type": "string", + "tags": [], + "label": "serviceType", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive", + "type": "Object", + "tags": [], + "label": "onedrive", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration", + "type": "Object", + "tags": [], + "label": "configuration", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_id", + "type": "Object", + "tags": [], + "label": "client_id", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_id.default_value", + "type": "Uncategorized", + "tags": [], + "label": "default_value", + "description": [], + "signature": [ + "null" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_id.depends_on", + "type": "Array", + "tags": [], + "label": "depends_on", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_id.display", + "type": "string", + "tags": [], + "label": "display", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.DisplayType", + "text": "DisplayType" + }, + ".TEXTBOX" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_id.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_id.options", + "type": "Array", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_id.order", + "type": "number", + "tags": [], + "label": "order", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_id.required", + "type": "boolean", + "tags": [], + "label": "required", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_id.sensitive", + "type": "boolean", + "tags": [], + "label": "sensitive", + "description": [], + "signature": [ + "false" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_id.tooltip", + "type": "Uncategorized", + "tags": [], + "label": "tooltip", + "description": [], + "signature": [ + "null" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_id.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.FieldType", + "text": "FieldType" + }, + ".STRING" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_id.ui_restrictions", + "type": "Array", + "tags": [], + "label": "ui_restrictions", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_id.validations", + "type": "Array", + "tags": [], + "label": "validations", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_id.value", + "type": "string", + "tags": [], + "label": "value", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_secret", + "type": "Object", + "tags": [], + "label": "client_secret", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_secret.default_value", + "type": "Uncategorized", + "tags": [], + "label": "default_value", + "description": [], + "signature": [ + "null" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_secret.depends_on", + "type": "Array", + "tags": [], + "label": "depends_on", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_secret.display", + "type": "string", + "tags": [], + "label": "display", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.DisplayType", + "text": "DisplayType" + }, + ".TEXTBOX" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_secret.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_secret.options", + "type": "Array", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_secret.order", + "type": "number", + "tags": [], + "label": "order", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_secret.required", + "type": "boolean", + "tags": [], + "label": "required", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_secret.sensitive", + "type": "boolean", + "tags": [], + "label": "sensitive", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_secret.tooltip", + "type": "Uncategorized", + "tags": [], + "label": "tooltip", + "description": [], + "signature": [ + "null" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_secret.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.FieldType", + "text": "FieldType" + }, + ".STRING" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_secret.ui_restrictions", + "type": "Array", + "tags": [], + "label": "ui_restrictions", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_secret.validations", + "type": "Array", + "tags": [], + "label": "validations", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_secret.value", + "type": "string", + "tags": [], + "label": "value", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.tenant_id", + "type": "Object", + "tags": [], + "label": "tenant_id", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.tenant_id.default_value", + "type": "Uncategorized", + "tags": [], + "label": "default_value", + "description": [], + "signature": [ + "null" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.tenant_id.depends_on", + "type": "Array", + "tags": [], + "label": "depends_on", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.tenant_id.display", + "type": "string", + "tags": [], + "label": "display", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.DisplayType", + "text": "DisplayType" + }, + ".TEXTBOX" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.tenant_id.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.tenant_id.options", + "type": "Array", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.tenant_id.order", + "type": "number", + "tags": [], + "label": "order", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.tenant_id.required", + "type": "boolean", + "tags": [], + "label": "required", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.tenant_id.sensitive", + "type": "boolean", + "tags": [], + "label": "sensitive", + "description": [], + "signature": [ + "false" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.tenant_id.tooltip", + "type": "Uncategorized", + "tags": [], + "label": "tooltip", + "description": [], + "signature": [ + "null" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.tenant_id.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.FieldType", + "text": "FieldType" + }, + ".STRING" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.tenant_id.ui_restrictions", + "type": "Array", + "tags": [], + "label": "ui_restrictions", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.tenant_id.validations", + "type": "Array", + "tags": [], + "label": "validations", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.tenant_id.value", + "type": "string", + "tags": [], + "label": "value", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.retry_count", + "type": "Object", + "tags": [], + "label": "retry_count", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.retry_count.default_value", + "type": "number", + "tags": [], + "label": "default_value", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.retry_count.depends_on", + "type": "Array", + "tags": [], + "label": "depends_on", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.retry_count.display", + "type": "string", + "tags": [], + "label": "display", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.DisplayType", + "text": "DisplayType" + }, + ".NUMERIC" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.retry_count.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.retry_count.options", + "type": "Array", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.retry_count.order", + "type": "number", + "tags": [], + "label": "order", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.retry_count.required", + "type": "boolean", + "tags": [], + "label": "required", + "description": [], + "signature": [ + "false" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.retry_count.sensitive", + "type": "boolean", + "tags": [], + "label": "sensitive", + "description": [], + "signature": [ + "false" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.retry_count.tooltip", + "type": "Uncategorized", + "tags": [], + "label": "tooltip", + "description": [], + "signature": [ + "null" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.retry_count.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.FieldType", + "text": "FieldType" + }, + ".INTEGER" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.retry_count.ui_restrictions", + "type": "Array", + "tags": [], + "label": "ui_restrictions", + "description": [], + "signature": [ + "string[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.retry_count.validations", + "type": "Array", + "tags": [], + "label": "validations", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.retry_count.value", + "type": "string", + "tags": [], + "label": "value", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.concurrent_downloads", + "type": "Object", + "tags": [], + "label": "concurrent_downloads", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.concurrent_downloads.default_value", + "type": "number", + "tags": [], + "label": "default_value", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.concurrent_downloads.depends_on", + "type": "Array", + "tags": [], + "label": "depends_on", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.concurrent_downloads.display", + "type": "string", + "tags": [], + "label": "display", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.DisplayType", + "text": "DisplayType" + }, + ".NUMERIC" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.concurrent_downloads.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.concurrent_downloads.options", + "type": "Array", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.concurrent_downloads.order", + "type": "number", + "tags": [], + "label": "order", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.concurrent_downloads.required", + "type": "boolean", + "tags": [], + "label": "required", + "description": [], + "signature": [ + "false" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.concurrent_downloads.sensitive", + "type": "boolean", + "tags": [], + "label": "sensitive", + "description": [], + "signature": [ + "false" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.concurrent_downloads.tooltip", + "type": "Uncategorized", + "tags": [], + "label": "tooltip", + "description": [], + "signature": [ + "null" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.concurrent_downloads.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.FieldType", + "text": "FieldType" + }, + ".INTEGER" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.concurrent_downloads.ui_restrictions", + "type": "Array", + "tags": [], + "label": "ui_restrictions", + "description": [], + "signature": [ + "string[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.concurrent_downloads.validations", + "type": "Array", + "tags": [], + "label": "validations", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.concurrent_downloads.value", + "type": "string", + "tags": [], + "label": "value", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.username", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_document_level_security", "type": "Object", "tags": [], - "label": "username", + "label": "use_document_level_security", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -22861,7 +26495,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.username.default_value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_document_level_security.default_value", "type": "Uncategorized", "tags": [], "label": "default_value", @@ -22875,7 +26509,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.username.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_document_level_security.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -22889,7 +26523,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.username.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_document_level_security.display", "type": "string", "tags": [], "label": "display", @@ -22902,7 +26536,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TEXTBOX" + ".TOGGLE" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -22910,7 +26544,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.username.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_document_level_security.label", "type": "string", "tags": [], "label": "label", @@ -22921,7 +26555,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.username.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_document_level_security.options", "type": "Array", "tags": [], "label": "options", @@ -22935,7 +26569,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.username.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_document_level_security.order", "type": "number", "tags": [], "label": "order", @@ -22946,7 +26580,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.username.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_document_level_security.required", "type": "boolean", "tags": [], "label": "required", @@ -22960,7 +26594,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.username.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_document_level_security.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -22974,7 +26608,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.username.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_document_level_security.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -22985,7 +26619,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.username.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_document_level_security.type", "type": "string", "tags": [], "label": "type", @@ -22998,7 +26632,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".STRING" + ".BOOLEAN" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -23006,7 +26640,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.username.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_document_level_security.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -23020,7 +26654,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.username.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_document_level_security.validations", "type": "Array", "tags": [], "label": "validations", @@ -23034,11 +26668,14 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.username.value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_document_level_security.value", + "type": "boolean", "tags": [], "label": "value", "description": [], + "signature": [ + "false" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false @@ -23047,10 +26684,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.password", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_text_extraction_service", "type": "Object", "tags": [], - "label": "password", + "label": "use_text_extraction_service", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -23058,7 +26695,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.password.default_value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_text_extraction_service.default_value", "type": "Uncategorized", "tags": [], "label": "default_value", @@ -23072,7 +26709,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.password.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_text_extraction_service.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -23086,7 +26723,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.password.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_text_extraction_service.display", "type": "string", "tags": [], "label": "display", @@ -23099,7 +26736,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TEXTBOX" + ".TOGGLE" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -23107,7 +26744,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.password.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_text_extraction_service.label", "type": "string", "tags": [], "label": "label", @@ -23118,7 +26755,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.password.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_text_extraction_service.options", "type": "Array", "tags": [], "label": "options", @@ -23132,7 +26769,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.password.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_text_extraction_service.order", "type": "number", "tags": [], "label": "order", @@ -23143,7 +26780,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.password.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_text_extraction_service.required", "type": "boolean", "tags": [], "label": "required", @@ -23157,13 +26794,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.password.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_text_extraction_service.sensitive", "type": "boolean", "tags": [], "label": "sensitive", "description": [], "signature": [ - "true" + "false" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -23171,7 +26808,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.password.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_text_extraction_service.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -23182,7 +26819,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.password.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_text_extraction_service.type", "type": "string", "tags": [], "label": "type", @@ -23195,7 +26832,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".STRING" + ".BOOLEAN" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -23203,13 +26840,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.password.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_text_extraction_service.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", "description": [], "signature": [ - "never[]" + "string[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -23217,7 +26854,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.password.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_text_extraction_service.validations", "type": "Array", "tags": [], "label": "validations", @@ -23231,23 +26868,114 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.password.value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_text_extraction_service.value", + "type": "boolean", "tags": [], "label": "value", "description": [], + "signature": [ + "false" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false } ] - }, + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.features", + "type": "Object", + "tags": [], + "label": "features", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.features.FeatureName.DOCUMENT_LEVEL_SECURITY", + "type": "Object", + "tags": [], + "label": "[FeatureName.DOCUMENT_LEVEL_SECURITY]", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.features.FeatureName.DOCUMENT_LEVEL_SECURITY.enabled", + "type": "boolean", + "tags": [], + "label": "enabled", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.serviceType", + "type": "string", + "tags": [], + "label": "serviceType", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle", + "type": "Object", + "tags": [], + "label": "oracle", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration", + "type": "Object", + "tags": [], + "label": "configuration", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_ip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.host", "type": "Object", "tags": [], - "label": "server_ip", + "label": "host", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -23255,21 +26983,18 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_ip.default_value", - "type": "Uncategorized", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.host.default_value", + "type": "string", "tags": [], "label": "default_value", "description": [], - "signature": [ - "null" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_ip.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.host.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -23283,7 +27008,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_ip.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.host.display", "type": "string", "tags": [], "label": "display", @@ -23304,7 +27029,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_ip.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.host.label", "type": "string", "tags": [], "label": "label", @@ -23315,7 +27040,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_ip.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.host.options", "type": "Array", "tags": [], "label": "options", @@ -23329,7 +27054,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_ip.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.host.order", "type": "number", "tags": [], "label": "order", @@ -23340,18 +27065,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_ip.placeholder", - "type": "string", - "tags": [], - "label": "placeholder", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_ip.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.host.required", "type": "boolean", "tags": [], "label": "required", @@ -23365,7 +27079,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_ip.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.host.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -23379,7 +27093,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_ip.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.host.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -23390,7 +27104,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_ip.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.host.type", "type": "string", "tags": [], "label": "type", @@ -23411,7 +27125,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_ip.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.host.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -23425,7 +27139,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_ip.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.host.validations", "type": "Array", "tags": [], "label": "validations", @@ -23439,7 +27153,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_ip.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.host.value", "type": "string", "tags": [], "label": "value", @@ -23452,10 +27166,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_port", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.port", "type": "Object", "tags": [], - "label": "server_port", + "label": "port", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -23463,7 +27177,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_port.default_value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.port.default_value", "type": "Uncategorized", "tags": [], "label": "default_value", @@ -23477,7 +27191,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_port.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.port.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -23491,7 +27205,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_port.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.port.display", "type": "string", "tags": [], "label": "display", @@ -23512,7 +27226,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_port.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.port.label", "type": "string", "tags": [], "label": "label", @@ -23523,7 +27237,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_port.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.port.options", "type": "Array", "tags": [], "label": "options", @@ -23537,7 +27251,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_port.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.port.order", "type": "number", "tags": [], "label": "order", @@ -23548,7 +27262,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_port.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.port.required", "type": "boolean", "tags": [], "label": "required", @@ -23562,7 +27276,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_port.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.port.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -23576,7 +27290,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_port.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.port.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -23587,7 +27301,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_port.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.port.type", "type": "string", "tags": [], "label": "type", @@ -23608,7 +27322,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_port.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.port.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -23622,7 +27336,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_port.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.port.validations", "type": "Array", "tags": [], "label": "validations", @@ -23636,8 +27350,8 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.server_port.value", - "type": "number", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.port.value", + "type": "string", "tags": [], "label": "value", "description": [], @@ -23649,10 +27363,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.drive_path", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.username", "type": "Object", "tags": [], - "label": "drive_path", + "label": "username", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -23660,21 +27374,18 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.drive_path.default_value", - "type": "Uncategorized", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.username.default_value", + "type": "string", "tags": [], "label": "default_value", "description": [], - "signature": [ - "null" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.drive_path.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.username.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -23688,7 +27399,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.drive_path.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.username.display", "type": "string", "tags": [], "label": "display", @@ -23709,7 +27420,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.drive_path.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.username.label", "type": "string", "tags": [], "label": "label", @@ -23720,7 +27431,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.drive_path.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.username.options", "type": "Array", "tags": [], "label": "options", @@ -23734,7 +27445,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.drive_path.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.username.order", "type": "number", "tags": [], "label": "order", @@ -23745,18 +27456,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.drive_path.placeholder", - "type": "string", - "tags": [], - "label": "placeholder", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.drive_path.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.username.required", "type": "boolean", "tags": [], "label": "required", @@ -23770,7 +27470,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.drive_path.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.username.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -23784,7 +27484,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.drive_path.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.username.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -23795,7 +27495,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.drive_path.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.username.type", "type": "string", "tags": [], "label": "type", @@ -23816,7 +27516,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.drive_path.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.username.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -23830,7 +27530,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.drive_path.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.username.validations", "type": "Array", "tags": [], "label": "validations", @@ -23844,7 +27544,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.drive_path.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.username.value", "type": "string", "tags": [], "label": "value", @@ -23857,10 +27557,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.use_document_level_security", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.password", "type": "Object", "tags": [], - "label": "use_document_level_security", + "label": "password", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -23868,21 +27568,18 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.use_document_level_security.default_value", - "type": "Uncategorized", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.password.default_value", + "type": "string", "tags": [], "label": "default_value", "description": [], - "signature": [ - "null" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.use_document_level_security.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.password.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -23896,7 +27593,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.use_document_level_security.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.password.display", "type": "string", "tags": [], "label": "display", @@ -23909,7 +27606,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TOGGLE" + ".TEXTBOX" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -23917,7 +27614,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.use_document_level_security.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.password.label", "type": "string", "tags": [], "label": "label", @@ -23928,7 +27625,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.use_document_level_security.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.password.options", "type": "Array", "tags": [], "label": "options", @@ -23942,7 +27639,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.use_document_level_security.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.password.order", "type": "number", "tags": [], "label": "order", @@ -23953,7 +27650,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.use_document_level_security.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.password.required", "type": "boolean", "tags": [], "label": "required", @@ -23967,13 +27664,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.use_document_level_security.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.password.sensitive", "type": "boolean", "tags": [], "label": "sensitive", "description": [], "signature": [ - "false" + "true" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -23981,7 +27678,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.use_document_level_security.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.password.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -23992,7 +27689,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.use_document_level_security.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.password.type", "type": "string", "tags": [], "label": "type", @@ -24005,7 +27702,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".BOOLEAN" + ".STRING" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -24013,7 +27710,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.use_document_level_security.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.password.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -24027,7 +27724,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.use_document_level_security.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.password.validations", "type": "Array", "tags": [], "label": "validations", @@ -24041,154 +27738,23 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.configuration.use_document_level_security.value", - "type": "boolean", - "tags": [], - "label": "value", - "description": [], - "signature": [ - "false" - ], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false - } - ] - } - ] - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.features", - "type": "Object", - "tags": [], - "label": "features", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.features.FeatureName.SYNC_RULES", - "type": "Object", - "tags": [], - "label": "[FeatureName.SYNC_RULES]", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.features.FeatureName.SYNC_RULES.advanced", - "type": "Object", - "tags": [], - "label": "advanced", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.features.FeatureName.SYNC_RULES.advanced.enabled", - "type": "boolean", - "tags": [], - "label": "enabled", - "description": [], - "signature": [ - "false" - ], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false - } - ] - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.features.FeatureName.SYNC_RULES.basic", - "type": "Object", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.password.value", + "type": "string", "tags": [], - "label": "basic", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.features.FeatureName.SYNC_RULES.basic.enabled", - "type": "boolean", - "tags": [], - "label": "enabled", - "description": [], - "signature": [ - "true" - ], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false - } - ] + "label": "value", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false } ] - } - ] - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.name", - "type": "string", - "tags": [], - "label": "name", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.network_drive.serviceType", - "type": "string", - "tags": [], - "label": "serviceType", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false - } - ] - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive", - "type": "Object", - "tags": [], - "label": "onedrive", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration", - "type": "Object", - "tags": [], - "label": "configuration", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ + }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_id", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.database", "type": "Object", "tags": [], - "label": "client_id", + "label": "database", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -24196,21 +27762,18 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_id.default_value", - "type": "Uncategorized", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.database.default_value", + "type": "string", "tags": [], "label": "default_value", "description": [], - "signature": [ - "null" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_id.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.database.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -24224,7 +27787,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_id.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.database.display", "type": "string", "tags": [], "label": "display", @@ -24245,7 +27808,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_id.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.database.label", "type": "string", "tags": [], "label": "label", @@ -24256,7 +27819,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_id.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.database.options", "type": "Array", "tags": [], "label": "options", @@ -24270,7 +27833,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_id.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.database.order", "type": "number", "tags": [], "label": "order", @@ -24281,7 +27844,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_id.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.database.required", "type": "boolean", "tags": [], "label": "required", @@ -24295,7 +27858,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_id.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.database.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -24309,21 +27872,18 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_id.tooltip", - "type": "Uncategorized", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.database.tooltip", + "type": "string", "tags": [], "label": "tooltip", "description": [], - "signature": [ - "null" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_id.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.database.type", "type": "string", "tags": [], "label": "type", @@ -24344,7 +27904,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_id.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.database.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -24358,7 +27918,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_id.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.database.validations", "type": "Array", "tags": [], "label": "validations", @@ -24372,7 +27932,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_id.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.database.value", "type": "string", "tags": [], "label": "value", @@ -24385,10 +27945,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_secret", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.tables", "type": "Object", "tags": [], - "label": "client_secret", + "label": "tables", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -24396,21 +27956,18 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_secret.default_value", - "type": "Uncategorized", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.tables.default_value", + "type": "string", "tags": [], "label": "default_value", "description": [], - "signature": [ - "null" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_secret.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.tables.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -24424,7 +27981,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_secret.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.tables.display", "type": "string", "tags": [], "label": "display", @@ -24437,7 +27994,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TEXTBOX" + ".TEXTAREA" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -24445,7 +28002,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_secret.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.tables.label", "type": "string", "tags": [], "label": "label", @@ -24456,7 +28013,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_secret.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.tables.options", "type": "Array", "tags": [], "label": "options", @@ -24470,7 +28027,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_secret.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.tables.order", "type": "number", "tags": [], "label": "order", @@ -24481,7 +28038,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_secret.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.tables.required", "type": "boolean", "tags": [], "label": "required", @@ -24495,13 +28052,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_secret.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.tables.sensitive", "type": "boolean", "tags": [], "label": "sensitive", "description": [], "signature": [ - "true" + "false" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -24509,21 +28066,18 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_secret.tooltip", - "type": "Uncategorized", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.tables.tooltip", + "type": "string", "tags": [], "label": "tooltip", "description": [], - "signature": [ - "null" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_secret.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.tables.type", "type": "string", "tags": [], "label": "type", @@ -24536,7 +28090,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".STRING" + ".LIST" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -24544,7 +28098,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_secret.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.tables.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -24558,7 +28112,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_secret.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.tables.validations", "type": "Array", "tags": [], "label": "validations", @@ -24572,7 +28126,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.client_secret.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.tables.value", "type": "string", "tags": [], "label": "value", @@ -24585,10 +28139,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.tenant_id", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.fetch_size", "type": "Object", "tags": [], - "label": "tenant_id", + "label": "fetch_size", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -24596,21 +28150,18 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.tenant_id.default_value", - "type": "Uncategorized", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.fetch_size.default_value", + "type": "number", "tags": [], "label": "default_value", "description": [], - "signature": [ - "null" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.tenant_id.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.fetch_size.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -24624,7 +28175,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.tenant_id.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.fetch_size.display", "type": "string", "tags": [], "label": "display", @@ -24637,7 +28188,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TEXTBOX" + ".NUMERIC" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -24645,7 +28196,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.tenant_id.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.fetch_size.label", "type": "string", "tags": [], "label": "label", @@ -24656,7 +28207,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.tenant_id.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.fetch_size.options", "type": "Array", "tags": [], "label": "options", @@ -24670,7 +28221,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.tenant_id.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.fetch_size.order", "type": "number", "tags": [], "label": "order", @@ -24681,13 +28232,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.tenant_id.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.fetch_size.required", "type": "boolean", "tags": [], "label": "required", "description": [], "signature": [ - "true" + "false" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -24695,7 +28246,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.tenant_id.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.fetch_size.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -24709,21 +28260,18 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.tenant_id.tooltip", - "type": "Uncategorized", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.fetch_size.tooltip", + "type": "string", "tags": [], "label": "tooltip", "description": [], - "signature": [ - "null" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.tenant_id.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.fetch_size.type", "type": "string", "tags": [], "label": "type", @@ -24736,7 +28284,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".STRING" + ".INTEGER" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -24744,13 +28292,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.tenant_id.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.fetch_size.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", "description": [], "signature": [ - "never[]" + "string[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -24758,7 +28306,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.tenant_id.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.fetch_size.validations", "type": "Array", "tags": [], "label": "validations", @@ -24772,8 +28320,8 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.tenant_id.value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.fetch_size.value", + "type": "number", "tags": [], "label": "value", "description": [], @@ -24785,7 +28333,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.retry_count", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.retry_count", "type": "Object", "tags": [], "label": "retry_count", @@ -24796,7 +28344,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.retry_count.default_value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.retry_count.default_value", "type": "number", "tags": [], "label": "default_value", @@ -24807,7 +28355,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.retry_count.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.retry_count.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -24821,7 +28369,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.retry_count.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.retry_count.display", "type": "string", "tags": [], "label": "display", @@ -24842,7 +28390,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.retry_count.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.retry_count.label", "type": "string", "tags": [], "label": "label", @@ -24853,7 +28401,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.retry_count.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.retry_count.options", "type": "Array", "tags": [], "label": "options", @@ -24867,7 +28415,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.retry_count.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.retry_count.order", "type": "number", "tags": [], "label": "order", @@ -24878,7 +28426,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.retry_count.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.retry_count.required", "type": "boolean", "tags": [], "label": "required", @@ -24892,7 +28440,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.retry_count.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.retry_count.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -24906,21 +28454,18 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.retry_count.tooltip", - "type": "Uncategorized", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.retry_count.tooltip", + "type": "string", "tags": [], "label": "tooltip", "description": [], - "signature": [ - "null" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.retry_count.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.retry_count.type", "type": "string", "tags": [], "label": "type", @@ -24941,7 +28486,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.retry_count.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.retry_count.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -24955,7 +28500,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.retry_count.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.retry_count.validations", "type": "Array", "tags": [], "label": "validations", @@ -24969,8 +28514,8 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.retry_count.value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.retry_count.value", + "type": "number", "tags": [], "label": "value", "description": [], @@ -24982,10 +28527,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.concurrent_downloads", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_protocol", "type": "Object", "tags": [], - "label": "concurrent_downloads", + "label": "oracle_protocol", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -24993,8 +28538,8 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.concurrent_downloads.default_value", - "type": "number", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_protocol.default_value", + "type": "string", "tags": [], "label": "default_value", "description": [], @@ -25004,7 +28549,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.concurrent_downloads.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_protocol.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -25018,7 +28563,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.concurrent_downloads.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_protocol.display", "type": "string", "tags": [], "label": "display", @@ -25031,7 +28576,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".NUMERIC" + ".DROPDOWN" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -25039,7 +28584,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.concurrent_downloads.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_protocol.label", "type": "string", "tags": [], "label": "label", @@ -25050,13 +28595,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.concurrent_downloads.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_protocol.options", "type": "Array", "tags": [], "label": "options", "description": [], "signature": [ - "never[]" + "{ label: string; value: string; }[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -25064,7 +28609,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.concurrent_downloads.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_protocol.order", "type": "number", "tags": [], "label": "order", @@ -25075,13 +28620,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.concurrent_downloads.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_protocol.required", "type": "boolean", "tags": [], "label": "required", "description": [], "signature": [ - "false" + "true" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -25089,7 +28634,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.concurrent_downloads.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_protocol.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -25103,21 +28648,18 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.concurrent_downloads.tooltip", - "type": "Uncategorized", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_protocol.tooltip", + "type": "string", "tags": [], "label": "tooltip", "description": [], - "signature": [ - "null" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.concurrent_downloads.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_protocol.type", "type": "string", "tags": [], "label": "type", @@ -25130,7 +28672,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".INTEGER" + ".STRING" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -25138,7 +28680,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.concurrent_downloads.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_protocol.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -25152,7 +28694,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.concurrent_downloads.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_protocol.validations", "type": "Array", "tags": [], "label": "validations", @@ -25166,7 +28708,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.concurrent_downloads.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_protocol.value", "type": "string", "tags": [], "label": "value", @@ -25179,10 +28721,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_document_level_security", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_home", "type": "Object", "tags": [], - "label": "use_document_level_security", + "label": "oracle_home", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -25190,21 +28732,18 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_document_level_security.default_value", - "type": "Uncategorized", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_home.default_value", + "type": "string", "tags": [], "label": "default_value", "description": [], - "signature": [ - "null" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_document_level_security.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_home.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -25218,7 +28757,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_document_level_security.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_home.display", "type": "string", "tags": [], "label": "display", @@ -25231,7 +28770,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TOGGLE" + ".TEXTBOX" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -25239,7 +28778,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_document_level_security.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_home.label", "type": "string", "tags": [], "label": "label", @@ -25250,7 +28789,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_document_level_security.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_home.options", "type": "Array", "tags": [], "label": "options", @@ -25264,7 +28803,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_document_level_security.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_home.order", "type": "number", "tags": [], "label": "order", @@ -25275,13 +28814,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_document_level_security.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_home.required", "type": "boolean", "tags": [], "label": "required", "description": [], "signature": [ - "true" + "false" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -25289,7 +28828,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_document_level_security.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_home.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -25303,7 +28842,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_document_level_security.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_home.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -25314,7 +28853,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_document_level_security.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_home.type", "type": "string", "tags": [], "label": "type", @@ -25327,7 +28866,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".BOOLEAN" + ".STRING" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -25335,13 +28874,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_document_level_security.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_home.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", "description": [], "signature": [ - "never[]" + "string[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -25349,7 +28888,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_document_level_security.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_home.validations", "type": "Array", "tags": [], "label": "validations", @@ -25363,14 +28902,11 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_document_level_security.value", - "type": "boolean", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_home.value", + "type": "string", "tags": [], "label": "value", "description": [], - "signature": [ - "false" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false @@ -25379,10 +28915,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_text_extraction_service", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.wallet_configuration_path", "type": "Object", "tags": [], - "label": "use_text_extraction_service", + "label": "wallet_configuration_path", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -25390,21 +28926,18 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_text_extraction_service.default_value", - "type": "Uncategorized", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.wallet_configuration_path.default_value", + "type": "string", "tags": [], "label": "default_value", "description": [], - "signature": [ - "null" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_text_extraction_service.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.wallet_configuration_path.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -25418,7 +28951,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_text_extraction_service.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.wallet_configuration_path.display", "type": "string", "tags": [], "label": "display", @@ -25431,7 +28964,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TOGGLE" + ".TEXTBOX" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -25439,7 +28972,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_text_extraction_service.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.wallet_configuration_path.label", "type": "string", "tags": [], "label": "label", @@ -25450,7 +28983,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_text_extraction_service.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.wallet_configuration_path.options", "type": "Array", "tags": [], "label": "options", @@ -25464,7 +28997,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_text_extraction_service.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.wallet_configuration_path.order", "type": "number", "tags": [], "label": "order", @@ -25475,13 +29008,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_text_extraction_service.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.wallet_configuration_path.required", "type": "boolean", "tags": [], "label": "required", "description": [], "signature": [ - "true" + "false" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -25489,7 +29022,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_text_extraction_service.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.wallet_configuration_path.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -25503,7 +29036,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_text_extraction_service.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.wallet_configuration_path.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -25514,7 +29047,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_text_extraction_service.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.wallet_configuration_path.type", "type": "string", "tags": [], "label": "type", @@ -25527,7 +29060,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".BOOLEAN" + ".STRING" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -25535,7 +29068,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_text_extraction_service.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.wallet_configuration_path.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -25549,7 +29082,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_text_extraction_service.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.wallet_configuration_path.validations", "type": "Array", "tags": [], "label": "validations", @@ -25563,14 +29096,11 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.configuration.use_text_extraction_service.value", - "type": "boolean", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.wallet_configuration_path.value", + "type": "string", "tags": [], "label": "value", "description": [], - "signature": [ - "false" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false @@ -25581,18 +29111,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.name", - "type": "string", - "tags": [], - "label": "name", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.features", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.features", "type": "Object", "tags": [], "label": "features", @@ -25603,10 +29122,10 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.features.FeatureName.DOCUMENT_LEVEL_SECURITY", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.features.FeatureName.SYNC_RULES", "type": "Object", "tags": [], - "label": "[FeatureName.DOCUMENT_LEVEL_SECURITY]", + "label": "[FeatureName.SYNC_RULES]", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -25614,17 +29133,57 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.features.FeatureName.DOCUMENT_LEVEL_SECURITY.enabled", - "type": "boolean", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.features.FeatureName.SYNC_RULES.advanced", + "type": "Object", "tags": [], - "label": "enabled", + "label": "advanced", "description": [], - "signature": [ - "true" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, - "trackAdoption": false + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.features.FeatureName.SYNC_RULES.advanced.enabled", + "type": "boolean", + "tags": [], + "label": "enabled", + "description": [], + "signature": [ + "false" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.features.FeatureName.SYNC_RULES.basic", + "type": "Object", + "tags": [], + "label": "basic", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.features.FeatureName.SYNC_RULES.basic.enabled", + "type": "boolean", + "tags": [], + "label": "enabled", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] } ] } @@ -25632,7 +29191,18 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.onedrive.serviceType", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.serviceType", "type": "string", "tags": [], "label": "serviceType", @@ -25645,10 +29215,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql", "type": "Object", "tags": [], - "label": "oracle", + "label": "postgresql", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -25656,7 +29226,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration", "type": "Object", "tags": [], "label": "configuration", @@ -25667,7 +29237,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.host", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.host", "type": "Object", "tags": [], "label": "host", @@ -25678,7 +29248,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.host.default_value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.host.default_value", "type": "string", "tags": [], "label": "default_value", @@ -25689,7 +29259,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.host.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.host.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -25703,7 +29273,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.host.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.host.display", "type": "string", "tags": [], "label": "display", @@ -25724,7 +29294,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.host.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.host.label", "type": "string", "tags": [], "label": "label", @@ -25735,7 +29305,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.host.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.host.options", "type": "Array", "tags": [], "label": "options", @@ -25749,7 +29319,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.host.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.host.order", "type": "number", "tags": [], "label": "order", @@ -25760,7 +29330,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.host.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.host.required", "type": "boolean", "tags": [], "label": "required", @@ -25774,7 +29344,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.host.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.host.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -25788,7 +29358,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.host.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.host.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -25799,7 +29369,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.host.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.host.type", "type": "string", "tags": [], "label": "type", @@ -25820,7 +29390,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.host.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.host.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -25834,7 +29404,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.host.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.host.validations", "type": "Array", "tags": [], "label": "validations", @@ -25848,7 +29418,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.host.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.host.value", "type": "string", "tags": [], "label": "value", @@ -25861,7 +29431,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.port", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.port", "type": "Object", "tags": [], "label": "port", @@ -25872,7 +29442,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.port.default_value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.port.default_value", "type": "Uncategorized", "tags": [], "label": "default_value", @@ -25886,7 +29456,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.port.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.port.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -25900,7 +29470,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.port.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.port.display", "type": "string", "tags": [], "label": "display", @@ -25921,7 +29491,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.port.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.port.label", "type": "string", "tags": [], "label": "label", @@ -25932,7 +29502,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.port.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.port.options", "type": "Array", "tags": [], "label": "options", @@ -25946,7 +29516,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.port.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.port.order", "type": "number", "tags": [], "label": "order", @@ -25957,7 +29527,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.port.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.port.required", "type": "boolean", "tags": [], "label": "required", @@ -25971,7 +29541,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.port.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.port.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -25985,7 +29555,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.port.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.port.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -25996,7 +29566,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.port.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.port.type", "type": "string", "tags": [], "label": "type", @@ -26017,7 +29587,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.port.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.port.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -26031,7 +29601,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.port.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.port.validations", "type": "Array", "tags": [], "label": "validations", @@ -26044,9 +29614,9 @@ "trackAdoption": false }, { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.port.value", - "type": "string", + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.port.value", + "type": "number", "tags": [], "label": "value", "description": [], @@ -26058,7 +29628,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.username", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.username", "type": "Object", "tags": [], "label": "username", @@ -26069,7 +29639,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.username.default_value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.username.default_value", "type": "string", "tags": [], "label": "default_value", @@ -26080,7 +29650,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.username.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.username.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -26094,7 +29664,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.username.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.username.display", "type": "string", "tags": [], "label": "display", @@ -26115,7 +29685,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.username.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.username.label", "type": "string", "tags": [], "label": "label", @@ -26126,7 +29696,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.username.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.username.options", "type": "Array", "tags": [], "label": "options", @@ -26140,7 +29710,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.username.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.username.order", "type": "number", "tags": [], "label": "order", @@ -26151,7 +29721,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.username.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.username.required", "type": "boolean", "tags": [], "label": "required", @@ -26165,7 +29735,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.username.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.username.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -26179,7 +29749,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.username.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.username.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -26190,7 +29760,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.username.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.username.type", "type": "string", "tags": [], "label": "type", @@ -26211,7 +29781,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.username.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.username.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -26225,7 +29795,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.username.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.username.validations", "type": "Array", "tags": [], "label": "validations", @@ -26239,7 +29809,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.username.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.username.value", "type": "string", "tags": [], "label": "value", @@ -26252,7 +29822,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.password", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.password", "type": "Object", "tags": [], "label": "password", @@ -26263,7 +29833,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.password.default_value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.password.default_value", "type": "string", "tags": [], "label": "default_value", @@ -26274,7 +29844,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.password.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.password.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -26288,7 +29858,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.password.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.password.display", "type": "string", "tags": [], "label": "display", @@ -26309,7 +29879,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.password.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.password.label", "type": "string", "tags": [], "label": "label", @@ -26320,7 +29890,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.password.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.password.options", "type": "Array", "tags": [], "label": "options", @@ -26334,7 +29904,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.password.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.password.order", "type": "number", "tags": [], "label": "order", @@ -26345,7 +29915,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.password.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.password.required", "type": "boolean", "tags": [], "label": "required", @@ -26359,7 +29929,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.password.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.password.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -26373,7 +29943,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.password.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.password.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -26384,7 +29954,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.password.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.password.type", "type": "string", "tags": [], "label": "type", @@ -26405,7 +29975,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.password.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.password.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -26419,7 +29989,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.password.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.password.validations", "type": "Array", "tags": [], "label": "validations", @@ -26433,7 +30003,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.password.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.password.value", "type": "string", "tags": [], "label": "value", @@ -26446,7 +30016,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.database", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.database", "type": "Object", "tags": [], "label": "database", @@ -26457,7 +30027,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.database.default_value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.database.default_value", "type": "string", "tags": [], "label": "default_value", @@ -26468,7 +30038,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.database.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.database.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -26482,7 +30052,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.database.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.database.display", "type": "string", "tags": [], "label": "display", @@ -26503,7 +30073,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.database.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.database.label", "type": "string", "tags": [], "label": "label", @@ -26514,7 +30084,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.database.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.database.options", "type": "Array", "tags": [], "label": "options", @@ -26528,7 +30098,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.database.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.database.order", "type": "number", "tags": [], "label": "order", @@ -26539,7 +30109,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.database.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.database.required", "type": "boolean", "tags": [], "label": "required", @@ -26553,7 +30123,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.database.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.database.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -26567,7 +30137,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.database.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.database.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -26578,7 +30148,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.database.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.database.type", "type": "string", "tags": [], "label": "type", @@ -26599,7 +30169,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.database.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.database.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -26613,7 +30183,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.database.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.database.validations", "type": "Array", "tags": [], "label": "validations", @@ -26627,7 +30197,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.database.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.database.value", "type": "string", "tags": [], "label": "value", @@ -26640,10 +30210,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.tables", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.schema", "type": "Object", "tags": [], - "label": "tables", + "label": "schema", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -26651,7 +30221,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.tables.default_value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.schema.default_value", "type": "string", "tags": [], "label": "default_value", @@ -26662,7 +30232,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.tables.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.schema.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -26676,7 +30246,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.tables.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.schema.display", "type": "string", "tags": [], "label": "display", @@ -26689,7 +30259,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TEXTAREA" + ".TEXTBOX" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -26697,7 +30267,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.tables.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.schema.label", "type": "string", "tags": [], "label": "label", @@ -26708,7 +30278,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.tables.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.schema.options", "type": "Array", "tags": [], "label": "options", @@ -26722,7 +30292,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.tables.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.schema.order", "type": "number", "tags": [], "label": "order", @@ -26733,7 +30303,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.tables.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.schema.required", "type": "boolean", "tags": [], "label": "required", @@ -26747,7 +30317,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.tables.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.schema.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -26761,7 +30331,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.tables.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.schema.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -26772,7 +30342,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.tables.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.schema.type", "type": "string", "tags": [], "label": "type", @@ -26785,7 +30355,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".LIST" + ".STRING" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -26793,7 +30363,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.tables.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.schema.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -26807,7 +30377,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.tables.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.schema.validations", "type": "Array", "tags": [], "label": "validations", @@ -26821,7 +30391,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.tables.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.schema.value", "type": "string", "tags": [], "label": "value", @@ -26834,10 +30404,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.fetch_size", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.tables", "type": "Object", "tags": [], - "label": "fetch_size", + "label": "tables", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -26845,8 +30415,8 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.fetch_size.default_value", - "type": "number", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.tables.default_value", + "type": "string", "tags": [], "label": "default_value", "description": [], @@ -26856,7 +30426,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.fetch_size.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.tables.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -26870,7 +30440,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.fetch_size.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.tables.display", "type": "string", "tags": [], "label": "display", @@ -26883,7 +30453,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".NUMERIC" + ".TEXTAREA" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -26891,7 +30461,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.fetch_size.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.tables.label", "type": "string", "tags": [], "label": "label", @@ -26902,7 +30472,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.fetch_size.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.tables.options", "type": "Array", "tags": [], "label": "options", @@ -26916,7 +30486,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.fetch_size.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.tables.order", "type": "number", "tags": [], "label": "order", @@ -26927,13 +30497,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.fetch_size.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.tables.required", "type": "boolean", "tags": [], "label": "required", "description": [], "signature": [ - "false" + "true" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -26941,7 +30511,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.fetch_size.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.tables.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -26955,7 +30525,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.fetch_size.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.tables.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -26966,7 +30536,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.fetch_size.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.tables.type", "type": "string", "tags": [], "label": "type", @@ -26979,7 +30549,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".INTEGER" + ".LIST" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -26987,13 +30557,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.fetch_size.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.tables.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", "description": [], "signature": [ - "string[]" + "never[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -27001,7 +30571,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.fetch_size.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.tables.validations", "type": "Array", "tags": [], "label": "validations", @@ -27015,8 +30585,8 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.fetch_size.value", - "type": "number", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.tables.value", + "type": "string", "tags": [], "label": "value", "description": [], @@ -27028,10 +30598,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.retry_count", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_enabled", "type": "Object", "tags": [], - "label": "retry_count", + "label": "ssl_enabled", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -27039,18 +30609,21 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.retry_count.default_value", - "type": "number", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_enabled.default_value", + "type": "boolean", "tags": [], "label": "default_value", "description": [], + "signature": [ + "false" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.retry_count.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_enabled.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -27064,7 +30637,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.retry_count.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_enabled.display", "type": "string", "tags": [], "label": "display", @@ -27077,7 +30650,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".NUMERIC" + ".TOGGLE" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -27085,7 +30658,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.retry_count.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_enabled.label", "type": "string", "tags": [], "label": "label", @@ -27096,7 +30669,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.retry_count.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_enabled.options", "type": "Array", "tags": [], "label": "options", @@ -27110,7 +30683,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.retry_count.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_enabled.order", "type": "number", "tags": [], "label": "order", @@ -27121,13 +30694,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.retry_count.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_enabled.required", "type": "boolean", "tags": [], "label": "required", "description": [], "signature": [ - "false" + "true" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -27135,7 +30708,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.retry_count.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_enabled.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -27149,7 +30722,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.retry_count.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_enabled.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -27160,7 +30733,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.retry_count.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_enabled.type", "type": "string", "tags": [], "label": "type", @@ -27173,7 +30746,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".INTEGER" + ".BOOLEAN" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -27181,13 +30754,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.retry_count.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_enabled.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", "description": [], "signature": [ - "string[]" + "never[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -27195,7 +30768,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.retry_count.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_enabled.validations", "type": "Array", "tags": [], "label": "validations", @@ -27209,11 +30782,14 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.retry_count.value", - "type": "number", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_enabled.value", + "type": "boolean", "tags": [], "label": "value", "description": [], + "signature": [ + "false" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false @@ -27222,10 +30798,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_protocol", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_ca", "type": "Object", "tags": [], - "label": "oracle_protocol", + "label": "ssl_ca", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -27233,7 +30809,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_protocol.default_value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_ca.default_value", "type": "string", "tags": [], "label": "default_value", @@ -27244,13 +30820,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_protocol.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_ca.depends_on", "type": "Array", "tags": [], "label": "depends_on", "description": [], "signature": [ - "never[]" + "{ field: string; value: true; }[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -27258,7 +30834,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_protocol.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_ca.display", "type": "string", "tags": [], "label": "display", @@ -27271,7 +30847,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".DROPDOWN" + ".TEXTBOX" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -27279,7 +30855,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_protocol.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_ca.label", "type": "string", "tags": [], "label": "label", @@ -27290,13 +30866,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_protocol.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_ca.options", "type": "Array", "tags": [], "label": "options", "description": [], "signature": [ - "{ label: string; value: string; }[]" + "never[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -27304,7 +30880,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_protocol.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_ca.order", "type": "number", "tags": [], "label": "order", @@ -27315,7 +30891,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_protocol.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_ca.required", "type": "boolean", "tags": [], "label": "required", @@ -27329,7 +30905,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_protocol.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_ca.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -27343,7 +30919,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_protocol.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_ca.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -27354,7 +30930,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_protocol.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_ca.type", "type": "string", "tags": [], "label": "type", @@ -27375,13 +30951,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_protocol.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_ca.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", "description": [], "signature": [ - "string[]" + "never[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -27389,7 +30965,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_protocol.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_ca.validations", "type": "Array", "tags": [], "label": "validations", @@ -27403,7 +30979,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_protocol.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_ca.value", "type": "string", "tags": [], "label": "value", @@ -27416,19 +30992,19 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_home", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.fetch_size", "type": "Object", "tags": [], - "label": "oracle_home", + "label": "fetch_size", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false, "children": [ { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_home.default_value", - "type": "string", + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.fetch_size.default_value", + "type": "number", "tags": [], "label": "default_value", "description": [], @@ -27438,7 +31014,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_home.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.fetch_size.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -27452,7 +31028,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_home.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.fetch_size.display", "type": "string", "tags": [], "label": "display", @@ -27465,7 +31041,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TEXTBOX" + ".NUMERIC" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -27473,7 +31049,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_home.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.fetch_size.label", "type": "string", "tags": [], "label": "label", @@ -27484,7 +31060,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_home.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.fetch_size.options", "type": "Array", "tags": [], "label": "options", @@ -27498,7 +31074,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_home.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.fetch_size.order", "type": "number", "tags": [], "label": "order", @@ -27509,7 +31085,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_home.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.fetch_size.required", "type": "boolean", "tags": [], "label": "required", @@ -27523,7 +31099,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_home.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.fetch_size.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -27537,7 +31113,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_home.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.fetch_size.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -27548,7 +31124,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_home.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.fetch_size.type", "type": "string", "tags": [], "label": "type", @@ -27561,7 +31137,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".STRING" + ".INTEGER" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -27569,7 +31145,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_home.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.fetch_size.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -27583,7 +31159,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_home.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.fetch_size.validations", "type": "Array", "tags": [], "label": "validations", @@ -27597,8 +31173,8 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.oracle_home.value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.fetch_size.value", + "type": "number", "tags": [], "label": "value", "description": [], @@ -27610,10 +31186,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.wallet_configuration_path", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.retry_count", "type": "Object", "tags": [], - "label": "wallet_configuration_path", + "label": "retry_count", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -27621,8 +31197,8 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.wallet_configuration_path.default_value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.retry_count.default_value", + "type": "number", "tags": [], "label": "default_value", "description": [], @@ -27632,7 +31208,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.wallet_configuration_path.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.retry_count.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -27646,7 +31222,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.wallet_configuration_path.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.retry_count.display", "type": "string", "tags": [], "label": "display", @@ -27659,7 +31235,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TEXTBOX" + ".NUMERIC" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -27667,7 +31243,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.wallet_configuration_path.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.retry_count.label", "type": "string", "tags": [], "label": "label", @@ -27678,7 +31254,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.wallet_configuration_path.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.retry_count.options", "type": "Array", "tags": [], "label": "options", @@ -27692,7 +31268,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.wallet_configuration_path.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.retry_count.order", "type": "number", "tags": [], "label": "order", @@ -27703,7 +31279,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.wallet_configuration_path.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.retry_count.required", "type": "boolean", "tags": [], "label": "required", @@ -27717,7 +31293,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.wallet_configuration_path.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.retry_count.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -27731,7 +31307,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.wallet_configuration_path.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.retry_count.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -27742,7 +31318,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.wallet_configuration_path.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.retry_count.type", "type": "string", "tags": [], "label": "type", @@ -27755,7 +31331,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".STRING" + ".INTEGER" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -27763,7 +31339,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.wallet_configuration_path.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.retry_count.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -27777,7 +31353,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.wallet_configuration_path.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.retry_count.validations", "type": "Array", "tags": [], "label": "validations", @@ -27791,8 +31367,8 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.configuration.wallet_configuration_path.value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.retry_count.value", + "type": "number", "tags": [], "label": "value", "description": [], @@ -27806,7 +31382,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.features", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.features", "type": "Object", "tags": [], "label": "features", @@ -27817,7 +31393,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.features.FeatureName.SYNC_RULES", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.features.FeatureName.SYNC_RULES", "type": "Object", "tags": [], "label": "[FeatureName.SYNC_RULES]", @@ -27828,7 +31404,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.features.FeatureName.SYNC_RULES.advanced", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.features.FeatureName.SYNC_RULES.advanced", "type": "Object", "tags": [], "label": "advanced", @@ -27839,7 +31415,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.features.FeatureName.SYNC_RULES.advanced.enabled", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.features.FeatureName.SYNC_RULES.advanced.enabled", "type": "boolean", "tags": [], "label": "enabled", @@ -27855,7 +31431,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.features.FeatureName.SYNC_RULES.basic", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.features.FeatureName.SYNC_RULES.basic", "type": "Object", "tags": [], "label": "basic", @@ -27866,7 +31442,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.features.FeatureName.SYNC_RULES.basic.enabled", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.features.FeatureName.SYNC_RULES.basic.enabled", "type": "boolean", "tags": [], "label": "enabled", @@ -27886,7 +31462,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.name", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.name", "type": "string", "tags": [], "label": "name", @@ -27897,7 +31473,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.oracle.serviceType", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.serviceType", "type": "string", "tags": [], "label": "serviceType", @@ -27910,10 +31486,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3", "type": "Object", "tags": [], - "label": "postgresql", + "label": "s3", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -27921,7 +31497,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration", "type": "Object", "tags": [], "label": "configuration", @@ -27932,10 +31508,207 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.host", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.buckets", + "type": "Object", + "tags": [], + "label": "buckets", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.buckets.default_value", + "type": "Uncategorized", + "tags": [], + "label": "default_value", + "description": [], + "signature": [ + "null" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.buckets.depends_on", + "type": "Array", + "tags": [], + "label": "depends_on", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.buckets.display", + "type": "string", + "tags": [], + "label": "display", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.DisplayType", + "text": "DisplayType" + }, + ".TEXTAREA" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.buckets.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.buckets.options", + "type": "Array", + "tags": [], + "label": "options", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.buckets.order", + "type": "number", + "tags": [], + "label": "order", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.buckets.required", + "type": "boolean", + "tags": [], + "label": "required", + "description": [], + "signature": [ + "true" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.buckets.sensitive", + "type": "boolean", + "tags": [], + "label": "sensitive", + "description": [], + "signature": [ + "false" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.buckets.tooltip", + "type": "string", + "tags": [], + "label": "tooltip", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.buckets.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-connectors", + "scope": "common", + "docId": "kibKbnSearchConnectorsPluginApi", + "section": "def-common.FieldType", + "text": "FieldType" + }, + ".LIST" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.buckets.ui_restrictions", + "type": "Array", + "tags": [], + "label": "ui_restrictions", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.buckets.validations", + "type": "Array", + "tags": [], + "label": "validations", + "description": [], + "signature": [ + "never[]" + ], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.buckets.value", + "type": "string", + "tags": [], + "label": "value", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.aws_access_key_id", "type": "Object", "tags": [], - "label": "host", + "label": "aws_access_key_id", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -27943,18 +31716,21 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.host.default_value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.aws_access_key_id.default_value", + "type": "Uncategorized", "tags": [], "label": "default_value", "description": [], + "signature": [ + "null" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.host.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.aws_access_key_id.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -27968,7 +31744,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.host.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.aws_access_key_id.display", "type": "string", "tags": [], "label": "display", @@ -27989,7 +31765,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.host.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.aws_access_key_id.label", "type": "string", "tags": [], "label": "label", @@ -28000,7 +31776,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.host.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.aws_access_key_id.options", "type": "Array", "tags": [], "label": "options", @@ -28014,7 +31790,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.host.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.aws_access_key_id.order", "type": "number", "tags": [], "label": "order", @@ -28025,7 +31801,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.host.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.aws_access_key_id.required", "type": "boolean", "tags": [], "label": "required", @@ -28039,7 +31815,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.host.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.aws_access_key_id.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -28053,18 +31829,21 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.host.tooltip", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.aws_access_key_id.tooltip", + "type": "Uncategorized", "tags": [], "label": "tooltip", "description": [], + "signature": [ + "null" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.host.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.aws_access_key_id.type", "type": "string", "tags": [], "label": "type", @@ -28085,7 +31864,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.host.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.aws_access_key_id.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -28099,7 +31878,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.host.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.aws_access_key_id.validations", "type": "Array", "tags": [], "label": "validations", @@ -28113,7 +31892,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.host.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.aws_access_key_id.value", "type": "string", "tags": [], "label": "value", @@ -28126,10 +31905,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.port", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.aws_secret_access_key", "type": "Object", "tags": [], - "label": "port", + "label": "aws_secret_access_key", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -28137,7 +31916,7 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.port.default_value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.aws_secret_access_key.default_value", "type": "Uncategorized", "tags": [], "label": "default_value", @@ -28151,7 +31930,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.port.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.aws_secret_access_key.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -28165,7 +31944,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.port.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.aws_secret_access_key.display", "type": "string", "tags": [], "label": "display", @@ -28178,7 +31957,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".NUMERIC" + ".TEXTBOX" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -28186,7 +31965,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.port.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.aws_secret_access_key.label", "type": "string", "tags": [], "label": "label", @@ -28197,7 +31976,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.port.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.aws_secret_access_key.options", "type": "Array", "tags": [], "label": "options", @@ -28211,7 +31990,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.port.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.aws_secret_access_key.order", "type": "number", "tags": [], "label": "order", @@ -28222,7 +32001,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.port.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.aws_secret_access_key.required", "type": "boolean", "tags": [], "label": "required", @@ -28236,13 +32015,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.port.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.aws_secret_access_key.sensitive", "type": "boolean", "tags": [], "label": "sensitive", "description": [], "signature": [ - "false" + "true" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -28250,18 +32029,21 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.port.tooltip", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.aws_secret_access_key.tooltip", + "type": "Uncategorized", "tags": [], "label": "tooltip", "description": [], + "signature": [ + "null" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.port.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.aws_secret_access_key.type", "type": "string", "tags": [], "label": "type", @@ -28274,7 +32056,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".INTEGER" + ".STRING" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -28282,7 +32064,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.port.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.aws_secret_access_key.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -28296,7 +32078,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.port.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.aws_secret_access_key.validations", "type": "Array", "tags": [], "label": "validations", @@ -28310,8 +32092,8 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.port.value", - "type": "number", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.aws_secret_access_key.value", + "type": "string", "tags": [], "label": "value", "description": [], @@ -28323,10 +32105,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.username", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.read_timeout", "type": "Object", "tags": [], - "label": "username", + "label": "read_timeout", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -28334,8 +32116,8 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.username.default_value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.read_timeout.default_value", + "type": "number", "tags": [], "label": "default_value", "description": [], @@ -28345,7 +32127,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.username.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.read_timeout.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -28359,7 +32141,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.username.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.read_timeout.display", "type": "string", "tags": [], "label": "display", @@ -28372,7 +32154,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TEXTBOX" + ".NUMERIC" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -28380,7 +32162,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.username.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.read_timeout.label", "type": "string", "tags": [], "label": "label", @@ -28391,7 +32173,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.username.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.read_timeout.options", "type": "Array", "tags": [], "label": "options", @@ -28405,7 +32187,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.username.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.read_timeout.order", "type": "number", "tags": [], "label": "order", @@ -28416,13 +32198,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.username.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.read_timeout.required", "type": "boolean", "tags": [], "label": "required", "description": [], "signature": [ - "true" + "false" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -28430,7 +32212,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.username.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.read_timeout.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -28444,18 +32226,21 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.username.tooltip", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.read_timeout.tooltip", + "type": "Uncategorized", "tags": [], "label": "tooltip", "description": [], + "signature": [ + "null" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.username.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.read_timeout.type", "type": "string", "tags": [], "label": "type", @@ -28468,7 +32253,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".STRING" + ".INTEGER" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -28476,13 +32261,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.username.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.read_timeout.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", "description": [], "signature": [ - "never[]" + "string[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -28490,7 +32275,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.username.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.read_timeout.validations", "type": "Array", "tags": [], "label": "validations", @@ -28504,7 +32289,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.username.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.read_timeout.value", "type": "string", "tags": [], "label": "value", @@ -28517,10 +32302,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.password", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.connect_timeout", "type": "Object", "tags": [], - "label": "password", + "label": "connect_timeout", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -28528,8 +32313,8 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.password.default_value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.connect_timeout.default_value", + "type": "number", "tags": [], "label": "default_value", "description": [], @@ -28539,7 +32324,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.password.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.connect_timeout.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -28553,7 +32338,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.password.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.connect_timeout.display", "type": "string", "tags": [], "label": "display", @@ -28566,7 +32351,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TEXTBOX" + ".NUMERIC" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -28574,7 +32359,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.password.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.connect_timeout.label", "type": "string", "tags": [], "label": "label", @@ -28585,7 +32370,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.password.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.connect_timeout.options", "type": "Array", "tags": [], "label": "options", @@ -28599,7 +32384,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.password.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.connect_timeout.order", "type": "number", "tags": [], "label": "order", @@ -28610,13 +32395,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.password.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.connect_timeout.required", "type": "boolean", "tags": [], "label": "required", "description": [], "signature": [ - "true" + "false" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -28624,13 +32409,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.password.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.connect_timeout.sensitive", "type": "boolean", "tags": [], "label": "sensitive", "description": [], "signature": [ - "true" + "false" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -28638,18 +32423,21 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.password.tooltip", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.connect_timeout.tooltip", + "type": "Uncategorized", "tags": [], "label": "tooltip", "description": [], + "signature": [ + "null" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.password.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.connect_timeout.type", "type": "string", "tags": [], "label": "type", @@ -28662,7 +32450,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".STRING" + ".INTEGER" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -28670,13 +32458,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.password.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.connect_timeout.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", "description": [], "signature": [ - "never[]" + "string[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -28684,7 +32472,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.password.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.connect_timeout.validations", "type": "Array", "tags": [], "label": "validations", @@ -28698,7 +32486,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.password.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.connect_timeout.value", "type": "string", "tags": [], "label": "value", @@ -28711,10 +32499,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.database", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.max_attempts", "type": "Object", "tags": [], - "label": "database", + "label": "max_attempts", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -28722,8 +32510,8 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.database.default_value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.max_attempts.default_value", + "type": "number", "tags": [], "label": "default_value", "description": [], @@ -28733,7 +32521,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.database.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.max_attempts.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -28747,7 +32535,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.database.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.max_attempts.display", "type": "string", "tags": [], "label": "display", @@ -28760,7 +32548,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TEXTBOX" + ".NUMERIC" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -28768,7 +32556,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.database.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.max_attempts.label", "type": "string", "tags": [], "label": "label", @@ -28779,7 +32567,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.database.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.max_attempts.options", "type": "Array", "tags": [], "label": "options", @@ -28793,7 +32581,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.database.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.max_attempts.order", "type": "number", "tags": [], "label": "order", @@ -28804,13 +32592,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.database.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.max_attempts.required", "type": "boolean", "tags": [], "label": "required", "description": [], "signature": [ - "true" + "false" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -28818,7 +32606,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.database.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.max_attempts.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -28832,18 +32620,21 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.database.tooltip", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.max_attempts.tooltip", + "type": "Uncategorized", "tags": [], "label": "tooltip", "description": [], + "signature": [ + "null" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.database.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.max_attempts.type", "type": "string", "tags": [], "label": "type", @@ -28856,7 +32647,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".STRING" + ".INTEGER" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -28864,13 +32655,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.database.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.max_attempts.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", "description": [], "signature": [ - "never[]" + "string[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -28878,7 +32669,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.database.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.max_attempts.validations", "type": "Array", "tags": [], "label": "validations", @@ -28892,7 +32683,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.database.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.max_attempts.value", "type": "string", "tags": [], "label": "value", @@ -28905,10 +32696,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.schema", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.page_size", "type": "Object", "tags": [], - "label": "schema", + "label": "page_size", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -28916,8 +32707,8 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.schema.default_value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.page_size.default_value", + "type": "number", "tags": [], "label": "default_value", "description": [], @@ -28927,7 +32718,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.schema.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.page_size.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -28941,7 +32732,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.schema.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.page_size.display", "type": "string", "tags": [], "label": "display", @@ -28954,7 +32745,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TEXTBOX" + ".NUMERIC" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -28962,7 +32753,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.schema.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.page_size.label", "type": "string", "tags": [], "label": "label", @@ -28973,7 +32764,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.schema.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.page_size.options", "type": "Array", "tags": [], "label": "options", @@ -28987,7 +32778,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.schema.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.page_size.order", "type": "number", "tags": [], "label": "order", @@ -28998,13 +32789,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.schema.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.page_size.required", "type": "boolean", "tags": [], "label": "required", "description": [], "signature": [ - "true" + "false" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -29012,7 +32803,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.schema.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.page_size.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -29026,18 +32817,21 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.schema.tooltip", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.page_size.tooltip", + "type": "Uncategorized", "tags": [], "label": "tooltip", "description": [], + "signature": [ + "null" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.schema.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.page_size.type", "type": "string", "tags": [], "label": "type", @@ -29050,7 +32844,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".STRING" + ".INTEGER" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -29058,13 +32852,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.schema.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.page_size.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", "description": [], "signature": [ - "never[]" + "string[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -29072,7 +32866,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.schema.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.page_size.validations", "type": "Array", "tags": [], "label": "validations", @@ -29086,7 +32880,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.schema.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.page_size.value", "type": "string", "tags": [], "label": "value", @@ -29099,10 +32893,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.tables", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.use_text_extraction_service", "type": "Object", "tags": [], - "label": "tables", + "label": "use_text_extraction_service", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -29110,18 +32904,21 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.tables.default_value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.use_text_extraction_service.default_value", + "type": "Uncategorized", "tags": [], "label": "default_value", "description": [], + "signature": [ + "null" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.tables.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.use_text_extraction_service.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -29135,7 +32932,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.tables.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.use_text_extraction_service.display", "type": "string", "tags": [], "label": "display", @@ -29148,7 +32945,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TEXTAREA" + ".TOGGLE" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -29156,7 +32953,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.tables.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.use_text_extraction_service.label", "type": "string", "tags": [], "label": "label", @@ -29167,7 +32964,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.tables.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.use_text_extraction_service.options", "type": "Array", "tags": [], "label": "options", @@ -29181,7 +32978,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.tables.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.use_text_extraction_service.order", "type": "number", "tags": [], "label": "order", @@ -29192,7 +32989,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.tables.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.use_text_extraction_service.required", "type": "boolean", "tags": [], "label": "required", @@ -29206,7 +33003,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.tables.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.use_text_extraction_service.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -29220,7 +33017,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.tables.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.use_text_extraction_service.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -29231,7 +33028,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.tables.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.use_text_extraction_service.type", "type": "string", "tags": [], "label": "type", @@ -29244,7 +33041,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".LIST" + ".BOOLEAN" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -29252,13 +33049,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.tables.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.use_text_extraction_service.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", "description": [], "signature": [ - "never[]" + "string[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -29266,7 +33063,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.tables.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.use_text_extraction_service.validations", "type": "Array", "tags": [], "label": "validations", @@ -29280,23 +33077,98 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.tables.value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.configuration.use_text_extraction_service.value", + "type": "boolean", "tags": [], "label": "value", "description": [], + "signature": [ + "false" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false } ] - }, + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.features", + "type": "Object", + "tags": [], + "label": "features", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.name", + "type": "string", + "tags": [], + "label": "name", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.s3.serviceType", + "type": "string", + "tags": [], + "label": "serviceType", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + } + ] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce", + "type": "Object", + "tags": [], + "label": "salesforce", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.features", + "type": "Object", + "tags": [], + "label": "features", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [] + }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration", + "type": "Object", + "tags": [], + "label": "configuration", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_enabled", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.domain", "type": "Object", "tags": [], - "label": "ssl_enabled", + "label": "domain", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -29304,13 +33176,13 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_enabled.default_value", - "type": "boolean", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.domain.default_value", + "type": "Uncategorized", "tags": [], "label": "default_value", "description": [], "signature": [ - "false" + "null" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -29318,7 +33190,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_enabled.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.domain.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -29332,7 +33204,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_enabled.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.domain.display", "type": "string", "tags": [], "label": "display", @@ -29345,7 +33217,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".TOGGLE" + ".TEXTBOX" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -29353,7 +33225,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_enabled.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.domain.label", "type": "string", "tags": [], "label": "label", @@ -29364,7 +33236,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_enabled.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.domain.options", "type": "Array", "tags": [], "label": "options", @@ -29378,7 +33250,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_enabled.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.domain.order", "type": "number", "tags": [], "label": "order", @@ -29389,7 +33261,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_enabled.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.domain.required", "type": "boolean", "tags": [], "label": "required", @@ -29403,7 +33275,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_enabled.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.domain.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -29417,7 +33289,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_enabled.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.domain.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -29428,7 +33300,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_enabled.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.domain.type", "type": "string", "tags": [], "label": "type", @@ -29441,7 +33313,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".BOOLEAN" + ".STRING" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -29449,7 +33321,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_enabled.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.domain.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -29463,7 +33335,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_enabled.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.domain.validations", "type": "Array", "tags": [], "label": "validations", @@ -29477,14 +33349,11 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_enabled.value", - "type": "boolean", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.domain.value", + "type": "string", "tags": [], "label": "value", "description": [], - "signature": [ - "false" - ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false @@ -29493,10 +33362,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_ca", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.client_id", "type": "Object", "tags": [], - "label": "ssl_ca", + "label": "client_id", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -29504,24 +33373,27 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_ca.default_value", - "type": "string", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.client_id.default_value", + "type": "Uncategorized", "tags": [], "label": "default_value", "description": [], + "signature": [ + "null" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_ca.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.client_id.depends_on", "type": "Array", "tags": [], "label": "depends_on", "description": [], "signature": [ - "{ field: string; value: true; }[]" + "never[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -29529,7 +33401,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_ca.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.client_id.display", "type": "string", "tags": [], "label": "display", @@ -29550,7 +33422,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_ca.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.client_id.label", "type": "string", "tags": [], "label": "label", @@ -29561,7 +33433,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_ca.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.client_id.options", "type": "Array", "tags": [], "label": "options", @@ -29575,7 +33447,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_ca.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.client_id.order", "type": "number", "tags": [], "label": "order", @@ -29586,7 +33458,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_ca.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.client_id.required", "type": "boolean", "tags": [], "label": "required", @@ -29600,13 +33472,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_ca.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.client_id.sensitive", "type": "boolean", "tags": [], "label": "sensitive", "description": [], "signature": [ - "false" + "true" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -29614,7 +33486,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_ca.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.client_id.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -29625,7 +33497,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_ca.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.client_id.type", "type": "string", "tags": [], "label": "type", @@ -29646,7 +33518,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_ca.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.client_id.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -29660,7 +33532,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_ca.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.client_id.validations", "type": "Array", "tags": [], "label": "validations", @@ -29674,7 +33546,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.ssl_ca.value", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.client_id.value", "type": "string", "tags": [], "label": "value", @@ -29687,10 +33559,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.fetch_size", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.client_secret", "type": "Object", "tags": [], - "label": "fetch_size", + "label": "client_secret", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -29698,18 +33570,21 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.fetch_size.default_value", - "type": "number", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.client_secret.default_value", + "type": "Uncategorized", "tags": [], "label": "default_value", "description": [], + "signature": [ + "null" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.fetch_size.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.client_secret.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -29723,7 +33598,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.fetch_size.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.client_secret.display", "type": "string", "tags": [], "label": "display", @@ -29736,7 +33611,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".NUMERIC" + ".TEXTBOX" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -29744,7 +33619,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.fetch_size.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.client_secret.label", "type": "string", "tags": [], "label": "label", @@ -29755,7 +33630,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.fetch_size.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.client_secret.options", "type": "Array", "tags": [], "label": "options", @@ -29769,7 +33644,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.fetch_size.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.client_secret.order", "type": "number", "tags": [], "label": "order", @@ -29780,13 +33655,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.fetch_size.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.client_secret.required", "type": "boolean", "tags": [], "label": "required", "description": [], "signature": [ - "false" + "true" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -29794,13 +33669,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.fetch_size.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.client_secret.sensitive", "type": "boolean", "tags": [], "label": "sensitive", "description": [], "signature": [ - "false" + "true" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -29808,7 +33683,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.fetch_size.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.client_secret.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -29819,7 +33694,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.fetch_size.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.client_secret.type", "type": "string", "tags": [], "label": "type", @@ -29832,7 +33707,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".INTEGER" + ".STRING" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -29840,13 +33715,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.fetch_size.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.client_secret.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", "description": [], "signature": [ - "string[]" + "never[]" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -29854,7 +33729,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.fetch_size.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.client_secret.validations", "type": "Array", "tags": [], "label": "validations", @@ -29868,8 +33743,8 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.fetch_size.value", - "type": "number", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.client_secret.value", + "type": "string", "tags": [], "label": "value", "description": [], @@ -29881,10 +33756,10 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.retry_count", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_text_extraction_service", "type": "Object", "tags": [], - "label": "retry_count", + "label": "use_text_extraction_service", "description": [], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -29892,18 +33767,21 @@ "children": [ { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.retry_count.default_value", - "type": "number", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_text_extraction_service.default_value", + "type": "Uncategorized", "tags": [], "label": "default_value", "description": [], + "signature": [ + "null" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.retry_count.depends_on", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_text_extraction_service.depends_on", "type": "Array", "tags": [], "label": "depends_on", @@ -29917,7 +33795,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.retry_count.display", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_text_extraction_service.display", "type": "string", "tags": [], "label": "display", @@ -29930,7 +33808,7 @@ "section": "def-common.DisplayType", "text": "DisplayType" }, - ".NUMERIC" + ".TOGGLE" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -29938,7 +33816,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.retry_count.label", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_text_extraction_service.label", "type": "string", "tags": [], "label": "label", @@ -29949,7 +33827,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.retry_count.options", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_text_extraction_service.options", "type": "Array", "tags": [], "label": "options", @@ -29963,7 +33841,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.retry_count.order", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_text_extraction_service.order", "type": "number", "tags": [], "label": "order", @@ -29974,13 +33852,13 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.retry_count.required", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_text_extraction_service.required", "type": "boolean", "tags": [], "label": "required", "description": [], "signature": [ - "false" + "true" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -29988,7 +33866,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.retry_count.sensitive", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_text_extraction_service.sensitive", "type": "boolean", "tags": [], "label": "sensitive", @@ -30002,7 +33880,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.retry_count.tooltip", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_text_extraction_service.tooltip", "type": "string", "tags": [], "label": "tooltip", @@ -30013,7 +33891,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.retry_count.type", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_text_extraction_service.type", "type": "string", "tags": [], "label": "type", @@ -30026,7 +33904,7 @@ "section": "def-common.FieldType", "text": "FieldType" }, - ".INTEGER" + ".BOOLEAN" ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, @@ -30034,7 +33912,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.retry_count.ui_restrictions", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_text_extraction_service.ui_restrictions", "type": "Array", "tags": [], "label": "ui_restrictions", @@ -30048,7 +33926,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.retry_count.validations", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_text_extraction_service.validations", "type": "Array", "tags": [], "label": "validations", @@ -30062,11 +33940,14 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.configuration.retry_count.value", - "type": "number", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.configuration.use_text_extraction_service.value", + "type": "boolean", "tags": [], "label": "value", "description": [], + "signature": [ + "false" + ], "path": "packages/kbn-search-connectors/types/native_connectors.ts", "deprecated": false, "trackAdoption": false @@ -30077,87 +33958,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.features", - "type": "Object", - "tags": [], - "label": "features", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.features.FeatureName.SYNC_RULES", - "type": "Object", - "tags": [], - "label": "[FeatureName.SYNC_RULES]", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.features.FeatureName.SYNC_RULES.advanced", - "type": "Object", - "tags": [], - "label": "advanced", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.features.FeatureName.SYNC_RULES.advanced.enabled", - "type": "boolean", - "tags": [], - "label": "enabled", - "description": [], - "signature": [ - "false" - ], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false - } - ] - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.features.FeatureName.SYNC_RULES.basic", - "type": "Object", - "tags": [], - "label": "basic", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.features.FeatureName.SYNC_RULES.basic.enabled", - "type": "boolean", - "tags": [], - "label": "enabled", - "description": [], - "signature": [ - "true" - ], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false - } - ] - } - ] - } - ] - }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.name", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.name", "type": "string", "tags": [], "label": "name", @@ -30168,7 +33969,7 @@ }, { "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.postgresql.serviceType", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.salesforce.serviceType", "type": "string", "tags": [], "label": "serviceType", @@ -30840,6 +34641,17 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "@kbn/search-connectors", + "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.servicenow.configuration.services.label", + "type": "string", + "tags": [], + "label": "label", + "description": [], + "path": "packages/kbn-search-connectors/types/native_connectors.ts", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "@kbn/search-connectors", "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.servicenow.configuration.services.display", @@ -30861,17 +34673,6 @@ "deprecated": false, "trackAdoption": false }, - { - "parentPluginId": "@kbn/search-connectors", - "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.servicenow.configuration.services.label", - "type": "string", - "tags": [], - "label": "label", - "description": [], - "path": "packages/kbn-search-connectors/types/native_connectors.ts", - "deprecated": false, - "trackAdoption": false - }, { "parentPluginId": "@kbn/search-connectors", "id": "def-common.NATIVE_CONNECTOR_DEFINITIONS.servicenow.configuration.services.options", diff --git a/api_docs/kbn_search_connectors.mdx b/api_docs/kbn_search_connectors.mdx index e6c9aa8222a5c..0d004bcaccc8c 100644 --- a/api_docs/kbn_search_connectors.mdx +++ b/api_docs/kbn_search_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-connectors title: "@kbn/search-connectors" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-connectors plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-connectors'] --- import kbnSearchConnectorsObj from './kbn_search_connectors.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/te | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 2378 | 0 | 2378 | 0 | +| 2649 | 0 | 2649 | 0 | ## Common diff --git a/api_docs/kbn_search_errors.devdocs.json b/api_docs/kbn_search_errors.devdocs.json new file mode 100644 index 0000000000000..ef729a98515f1 --- /dev/null +++ b/api_docs/kbn_search_errors.devdocs.json @@ -0,0 +1,564 @@ +{ + "id": "@kbn/search-errors", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [ + { + "parentPluginId": "@kbn/search-errors", + "id": "def-common.EsError", + "type": "Class", + "tags": [], + "label": "EsError", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-errors", + "scope": "common", + "docId": "kibKbnSearchErrorsPluginApi", + "section": "def-common.EsError", + "text": "EsError" + }, + " extends Error" + ], + "path": "packages/kbn-search-errors/src/es_error.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-errors", + "id": "def-common.EsError.attributes", + "type": "Object", + "tags": [], + "label": "attributes", + "description": [], + "signature": [ + "IEsErrorAttributes | undefined" + ], + "path": "packages/kbn-search-errors/src/es_error.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-errors", + "id": "def-common.EsError.Unnamed", + "type": "Function", + "tags": [], + "label": "Constructor", + "description": [], + "signature": [ + "any" + ], + "path": "packages/kbn-search-errors/src/es_error.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-errors", + "id": "def-common.EsError.Unnamed.$1", + "type": "Object", + "tags": [], + "label": "err", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-errors", + "scope": "common", + "docId": "kibKbnSearchErrorsPluginApi", + "section": "def-common.IEsError", + "text": "IEsError" + } + ], + "path": "packages/kbn-search-errors/src/es_error.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/search-errors", + "id": "def-common.EsError.Unnamed.$2", + "type": "Function", + "tags": [], + "label": "openInInspector", + "description": [], + "signature": [ + "() => void" + ], + "path": "packages/kbn-search-errors/src/es_error.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/search-errors", + "id": "def-common.EsError.getErrorMessage", + "type": "Function", + "tags": [], + "label": "getErrorMessage", + "description": [], + "signature": [ + "() => JSX.Element | null" + ], + "path": "packages/kbn-search-errors/src/es_error.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/search-errors", + "id": "def-common.EsError.getActions", + "type": "Function", + "tags": [], + "label": "getActions", + "description": [], + "signature": [ + "(application: ", + { + "pluginId": "@kbn/core-application-browser", + "scope": "common", + "docId": "kibKbnCoreApplicationBrowserPluginApi", + "section": "def-common.ApplicationStart", + "text": "ApplicationStart" + }, + ") => JSX.Element[]" + ], + "path": "packages/kbn-search-errors/src/es_error.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-errors", + "id": "def-common.EsError.getActions.$1", + "type": "Object", + "tags": [], + "label": "application", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-application-browser", + "scope": "common", + "docId": "kibKbnCoreApplicationBrowserPluginApi", + "section": "def-common.ApplicationStart", + "text": "ApplicationStart" + } + ], + "path": "packages/kbn-search-errors/src/es_error.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/search-errors", + "id": "def-common.PainlessError", + "type": "Class", + "tags": [], + "label": "PainlessError", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-errors", + "scope": "common", + "docId": "kibKbnSearchErrorsPluginApi", + "section": "def-common.PainlessError", + "text": "PainlessError" + }, + " extends ", + { + "pluginId": "@kbn/search-errors", + "scope": "common", + "docId": "kibKbnSearchErrorsPluginApi", + "section": "def-common.EsError", + "text": "EsError" + } + ], + "path": "packages/kbn-search-errors/src/painless_error.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-errors", + "id": "def-common.PainlessError.painlessStack", + "type": "string", + "tags": [], + "label": "painlessStack", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "packages/kbn-search-errors/src/painless_error.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-errors", + "id": "def-common.PainlessError.indexPattern", + "type": "Object", + "tags": [], + "label": "indexPattern", + "description": [], + "signature": [ + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataView", + "text": "DataView" + }, + " | undefined" + ], + "path": "packages/kbn-search-errors/src/painless_error.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-errors", + "id": "def-common.PainlessError.Unnamed", + "type": "Function", + "tags": [], + "label": "Constructor", + "description": [], + "signature": [ + "any" + ], + "path": "packages/kbn-search-errors/src/painless_error.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-errors", + "id": "def-common.PainlessError.Unnamed.$1", + "type": "Object", + "tags": [], + "label": "err", + "description": [], + "signature": [ + { + "pluginId": "@kbn/search-errors", + "scope": "common", + "docId": "kibKbnSearchErrorsPluginApi", + "section": "def-common.IEsError", + "text": "IEsError" + } + ], + "path": "packages/kbn-search-errors/src/painless_error.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/search-errors", + "id": "def-common.PainlessError.Unnamed.$2", + "type": "Function", + "tags": [], + "label": "openInInspector", + "description": [], + "signature": [ + "() => void" + ], + "path": "packages/kbn-search-errors/src/painless_error.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "@kbn/search-errors", + "id": "def-common.PainlessError.Unnamed.$3", + "type": "Object", + "tags": [], + "label": "indexPattern", + "description": [], + "signature": [ + { + "pluginId": "dataViews", + "scope": "common", + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataView", + "text": "DataView" + }, + " | undefined" + ], + "path": "packages/kbn-search-errors/src/painless_error.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/search-errors", + "id": "def-common.PainlessError.getErrorMessage", + "type": "Function", + "tags": [], + "label": "getErrorMessage", + "description": [], + "signature": [ + "() => JSX.Element" + ], + "path": "packages/kbn-search-errors/src/painless_error.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/search-errors", + "id": "def-common.PainlessError.getActions", + "type": "Function", + "tags": [], + "label": "getActions", + "description": [], + "signature": [ + "(application: ", + { + "pluginId": "@kbn/core-application-browser", + "scope": "common", + "docId": "kibKbnCoreApplicationBrowserPluginApi", + "section": "def-common.ApplicationStart", + "text": "ApplicationStart" + }, + ") => JSX.Element[]" + ], + "path": "packages/kbn-search-errors/src/painless_error.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-errors", + "id": "def-common.PainlessError.getActions.$1", + "type": "Object", + "tags": [], + "label": "application", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-application-browser", + "scope": "common", + "docId": "kibKbnCoreApplicationBrowserPluginApi", + "section": "def-common.ApplicationStart", + "text": "ApplicationStart" + } + ], + "path": "packages/kbn-search-errors/src/painless_error.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + } + ], + "functions": [ + { + "parentPluginId": "@kbn/search-errors", + "id": "def-common.isEsError", + "type": "Function", + "tags": [], + "label": "isEsError", + "description": [ + "\nChecks if a given errors originated from Elasticsearch.\nThose params are assigned to the attributes property of an error.\n" + ], + "signature": [ + "(e: any) => boolean" + ], + "path": "packages/kbn-search-errors/src/es_error.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-errors", + "id": "def-common.isEsError.$1", + "type": "Any", + "tags": [], + "label": "e", + "description": [], + "signature": [ + "any" + ], + "path": "packages/kbn-search-errors/src/es_error.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/search-errors", + "id": "def-common.isPainlessError", + "type": "Function", + "tags": [], + "label": "isPainlessError", + "description": [], + "signature": [ + "(err: Error | ", + { + "pluginId": "@kbn/search-errors", + "scope": "common", + "docId": "kibKbnSearchErrorsPluginApi", + "section": "def-common.IEsError", + "text": "IEsError" + }, + ") => boolean" + ], + "path": "packages/kbn-search-errors/src/painless_error.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-errors", + "id": "def-common.isPainlessError.$1", + "type": "CompoundType", + "tags": [], + "label": "err", + "description": [], + "signature": [ + "Error | ", + { + "pluginId": "@kbn/search-errors", + "scope": "common", + "docId": "kibKbnSearchErrorsPluginApi", + "section": "def-common.IEsError", + "text": "IEsError" + } + ], + "path": "packages/kbn-search-errors/src/painless_error.tsx", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/search-errors", + "id": "def-common.renderSearchError", + "type": "Function", + "tags": [], + "label": "renderSearchError", + "description": [], + "signature": [ + "({\n error,\n application,\n}: { error: Error; application: ", + { + "pluginId": "@kbn/core-application-browser", + "scope": "common", + "docId": "kibKbnCoreApplicationBrowserPluginApi", + "section": "def-common.ApplicationStart", + "text": "ApplicationStart" + }, + "; }) => { title: string; body: React.ReactNode; actions?: React.ReactNode[] | undefined; } | undefined" + ], + "path": "packages/kbn-search-errors/src/render_search_error.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-errors", + "id": "def-common.renderSearchError.$1", + "type": "Object", + "tags": [], + "label": "{\n error,\n application,\n}", + "description": [], + "path": "packages/kbn-search-errors/src/render_search_error.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/search-errors", + "id": "def-common.renderSearchError.$1.error", + "type": "Object", + "tags": [], + "label": "error", + "description": [], + "signature": [ + "Error" + ], + "path": "packages/kbn-search-errors/src/render_search_error.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/search-errors", + "id": "def-common.renderSearchError.$1.application", + "type": "Object", + "tags": [], + "label": "application", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-application-browser", + "scope": "common", + "docId": "kibKbnCoreApplicationBrowserPluginApi", + "section": "def-common.ApplicationStart", + "text": "ApplicationStart" + } + ], + "path": "packages/kbn-search-errors/src/render_search_error.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [], + "enums": [], + "misc": [ + { + "parentPluginId": "@kbn/search-errors", + "id": "def-common.IEsError", + "type": "Type", + "tags": [], + "label": "IEsError", + "description": [], + "signature": [ + { + "pluginId": "kibanaUtils", + "scope": "common", + "docId": "kibKibanaUtilsPluginApi", + "section": "def-common.KibanaServerError", + "text": "KibanaServerError" + }, + "" + ], + "path": "packages/kbn-search-errors/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_search_errors.mdx b/api_docs/kbn_search_errors.mdx new file mode 100644 index 0000000000000..61fac6e4982b7 --- /dev/null +++ b/api_docs/kbn_search_errors.mdx @@ -0,0 +1,36 @@ +--- +#### +#### This document is auto-generated and is meant to be viewed inside our experimental, new docs system. +#### Reach out in #docs-engineering for more info. +#### +id: kibKbnSearchErrorsPluginApi +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: 2023-12-19 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/search-errors'] +--- +import kbnSearchErrorsObj from './kbn_search_errors.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 | +|-------------------|-----------|------------------------|-----------------| +| 27 | 1 | 26 | 0 | + +## Common + +### Functions + + +### Classes + + +### Consts, variables and types + + diff --git a/api_docs/kbn_search_index_documents.devdocs.json b/api_docs/kbn_search_index_documents.devdocs.json index a19264cf15a01..a68dcae8b5ffd 100644 --- a/api_docs/kbn_search_index_documents.devdocs.json +++ b/api_docs/kbn_search_index_documents.devdocs.json @@ -98,8 +98,8 @@ "pluginId": "@kbn/core-elasticsearch-server", "scope": "common", "docId": "kibKbnCoreElasticsearchServerPluginApi", - "section": "def-common.IScopedClusterClient", - "text": "IScopedClusterClient" + "section": "def-common.ElasticsearchClient", + "text": "ElasticsearchClient" }, ", indexName: string, query?: string | undefined, from?: number, size?: number) => Promise<", { @@ -129,8 +129,8 @@ "pluginId": "@kbn/core-elasticsearch-server", "scope": "common", "docId": "kibKbnCoreElasticsearchServerPluginApi", - "section": "def-common.IScopedClusterClient", - "text": "IScopedClusterClient" + "section": "def-common.ElasticsearchClient", + "text": "ElasticsearchClient" } ], "path": "packages/kbn-search-index-documents/lib/fetch_search_results.ts", diff --git a/api_docs/kbn_search_index_documents.mdx b/api_docs/kbn_search_index_documents.mdx index 449e828cc7a36..5a7c63aa4674e 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: 2023-12-06 +date: 2023-12-19 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 b64bec1fe94ab..340f50dfc6398 100644 --- a/api_docs/kbn_search_response_warnings.mdx +++ b/api_docs/kbn_search_response_warnings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-search-response-warnings title: "@kbn/search-response-warnings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/search-response-warnings plugin -date: 2023-12-06 +date: 2023-12-19 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 5cc0b256138b2..8bc2b68a4171d 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: 2023-12-06 +date: 2023-12-19 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 77936bae34bd7..952aaa3d69a10 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: 2023-12-06 +date: 2023-12-19 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 0e8518910830a..07507bdf6810f 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: 2023-12-06 +date: 2023-12-19 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 9acb2c9f27b96..97bfe358dbfdb 100644 --- a/api_docs/kbn_security_solution_features.mdx +++ b/api_docs/kbn_security_solution_features.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-features title: "@kbn/security-solution-features" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-features plugin -date: 2023-12-06 +date: 2023-12-19 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 714e77a74e0f9..2ea6cc41adc0a 100644 --- a/api_docs/kbn_security_solution_navigation.mdx +++ b/api_docs/kbn_security_solution_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-navigation title: "@kbn/security-solution-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-navigation plugin -date: 2023-12-06 +date: 2023-12-19 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 111c64f6fee50..4c59b624135c1 100644 --- a/api_docs/kbn_security_solution_side_nav.mdx +++ b/api_docs/kbn_security_solution_side_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-side-nav title: "@kbn/security-solution-side-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-side-nav plugin -date: 2023-12-06 +date: 2023-12-19 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 eb11bf6db7ad8..8a645d1ddbc8a 100644 --- a/api_docs/kbn_security_solution_storybook_config.mdx +++ b/api_docs/kbn_security_solution_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-security-solution-storybook-config title: "@kbn/security-solution-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/security-solution-storybook-config plugin -date: 2023-12-06 +date: 2023-12-19 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 4671d3148d551..25a9b2bc1a650 100644 --- a/api_docs/kbn_securitysolution_autocomplete.mdx +++ b/api_docs/kbn_securitysolution_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-autocomplete title: "@kbn/securitysolution-autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-autocomplete plugin -date: 2023-12-06 +date: 2023-12-19 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 c264ee94136aa..727c6a574826d 100644 --- a/api_docs/kbn_securitysolution_data_table.mdx +++ b/api_docs/kbn_securitysolution_data_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-data-table title: "@kbn/securitysolution-data-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-data-table plugin -date: 2023-12-06 +date: 2023-12-19 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 9e3fb696f7528..f4a567a70eaff 100644 --- a/api_docs/kbn_securitysolution_ecs.mdx +++ b/api_docs/kbn_securitysolution_ecs.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-ecs title: "@kbn/securitysolution-ecs" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-ecs plugin -date: 2023-12-06 +date: 2023-12-19 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 826c61fedd9ee..4286c5b9c9648 100644 --- a/api_docs/kbn_securitysolution_es_utils.mdx +++ b/api_docs/kbn_securitysolution_es_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-es-utils title: "@kbn/securitysolution-es-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-es-utils plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/securitysolution-es-utils'] --- import kbnSecuritysolutionEsUtilsObj from './kbn_securitysolution_es_utils.devdocs.json'; diff --git a/api_docs/kbn_securitysolution_exception_list_components.devdocs.json b/api_docs/kbn_securitysolution_exception_list_components.devdocs.json index 43eab9a3b5012..e264ff91b5597 100644 --- a/api_docs/kbn_securitysolution_exception_list_components.devdocs.json +++ b/api_docs/kbn_securitysolution_exception_list_components.devdocs.json @@ -834,7 +834,7 @@ "label": "formattedDateComponent", "description": [], "signature": [ - "\"symbol\" | \"object\" | \"big\" | \"link\" | \"small\" | \"sub\" | \"sup\" | \"source\" | \"desc\" | \"filter\" | \"text\" | \"map\" | \"head\" | React.ComponentType | \"slot\" | \"style\" | \"title\" | \"meta\" | \"data\" | \"pattern\" | \"summary\" | \"template\" | \"span\" | \"main\" | \"path\" | \"form\" | \"body\" | \"q\" | \"label\" | \"progress\" | \"legend\" | \"article\" | \"image\" | \"menu\" | \"stop\" | \"base\" | \"s\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"p\" | \"canvas\" | \"svg\" | \"select\" | \"output\" | \"script\" | \"time\" | \"mask\" | \"input\" | \"a\" | \"abbr\" | \"address\" | \"area\" | \"aside\" | \"audio\" | \"b\" | \"bdi\" | \"bdo\" | \"blockquote\" | \"br\" | \"button\" | \"caption\" | \"cite\" | \"code\" | \"col\" | \"colgroup\" | \"datalist\" | \"dd\" | \"del\" | \"details\" | \"dfn\" | \"dialog\" | \"div\" | \"dl\" | \"dt\" | \"em\" | \"embed\" | \"fieldset\" | \"figcaption\" | \"figure\" | \"footer\" | \"header\" | \"hgroup\" | \"hr\" | \"html\" | \"i\" | \"iframe\" | \"img\" | \"ins\" | \"kbd\" | \"keygen\" | \"li\" | \"mark\" | \"menuitem\" | \"meter\" | \"nav\" | \"noindex\" | \"noscript\" | \"ol\" | \"optgroup\" | \"option\" | \"param\" | \"picture\" | \"pre\" | \"rp\" | \"rt\" | \"ruby\" | \"samp\" | \"section\" | \"strong\" | \"table\" | \"tbody\" | \"td\" | \"textarea\" | \"tfoot\" | \"th\" | \"thead\" | \"tr\" | \"track\" | \"u\" | \"ul\" | \"var\" | \"video\" | \"wbr\" | \"webview\" | \"animate\" | \"animateMotion\" | \"animateTransform\" | \"circle\" | \"clipPath\" | \"defs\" | \"ellipse\" | \"feBlend\" | \"feColorMatrix\" | \"feComponentTransfer\" | \"feComposite\" | \"feConvolveMatrix\" | \"feDiffuseLighting\" | \"feDisplacementMap\" | \"feDistantLight\" | \"feDropShadow\" | \"feFlood\" | \"feFuncA\" | \"feFuncB\" | \"feFuncG\" | \"feFuncR\" | \"feGaussianBlur\" | \"feImage\" | \"feMerge\" | \"feMergeNode\" | \"feMorphology\" | \"feOffset\" | \"fePointLight\" | \"feSpecularLighting\" | \"feSpotLight\" | \"feTile\" | \"feTurbulence\" | \"foreignObject\" | \"g\" | \"line\" | \"linearGradient\" | \"marker\" | \"metadata\" | \"mpath\" | \"polygon\" | \"polyline\" | \"radialGradient\" | \"rect\" | \"switch\" | \"textPath\" | \"tspan\" | \"use\" | \"view\"" + "\"symbol\" | \"object\" | \"big\" | \"link\" | \"small\" | \"sub\" | \"sup\" | \"source\" | \"desc\" | \"filter\" | \"text\" | \"map\" | \"head\" | React.ComponentType | \"slot\" | \"style\" | \"title\" | \"meta\" | \"data\" | \"pattern\" | \"summary\" | \"template\" | \"span\" | \"main\" | \"path\" | \"form\" | \"body\" | \"q\" | \"label\" | \"progress\" | \"article\" | \"image\" | \"menu\" | \"stop\" | \"base\" | \"s\" | \"legend\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"p\" | \"canvas\" | \"svg\" | \"select\" | \"output\" | \"script\" | \"time\" | \"mask\" | \"input\" | \"a\" | \"abbr\" | \"address\" | \"area\" | \"aside\" | \"audio\" | \"b\" | \"bdi\" | \"bdo\" | \"blockquote\" | \"br\" | \"button\" | \"caption\" | \"cite\" | \"code\" | \"col\" | \"colgroup\" | \"datalist\" | \"dd\" | \"del\" | \"details\" | \"dfn\" | \"dialog\" | \"div\" | \"dl\" | \"dt\" | \"em\" | \"embed\" | \"fieldset\" | \"figcaption\" | \"figure\" | \"footer\" | \"header\" | \"hgroup\" | \"hr\" | \"html\" | \"i\" | \"iframe\" | \"img\" | \"ins\" | \"kbd\" | \"keygen\" | \"li\" | \"mark\" | \"menuitem\" | \"meter\" | \"nav\" | \"noindex\" | \"noscript\" | \"ol\" | \"optgroup\" | \"option\" | \"param\" | \"picture\" | \"pre\" | \"rp\" | \"rt\" | \"ruby\" | \"samp\" | \"section\" | \"strong\" | \"table\" | \"tbody\" | \"td\" | \"textarea\" | \"tfoot\" | \"th\" | \"thead\" | \"tr\" | \"track\" | \"u\" | \"ul\" | \"var\" | \"video\" | \"wbr\" | \"webview\" | \"animate\" | \"animateMotion\" | \"animateTransform\" | \"circle\" | \"clipPath\" | \"defs\" | \"ellipse\" | \"feBlend\" | \"feColorMatrix\" | \"feComponentTransfer\" | \"feComposite\" | \"feConvolveMatrix\" | \"feDiffuseLighting\" | \"feDisplacementMap\" | \"feDistantLight\" | \"feDropShadow\" | \"feFlood\" | \"feFuncA\" | \"feFuncB\" | \"feFuncG\" | \"feFuncR\" | \"feGaussianBlur\" | \"feImage\" | \"feMerge\" | \"feMergeNode\" | \"feMorphology\" | \"feOffset\" | \"fePointLight\" | \"feSpecularLighting\" | \"feSpotLight\" | \"feTile\" | \"feTurbulence\" | \"foreignObject\" | \"g\" | \"line\" | \"linearGradient\" | \"marker\" | \"metadata\" | \"mpath\" | \"polygon\" | \"polyline\" | \"radialGradient\" | \"rect\" | \"switch\" | \"textPath\" | \"tspan\" | \"use\" | \"view\"" ], "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/index.tsx", "deprecated": false, @@ -848,7 +848,7 @@ "label": "securityLinkAnchorComponent", "description": [], "signature": [ - "\"symbol\" | \"object\" | \"big\" | \"link\" | \"small\" | \"sub\" | \"sup\" | \"source\" | \"desc\" | \"filter\" | \"text\" | \"map\" | \"head\" | React.ComponentType | \"slot\" | \"style\" | \"title\" | \"meta\" | \"data\" | \"pattern\" | \"summary\" | \"template\" | \"span\" | \"main\" | \"path\" | \"form\" | \"body\" | \"q\" | \"label\" | \"progress\" | \"legend\" | \"article\" | \"image\" | \"menu\" | \"stop\" | \"base\" | \"s\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"p\" | \"canvas\" | \"svg\" | \"select\" | \"output\" | \"script\" | \"time\" | \"mask\" | \"input\" | \"a\" | \"abbr\" | \"address\" | \"area\" | \"aside\" | \"audio\" | \"b\" | \"bdi\" | \"bdo\" | \"blockquote\" | \"br\" | \"button\" | \"caption\" | \"cite\" | \"code\" | \"col\" | \"colgroup\" | \"datalist\" | \"dd\" | \"del\" | \"details\" | \"dfn\" | \"dialog\" | \"div\" | \"dl\" | \"dt\" | \"em\" | \"embed\" | \"fieldset\" | \"figcaption\" | \"figure\" | \"footer\" | \"header\" | \"hgroup\" | \"hr\" | \"html\" | \"i\" | \"iframe\" | \"img\" | \"ins\" | \"kbd\" | \"keygen\" | \"li\" | \"mark\" | \"menuitem\" | \"meter\" | \"nav\" | \"noindex\" | \"noscript\" | \"ol\" | \"optgroup\" | \"option\" | \"param\" | \"picture\" | \"pre\" | \"rp\" | \"rt\" | \"ruby\" | \"samp\" | \"section\" | \"strong\" | \"table\" | \"tbody\" | \"td\" | \"textarea\" | \"tfoot\" | \"th\" | \"thead\" | \"tr\" | \"track\" | \"u\" | \"ul\" | \"var\" | \"video\" | \"wbr\" | \"webview\" | \"animate\" | \"animateMotion\" | \"animateTransform\" | \"circle\" | \"clipPath\" | \"defs\" | \"ellipse\" | \"feBlend\" | \"feColorMatrix\" | \"feComponentTransfer\" | \"feComposite\" | \"feConvolveMatrix\" | \"feDiffuseLighting\" | \"feDisplacementMap\" | \"feDistantLight\" | \"feDropShadow\" | \"feFlood\" | \"feFuncA\" | \"feFuncB\" | \"feFuncG\" | \"feFuncR\" | \"feGaussianBlur\" | \"feImage\" | \"feMerge\" | \"feMergeNode\" | \"feMorphology\" | \"feOffset\" | \"fePointLight\" | \"feSpecularLighting\" | \"feSpotLight\" | \"feTile\" | \"feTurbulence\" | \"foreignObject\" | \"g\" | \"line\" | \"linearGradient\" | \"marker\" | \"metadata\" | \"mpath\" | \"polygon\" | \"polyline\" | \"radialGradient\" | \"rect\" | \"switch\" | \"textPath\" | \"tspan\" | \"use\" | \"view\"" + "\"symbol\" | \"object\" | \"big\" | \"link\" | \"small\" | \"sub\" | \"sup\" | \"source\" | \"desc\" | \"filter\" | \"text\" | \"map\" | \"head\" | React.ComponentType | \"slot\" | \"style\" | \"title\" | \"meta\" | \"data\" | \"pattern\" | \"summary\" | \"template\" | \"span\" | \"main\" | \"path\" | \"form\" | \"body\" | \"q\" | \"label\" | \"progress\" | \"article\" | \"image\" | \"menu\" | \"stop\" | \"base\" | \"s\" | \"legend\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"p\" | \"canvas\" | \"svg\" | \"select\" | \"output\" | \"script\" | \"time\" | \"mask\" | \"input\" | \"a\" | \"abbr\" | \"address\" | \"area\" | \"aside\" | \"audio\" | \"b\" | \"bdi\" | \"bdo\" | \"blockquote\" | \"br\" | \"button\" | \"caption\" | \"cite\" | \"code\" | \"col\" | \"colgroup\" | \"datalist\" | \"dd\" | \"del\" | \"details\" | \"dfn\" | \"dialog\" | \"div\" | \"dl\" | \"dt\" | \"em\" | \"embed\" | \"fieldset\" | \"figcaption\" | \"figure\" | \"footer\" | \"header\" | \"hgroup\" | \"hr\" | \"html\" | \"i\" | \"iframe\" | \"img\" | \"ins\" | \"kbd\" | \"keygen\" | \"li\" | \"mark\" | \"menuitem\" | \"meter\" | \"nav\" | \"noindex\" | \"noscript\" | \"ol\" | \"optgroup\" | \"option\" | \"param\" | \"picture\" | \"pre\" | \"rp\" | \"rt\" | \"ruby\" | \"samp\" | \"section\" | \"strong\" | \"table\" | \"tbody\" | \"td\" | \"textarea\" | \"tfoot\" | \"th\" | \"thead\" | \"tr\" | \"track\" | \"u\" | \"ul\" | \"var\" | \"video\" | \"wbr\" | \"webview\" | \"animate\" | \"animateMotion\" | \"animateTransform\" | \"circle\" | \"clipPath\" | \"defs\" | \"ellipse\" | \"feBlend\" | \"feColorMatrix\" | \"feComponentTransfer\" | \"feComposite\" | \"feConvolveMatrix\" | \"feDiffuseLighting\" | \"feDisplacementMap\" | \"feDistantLight\" | \"feDropShadow\" | \"feFlood\" | \"feFuncA\" | \"feFuncB\" | \"feFuncG\" | \"feFuncR\" | \"feGaussianBlur\" | \"feImage\" | \"feMerge\" | \"feMergeNode\" | \"feMorphology\" | \"feOffset\" | \"fePointLight\" | \"feSpecularLighting\" | \"feSpotLight\" | \"feTile\" | \"feTurbulence\" | \"foreignObject\" | \"g\" | \"line\" | \"linearGradient\" | \"marker\" | \"metadata\" | \"mpath\" | \"polygon\" | \"polyline\" | \"radialGradient\" | \"rect\" | \"switch\" | \"textPath\" | \"tspan\" | \"use\" | \"view\"" ], "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/meta/index.tsx", "deprecated": false, @@ -987,7 +987,7 @@ "label": "securityLinkAnchorComponent", "description": [], "signature": [ - "\"symbol\" | \"object\" | \"big\" | \"link\" | \"small\" | \"sub\" | \"sup\" | \"source\" | \"desc\" | \"filter\" | \"text\" | \"map\" | \"head\" | React.ComponentType | \"slot\" | \"style\" | \"title\" | \"meta\" | \"data\" | \"pattern\" | \"summary\" | \"template\" | \"span\" | \"main\" | \"path\" | \"form\" | \"body\" | \"q\" | \"label\" | \"progress\" | \"legend\" | \"article\" | \"image\" | \"menu\" | \"stop\" | \"base\" | \"s\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"p\" | \"canvas\" | \"svg\" | \"select\" | \"output\" | \"script\" | \"time\" | \"mask\" | \"input\" | \"a\" | \"abbr\" | \"address\" | \"area\" | \"aside\" | \"audio\" | \"b\" | \"bdi\" | \"bdo\" | \"blockquote\" | \"br\" | \"button\" | \"caption\" | \"cite\" | \"code\" | \"col\" | \"colgroup\" | \"datalist\" | \"dd\" | \"del\" | \"details\" | \"dfn\" | \"dialog\" | \"div\" | \"dl\" | \"dt\" | \"em\" | \"embed\" | \"fieldset\" | \"figcaption\" | \"figure\" | \"footer\" | \"header\" | \"hgroup\" | \"hr\" | \"html\" | \"i\" | \"iframe\" | \"img\" | \"ins\" | \"kbd\" | \"keygen\" | \"li\" | \"mark\" | \"menuitem\" | \"meter\" | \"nav\" | \"noindex\" | \"noscript\" | \"ol\" | \"optgroup\" | \"option\" | \"param\" | \"picture\" | \"pre\" | \"rp\" | \"rt\" | \"ruby\" | \"samp\" | \"section\" | \"strong\" | \"table\" | \"tbody\" | \"td\" | \"textarea\" | \"tfoot\" | \"th\" | \"thead\" | \"tr\" | \"track\" | \"u\" | \"ul\" | \"var\" | \"video\" | \"wbr\" | \"webview\" | \"animate\" | \"animateMotion\" | \"animateTransform\" | \"circle\" | \"clipPath\" | \"defs\" | \"ellipse\" | \"feBlend\" | \"feColorMatrix\" | \"feComponentTransfer\" | \"feComposite\" | \"feConvolveMatrix\" | \"feDiffuseLighting\" | \"feDisplacementMap\" | \"feDistantLight\" | \"feDropShadow\" | \"feFlood\" | \"feFuncA\" | \"feFuncB\" | \"feFuncG\" | \"feFuncR\" | \"feGaussianBlur\" | \"feImage\" | \"feMerge\" | \"feMergeNode\" | \"feMorphology\" | \"feOffset\" | \"fePointLight\" | \"feSpecularLighting\" | \"feSpotLight\" | \"feTile\" | \"feTurbulence\" | \"foreignObject\" | \"g\" | \"line\" | \"linearGradient\" | \"marker\" | \"metadata\" | \"mpath\" | \"polygon\" | \"polyline\" | \"radialGradient\" | \"rect\" | \"switch\" | \"textPath\" | \"tspan\" | \"use\" | \"view\"" + "\"symbol\" | \"object\" | \"big\" | \"link\" | \"small\" | \"sub\" | \"sup\" | \"source\" | \"desc\" | \"filter\" | \"text\" | \"map\" | \"head\" | React.ComponentType | \"slot\" | \"style\" | \"title\" | \"meta\" | \"data\" | \"pattern\" | \"summary\" | \"template\" | \"span\" | \"main\" | \"path\" | \"form\" | \"body\" | \"q\" | \"label\" | \"progress\" | \"article\" | \"image\" | \"menu\" | \"stop\" | \"base\" | \"s\" | \"legend\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"p\" | \"canvas\" | \"svg\" | \"select\" | \"output\" | \"script\" | \"time\" | \"mask\" | \"input\" | \"a\" | \"abbr\" | \"address\" | \"area\" | \"aside\" | \"audio\" | \"b\" | \"bdi\" | \"bdo\" | \"blockquote\" | \"br\" | \"button\" | \"caption\" | \"cite\" | \"code\" | \"col\" | \"colgroup\" | \"datalist\" | \"dd\" | \"del\" | \"details\" | \"dfn\" | \"dialog\" | \"div\" | \"dl\" | \"dt\" | \"em\" | \"embed\" | \"fieldset\" | \"figcaption\" | \"figure\" | \"footer\" | \"header\" | \"hgroup\" | \"hr\" | \"html\" | \"i\" | \"iframe\" | \"img\" | \"ins\" | \"kbd\" | \"keygen\" | \"li\" | \"mark\" | \"menuitem\" | \"meter\" | \"nav\" | \"noindex\" | \"noscript\" | \"ol\" | \"optgroup\" | \"option\" | \"param\" | \"picture\" | \"pre\" | \"rp\" | \"rt\" | \"ruby\" | \"samp\" | \"section\" | \"strong\" | \"table\" | \"tbody\" | \"td\" | \"textarea\" | \"tfoot\" | \"th\" | \"thead\" | \"tr\" | \"track\" | \"u\" | \"ul\" | \"var\" | \"video\" | \"wbr\" | \"webview\" | \"animate\" | \"animateMotion\" | \"animateTransform\" | \"circle\" | \"clipPath\" | \"defs\" | \"ellipse\" | \"feBlend\" | \"feColorMatrix\" | \"feComponentTransfer\" | \"feComposite\" | \"feConvolveMatrix\" | \"feDiffuseLighting\" | \"feDisplacementMap\" | \"feDistantLight\" | \"feDropShadow\" | \"feFlood\" | \"feFuncA\" | \"feFuncB\" | \"feFuncG\" | \"feFuncR\" | \"feGaussianBlur\" | \"feImage\" | \"feMerge\" | \"feMergeNode\" | \"feMorphology\" | \"feOffset\" | \"fePointLight\" | \"feSpecularLighting\" | \"feSpotLight\" | \"feTile\" | \"feTurbulence\" | \"foreignObject\" | \"g\" | \"line\" | \"linearGradient\" | \"marker\" | \"metadata\" | \"mpath\" | \"polygon\" | \"polyline\" | \"radialGradient\" | \"rect\" | \"switch\" | \"textPath\" | \"tspan\" | \"use\" | \"view\"" ], "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.tsx", "deprecated": false, @@ -1001,7 +1001,7 @@ "label": "formattedDateComponent", "description": [], "signature": [ - "\"symbol\" | \"object\" | \"big\" | \"link\" | \"small\" | \"sub\" | \"sup\" | \"source\" | \"desc\" | \"filter\" | \"text\" | \"map\" | \"head\" | React.ComponentType | \"slot\" | \"style\" | \"title\" | \"meta\" | \"data\" | \"pattern\" | \"summary\" | \"template\" | \"span\" | \"main\" | \"path\" | \"form\" | \"body\" | \"q\" | \"label\" | \"progress\" | \"legend\" | \"article\" | \"image\" | \"menu\" | \"stop\" | \"base\" | \"s\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"p\" | \"canvas\" | \"svg\" | \"select\" | \"output\" | \"script\" | \"time\" | \"mask\" | \"input\" | \"a\" | \"abbr\" | \"address\" | \"area\" | \"aside\" | \"audio\" | \"b\" | \"bdi\" | \"bdo\" | \"blockquote\" | \"br\" | \"button\" | \"caption\" | \"cite\" | \"code\" | \"col\" | \"colgroup\" | \"datalist\" | \"dd\" | \"del\" | \"details\" | \"dfn\" | \"dialog\" | \"div\" | \"dl\" | \"dt\" | \"em\" | \"embed\" | \"fieldset\" | \"figcaption\" | \"figure\" | \"footer\" | \"header\" | \"hgroup\" | \"hr\" | \"html\" | \"i\" | \"iframe\" | \"img\" | \"ins\" | \"kbd\" | \"keygen\" | \"li\" | \"mark\" | \"menuitem\" | \"meter\" | \"nav\" | \"noindex\" | \"noscript\" | \"ol\" | \"optgroup\" | \"option\" | \"param\" | \"picture\" | \"pre\" | \"rp\" | \"rt\" | \"ruby\" | \"samp\" | \"section\" | \"strong\" | \"table\" | \"tbody\" | \"td\" | \"textarea\" | \"tfoot\" | \"th\" | \"thead\" | \"tr\" | \"track\" | \"u\" | \"ul\" | \"var\" | \"video\" | \"wbr\" | \"webview\" | \"animate\" | \"animateMotion\" | \"animateTransform\" | \"circle\" | \"clipPath\" | \"defs\" | \"ellipse\" | \"feBlend\" | \"feColorMatrix\" | \"feComponentTransfer\" | \"feComposite\" | \"feConvolveMatrix\" | \"feDiffuseLighting\" | \"feDisplacementMap\" | \"feDistantLight\" | \"feDropShadow\" | \"feFlood\" | \"feFuncA\" | \"feFuncB\" | \"feFuncG\" | \"feFuncR\" | \"feGaussianBlur\" | \"feImage\" | \"feMerge\" | \"feMergeNode\" | \"feMorphology\" | \"feOffset\" | \"fePointLight\" | \"feSpecularLighting\" | \"feSpotLight\" | \"feTile\" | \"feTurbulence\" | \"foreignObject\" | \"g\" | \"line\" | \"linearGradient\" | \"marker\" | \"metadata\" | \"mpath\" | \"polygon\" | \"polyline\" | \"radialGradient\" | \"rect\" | \"switch\" | \"textPath\" | \"tspan\" | \"use\" | \"view\"" + "\"symbol\" | \"object\" | \"big\" | \"link\" | \"small\" | \"sub\" | \"sup\" | \"source\" | \"desc\" | \"filter\" | \"text\" | \"map\" | \"head\" | React.ComponentType | \"slot\" | \"style\" | \"title\" | \"meta\" | \"data\" | \"pattern\" | \"summary\" | \"template\" | \"span\" | \"main\" | \"path\" | \"form\" | \"body\" | \"q\" | \"label\" | \"progress\" | \"article\" | \"image\" | \"menu\" | \"stop\" | \"base\" | \"s\" | \"legend\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"p\" | \"canvas\" | \"svg\" | \"select\" | \"output\" | \"script\" | \"time\" | \"mask\" | \"input\" | \"a\" | \"abbr\" | \"address\" | \"area\" | \"aside\" | \"audio\" | \"b\" | \"bdi\" | \"bdo\" | \"blockquote\" | \"br\" | \"button\" | \"caption\" | \"cite\" | \"code\" | \"col\" | \"colgroup\" | \"datalist\" | \"dd\" | \"del\" | \"details\" | \"dfn\" | \"dialog\" | \"div\" | \"dl\" | \"dt\" | \"em\" | \"embed\" | \"fieldset\" | \"figcaption\" | \"figure\" | \"footer\" | \"header\" | \"hgroup\" | \"hr\" | \"html\" | \"i\" | \"iframe\" | \"img\" | \"ins\" | \"kbd\" | \"keygen\" | \"li\" | \"mark\" | \"menuitem\" | \"meter\" | \"nav\" | \"noindex\" | \"noscript\" | \"ol\" | \"optgroup\" | \"option\" | \"param\" | \"picture\" | \"pre\" | \"rp\" | \"rt\" | \"ruby\" | \"samp\" | \"section\" | \"strong\" | \"table\" | \"tbody\" | \"td\" | \"textarea\" | \"tfoot\" | \"th\" | \"thead\" | \"tr\" | \"track\" | \"u\" | \"ul\" | \"var\" | \"video\" | \"wbr\" | \"webview\" | \"animate\" | \"animateMotion\" | \"animateTransform\" | \"circle\" | \"clipPath\" | \"defs\" | \"ellipse\" | \"feBlend\" | \"feColorMatrix\" | \"feComponentTransfer\" | \"feComposite\" | \"feConvolveMatrix\" | \"feDiffuseLighting\" | \"feDisplacementMap\" | \"feDistantLight\" | \"feDropShadow\" | \"feFlood\" | \"feFuncA\" | \"feFuncB\" | \"feFuncG\" | \"feFuncR\" | \"feGaussianBlur\" | \"feImage\" | \"feMerge\" | \"feMergeNode\" | \"feMorphology\" | \"feOffset\" | \"fePointLight\" | \"feSpecularLighting\" | \"feSpotLight\" | \"feTile\" | \"feTurbulence\" | \"foreignObject\" | \"g\" | \"line\" | \"linearGradient\" | \"marker\" | \"metadata\" | \"mpath\" | \"polygon\" | \"polyline\" | \"radialGradient\" | \"rect\" | \"switch\" | \"textPath\" | \"tspan\" | \"use\" | \"view\"" ], "path": "packages/kbn-securitysolution-exception-list-components/src/exception_item_card/exception_item_card.tsx", "deprecated": false, diff --git a/api_docs/kbn_securitysolution_exception_list_components.mdx b/api_docs/kbn_securitysolution_exception_list_components.mdx index 52146e6c49e52..ddb253c6004f5 100644 --- a/api_docs/kbn_securitysolution_exception_list_components.mdx +++ b/api_docs/kbn_securitysolution_exception_list_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-exception-list-components title: "@kbn/securitysolution-exception-list-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-exception-list-components plugin -date: 2023-12-06 +date: 2023-12-19 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 deae59a654e2c..f224c136aa720 100644 --- a/api_docs/kbn_securitysolution_grouping.mdx +++ b/api_docs/kbn_securitysolution_grouping.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-grouping title: "@kbn/securitysolution-grouping" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-grouping plugin -date: 2023-12-06 +date: 2023-12-19 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 8a1af33e073f5..919cda06b8288 100644 --- a/api_docs/kbn_securitysolution_hook_utils.mdx +++ b/api_docs/kbn_securitysolution_hook_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-hook-utils title: "@kbn/securitysolution-hook-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-hook-utils plugin -date: 2023-12-06 +date: 2023-12-19 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 2a49be0f4f2b3..be01eefef4e58 100644 --- a/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_alerting_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-alerting-types title: "@kbn/securitysolution-io-ts-alerting-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-alerting-types plugin -date: 2023-12-06 +date: 2023-12-19 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 9d3f2ef24d493..e093e781ea1db 100644 --- a/api_docs/kbn_securitysolution_io_ts_list_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_list_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-list-types title: "@kbn/securitysolution-io-ts-list-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-list-types plugin -date: 2023-12-06 +date: 2023-12-19 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 73c112557e1fc..c86fc8aeae166 100644 --- a/api_docs/kbn_securitysolution_io_ts_types.mdx +++ b/api_docs/kbn_securitysolution_io_ts_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-types title: "@kbn/securitysolution-io-ts-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-types plugin -date: 2023-12-06 +date: 2023-12-19 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 d7875f5769e72..4add9df89302b 100644 --- a/api_docs/kbn_securitysolution_io_ts_utils.mdx +++ b/api_docs/kbn_securitysolution_io_ts_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-io-ts-utils title: "@kbn/securitysolution-io-ts-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-io-ts-utils plugin -date: 2023-12-06 +date: 2023-12-19 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 b0a099ea60b80..ca1fda2120a4b 100644 --- a/api_docs/kbn_securitysolution_list_api.mdx +++ b/api_docs/kbn_securitysolution_list_api.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-api title: "@kbn/securitysolution-list-api" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-api plugin -date: 2023-12-06 +date: 2023-12-19 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 86cb498c094f4..2ac4d1be857ed 100644 --- a/api_docs/kbn_securitysolution_list_constants.mdx +++ b/api_docs/kbn_securitysolution_list_constants.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-constants title: "@kbn/securitysolution-list-constants" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-constants plugin -date: 2023-12-06 +date: 2023-12-19 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 29e29c53fbe6d..4042efdb33501 100644 --- a/api_docs/kbn_securitysolution_list_hooks.mdx +++ b/api_docs/kbn_securitysolution_list_hooks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-hooks title: "@kbn/securitysolution-list-hooks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-hooks plugin -date: 2023-12-06 +date: 2023-12-19 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 83afc19633ad9..f2da8062625f4 100644 --- a/api_docs/kbn_securitysolution_list_utils.mdx +++ b/api_docs/kbn_securitysolution_list_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-list-utils title: "@kbn/securitysolution-list-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-list-utils plugin -date: 2023-12-06 +date: 2023-12-19 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 ac206a38e7b45..f5e0ead135f7d 100644 --- a/api_docs/kbn_securitysolution_rules.mdx +++ b/api_docs/kbn_securitysolution_rules.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-rules title: "@kbn/securitysolution-rules" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-rules plugin -date: 2023-12-06 +date: 2023-12-19 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 83648b6f409c8..c14208f53c527 100644 --- a/api_docs/kbn_securitysolution_t_grid.mdx +++ b/api_docs/kbn_securitysolution_t_grid.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-t-grid title: "@kbn/securitysolution-t-grid" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-t-grid plugin -date: 2023-12-06 +date: 2023-12-19 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 9583ecf805ba1..e2639d9d07fe7 100644 --- a/api_docs/kbn_securitysolution_utils.mdx +++ b/api_docs/kbn_securitysolution_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-securitysolution-utils title: "@kbn/securitysolution-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/securitysolution-utils plugin -date: 2023-12-06 +date: 2023-12-19 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 9d55f551dda86..45e895447d131 100644 --- a/api_docs/kbn_server_http_tools.mdx +++ b/api_docs/kbn_server_http_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-http-tools title: "@kbn/server-http-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-http-tools plugin -date: 2023-12-06 +date: 2023-12-19 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 f207367e9f1af..1f075b5381dde 100644 --- a/api_docs/kbn_server_route_repository.mdx +++ b/api_docs/kbn_server_route_repository.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-server-route-repository title: "@kbn/server-route-repository" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/server-route-repository plugin -date: 2023-12-06 +date: 2023-12-19 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 ccf54e481e2ea..df8244ef20d0e 100644 --- a/api_docs/kbn_serverless_common_settings.mdx +++ b/api_docs/kbn_serverless_common_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-common-settings title: "@kbn/serverless-common-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-common-settings plugin -date: 2023-12-06 +date: 2023-12-19 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 7a46e3996179d..203ac911d7a14 100644 --- a/api_docs/kbn_serverless_observability_settings.mdx +++ b/api_docs/kbn_serverless_observability_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-observability-settings title: "@kbn/serverless-observability-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-observability-settings plugin -date: 2023-12-06 +date: 2023-12-19 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 379c1a0264ef3..a2d578fdfee3f 100644 --- a/api_docs/kbn_serverless_project_switcher.mdx +++ b/api_docs/kbn_serverless_project_switcher.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-project-switcher title: "@kbn/serverless-project-switcher" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-project-switcher plugin -date: 2023-12-06 +date: 2023-12-19 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 55d7c1c36fcea..20f521277536d 100644 --- a/api_docs/kbn_serverless_search_settings.mdx +++ b/api_docs/kbn_serverless_search_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-search-settings title: "@kbn/serverless-search-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-search-settings plugin -date: 2023-12-06 +date: 2023-12-19 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 4a016b01805af..0ad3c61e9debc 100644 --- a/api_docs/kbn_serverless_security_settings.mdx +++ b/api_docs/kbn_serverless_security_settings.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-security-settings title: "@kbn/serverless-security-settings" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-security-settings plugin -date: 2023-12-06 +date: 2023-12-19 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 b1a25f7f9f5ba..1e658ebe23f4d 100644 --- a/api_docs/kbn_serverless_storybook_config.mdx +++ b/api_docs/kbn_serverless_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-serverless-storybook-config title: "@kbn/serverless-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/serverless-storybook-config plugin -date: 2023-12-06 +date: 2023-12-19 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 8de4e73655633..f912adcc82687 100644 --- a/api_docs/kbn_shared_svg.mdx +++ b/api_docs/kbn_shared_svg.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-svg title: "@kbn/shared-svg" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-svg plugin -date: 2023-12-06 +date: 2023-12-19 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 824718ab8870c..e6d9946bf8510 100644 --- a/api_docs/kbn_shared_ux_avatar_solution.mdx +++ b/api_docs/kbn_shared_ux_avatar_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-avatar-solution title: "@kbn/shared-ux-avatar-solution" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-avatar-solution plugin -date: 2023-12-06 +date: 2023-12-19 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 e2a8fdb5a0acd..9cbfbb081b215 100644 --- a/api_docs/kbn_shared_ux_button_exit_full_screen.mdx +++ b/api_docs/kbn_shared_ux_button_exit_full_screen.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-exit-full-screen title: "@kbn/shared-ux-button-exit-full-screen" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-exit-full-screen plugin -date: 2023-12-06 +date: 2023-12-19 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.devdocs.json b/api_docs/kbn_shared_ux_button_toolbar.devdocs.json index 3c4e59e38b544..1e2e1a3777596 100644 --- a/api_docs/kbn_shared_ux_button_toolbar.devdocs.json +++ b/api_docs/kbn_shared_ux_button_toolbar.devdocs.json @@ -471,7 +471,7 @@ "label": "Props", "description": [], "signature": [ - "{ fullWidth?: boolean | undefined; onClick?: React.MouseEventHandler | undefined; 'data-test-subj'?: string | undefined; isDisabled?: boolean | undefined; isLoading?: boolean | undefined; size?: \"m\" | \"s\" | undefined; as?: \"standard\" | undefined; fontWeight?: ToolbarButtonFontWeights | undefined; iconSide?: ", + "{ fullWidth?: boolean | undefined; 'aria-label'?: string | undefined; onClick?: React.MouseEventHandler | undefined; 'data-test-subj'?: string | undefined; isDisabled?: boolean | undefined; isLoading?: boolean | undefined; size?: \"m\" | \"s\" | undefined; as?: \"standard\" | undefined; fontWeight?: ToolbarButtonFontWeights | undefined; iconSide?: ", "ButtonContentIconSide", "; groupPosition?: ButtonPositions | undefined; hasArrow?: boolean | undefined; }" ], diff --git a/api_docs/kbn_shared_ux_button_toolbar.mdx b/api_docs/kbn_shared_ux_button_toolbar.mdx index 359360468a0b0..95271bebf4dbe 100644 --- a/api_docs/kbn_shared_ux_button_toolbar.mdx +++ b/api_docs/kbn_shared_ux_button_toolbar.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-button-toolbar title: "@kbn/shared-ux-button-toolbar" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-button-toolbar plugin -date: 2023-12-06 +date: 2023-12-19 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 3d00a164bbebb..676eba9a4c1a2 100644 --- a/api_docs/kbn_shared_ux_card_no_data.mdx +++ b/api_docs/kbn_shared_ux_card_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data title: "@kbn/shared-ux-card-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data plugin -date: 2023-12-06 +date: 2023-12-19 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 448fb42244833..daee8fd0085bf 100644 --- a/api_docs/kbn_shared_ux_card_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_card_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-card-no-data-mocks title: "@kbn/shared-ux-card-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-card-no-data-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 e2cb1623d9351..7cdc585433437 100644 --- a/api_docs/kbn_shared_ux_chrome_navigation.mdx +++ b/api_docs/kbn_shared_ux_chrome_navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-chrome-navigation title: "@kbn/shared-ux-chrome-navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-chrome-navigation plugin -date: 2023-12-06 +date: 2023-12-19 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 da4f4dee73d4c..84f922380955d 100644 --- a/api_docs/kbn_shared_ux_error_boundary.mdx +++ b/api_docs/kbn_shared_ux_error_boundary.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-error-boundary title: "@kbn/shared-ux-error-boundary" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-error-boundary plugin -date: 2023-12-06 +date: 2023-12-19 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 91b53a19414c6..483783a2e113b 100644 --- a/api_docs/kbn_shared_ux_file_context.mdx +++ b/api_docs/kbn_shared_ux_file_context.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-context title: "@kbn/shared-ux-file-context" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-context plugin -date: 2023-12-06 +date: 2023-12-19 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 57bcb6db7c00e..773af38ee6262 100644 --- a/api_docs/kbn_shared_ux_file_image.mdx +++ b/api_docs/kbn_shared_ux_file_image.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image title: "@kbn/shared-ux-file-image" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image plugin -date: 2023-12-06 +date: 2023-12-19 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 a89f9b6168646..e1f1d760777a7 100644 --- a/api_docs/kbn_shared_ux_file_image_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_image_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-image-mocks title: "@kbn/shared-ux-file-image-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-image-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 36a43fd080ee6..fd53ddb6ee586 100644 --- a/api_docs/kbn_shared_ux_file_mocks.mdx +++ b/api_docs/kbn_shared_ux_file_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-mocks title: "@kbn/shared-ux-file-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 24eb90138942a..41a1d82d91cdf 100644 --- a/api_docs/kbn_shared_ux_file_picker.mdx +++ b/api_docs/kbn_shared_ux_file_picker.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-picker title: "@kbn/shared-ux-file-picker" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-picker plugin -date: 2023-12-06 +date: 2023-12-19 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 d29c1e094402a..aafebe07b6276 100644 --- a/api_docs/kbn_shared_ux_file_types.mdx +++ b/api_docs/kbn_shared_ux_file_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-types title: "@kbn/shared-ux-file-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-types plugin -date: 2023-12-06 +date: 2023-12-19 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 97f4d57b2f4e7..6ae2f940ee5f5 100644 --- a/api_docs/kbn_shared_ux_file_upload.mdx +++ b/api_docs/kbn_shared_ux_file_upload.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-upload title: "@kbn/shared-ux-file-upload" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-upload plugin -date: 2023-12-06 +date: 2023-12-19 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 982b0ca363ad8..83a3bc810a812 100644 --- a/api_docs/kbn_shared_ux_file_util.mdx +++ b/api_docs/kbn_shared_ux_file_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-file-util title: "@kbn/shared-ux-file-util" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-file-util plugin -date: 2023-12-06 +date: 2023-12-19 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 44e0bc1442def..bda7c61a55595 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app title: "@kbn/shared-ux-link-redirect-app" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app plugin -date: 2023-12-06 +date: 2023-12-19 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 d746d3ae04de9..f064dc1ce8c32 100644 --- a/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx +++ b/api_docs/kbn_shared_ux_link_redirect_app_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-link-redirect-app-mocks title: "@kbn/shared-ux-link-redirect-app-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-link-redirect-app-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 0ee8ca4d679ca..813606d7fe4f6 100644 --- a/api_docs/kbn_shared_ux_markdown.mdx +++ b/api_docs/kbn_shared_ux_markdown.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown title: "@kbn/shared-ux-markdown" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown plugin -date: 2023-12-06 +date: 2023-12-19 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 5f5ca77450bcc..35d32a35715e4 100644 --- a/api_docs/kbn_shared_ux_markdown_mocks.mdx +++ b/api_docs/kbn_shared_ux_markdown_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-markdown-mocks title: "@kbn/shared-ux-markdown-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-markdown-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 eb8c7a80f3c06..334d5dedf331a 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data title: "@kbn/shared-ux-page-analytics-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data plugin -date: 2023-12-06 +date: 2023-12-19 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 0410276deb996..0f39ba0a6983a 100644 --- a/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_analytics_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-analytics-no-data-mocks title: "@kbn/shared-ux-page-analytics-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-analytics-no-data-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 8e3e0a30d34bb..9de368c36a62a 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data title: "@kbn/shared-ux-page-kibana-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data plugin -date: 2023-12-06 +date: 2023-12-19 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 bef1c329d7b0c..565e950e72b94 100644 --- a/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-no-data-mocks title: "@kbn/shared-ux-page-kibana-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-no-data-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 aab1a0956c2a3..5627b206928e1 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template title: "@kbn/shared-ux-page-kibana-template" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template plugin -date: 2023-12-06 +date: 2023-12-19 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 010c91168162c..c1362a374b242 100644 --- a/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_kibana_template_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-kibana-template-mocks title: "@kbn/shared-ux-page-kibana-template-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-kibana-template-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 6a9623da18f56..decb3cdf50bd0 100644 --- a/api_docs/kbn_shared_ux_page_no_data.mdx +++ b/api_docs/kbn_shared_ux_page_no_data.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data title: "@kbn/shared-ux-page-no-data" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data plugin -date: 2023-12-06 +date: 2023-12-19 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 01884de12cf2d..a76c44a23608f 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config title: "@kbn/shared-ux-page-no-data-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config plugin -date: 2023-12-06 +date: 2023-12-19 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 b2f0d7208fbde..cc8e13ea7d642 100644 --- a/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_config_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-config-mocks title: "@kbn/shared-ux-page-no-data-config-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-config-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 7804db88c1abb..6e16f063ba754 100644 --- a/api_docs/kbn_shared_ux_page_no_data_mocks.mdx +++ b/api_docs/kbn_shared_ux_page_no_data_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-no-data-mocks title: "@kbn/shared-ux-page-no-data-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-no-data-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 0bc4eab6f1117..e6e0947624f0b 100644 --- a/api_docs/kbn_shared_ux_page_solution_nav.mdx +++ b/api_docs/kbn_shared_ux_page_solution_nav.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-page-solution-nav title: "@kbn/shared-ux-page-solution-nav" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-page-solution-nav plugin -date: 2023-12-06 +date: 2023-12-19 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 b0d507f30cd0a..ab02252e8d617 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views title: "@kbn/shared-ux-prompt-no-data-views" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views plugin -date: 2023-12-06 +date: 2023-12-19 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 f019572bdb325..bb5321661e411 100644 --- a/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx +++ b/api_docs/kbn_shared_ux_prompt_no_data_views_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-no-data-views-mocks title: "@kbn/shared-ux-prompt-no-data-views-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-no-data-views-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 25483a7abeb12..f3eccad586b37 100644 --- a/api_docs/kbn_shared_ux_prompt_not_found.mdx +++ b/api_docs/kbn_shared_ux_prompt_not_found.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-prompt-not-found title: "@kbn/shared-ux-prompt-not-found" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-prompt-not-found plugin -date: 2023-12-06 +date: 2023-12-19 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 0a181014f22ce..bce53eb890e0c 100644 --- a/api_docs/kbn_shared_ux_router.mdx +++ b/api_docs/kbn_shared_ux_router.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router title: "@kbn/shared-ux-router" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router plugin -date: 2023-12-06 +date: 2023-12-19 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 c8f7aa079b643..2e6de026b19f5 100644 --- a/api_docs/kbn_shared_ux_router_mocks.mdx +++ b/api_docs/kbn_shared_ux_router_mocks.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-router-mocks title: "@kbn/shared-ux-router-mocks" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-router-mocks plugin -date: 2023-12-06 +date: 2023-12-19 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 17d5b5ba43243..061d2ddb1ea0f 100644 --- a/api_docs/kbn_shared_ux_storybook_config.mdx +++ b/api_docs/kbn_shared_ux_storybook_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-config title: "@kbn/shared-ux-storybook-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-config plugin -date: 2023-12-06 +date: 2023-12-19 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 769552a54bd0f..517f964e39f11 100644 --- a/api_docs/kbn_shared_ux_storybook_mock.mdx +++ b/api_docs/kbn_shared_ux_storybook_mock.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-storybook-mock title: "@kbn/shared-ux-storybook-mock" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-storybook-mock plugin -date: 2023-12-06 +date: 2023-12-19 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 5d75eb103f218..fdf6ec6e67325 100644 --- a/api_docs/kbn_shared_ux_utility.mdx +++ b/api_docs/kbn_shared_ux_utility.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-shared-ux-utility title: "@kbn/shared-ux-utility" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/shared-ux-utility plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/shared-ux-utility'] --- import kbnSharedUxUtilityObj from './kbn_shared_ux_utility.devdocs.json'; diff --git a/api_docs/kbn_slo_schema.devdocs.json b/api_docs/kbn_slo_schema.devdocs.json index 4e8b55b423af0..6ee292f202063 100644 --- a/api_docs/kbn_slo_schema.devdocs.json +++ b/api_docs/kbn_slo_schema.devdocs.json @@ -398,7 +398,115 @@ "initialIsOpen": false } ], - "interfaces": [], + "interfaces": [ + { + "parentPluginId": "@kbn/slo-schema", + "id": "def-common.Paginated", + "type": "Interface", + "tags": [], + "label": "Paginated", + "description": [], + "signature": [ + { + "pluginId": "@kbn/slo-schema", + "scope": "common", + "docId": "kibKbnSloSchemaPluginApi", + "section": "def-common.Paginated", + "text": "Paginated" + }, + "" + ], + "path": "x-pack/packages/kbn-slo-schema/src/models/pagination.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/slo-schema", + "id": "def-common.Paginated.total", + "type": "number", + "tags": [], + "label": "total", + "description": [], + "path": "x-pack/packages/kbn-slo-schema/src/models/pagination.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/slo-schema", + "id": "def-common.Paginated.page", + "type": "number", + "tags": [], + "label": "page", + "description": [], + "path": "x-pack/packages/kbn-slo-schema/src/models/pagination.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/slo-schema", + "id": "def-common.Paginated.perPage", + "type": "number", + "tags": [], + "label": "perPage", + "description": [], + "path": "x-pack/packages/kbn-slo-schema/src/models/pagination.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/slo-schema", + "id": "def-common.Paginated.results", + "type": "Array", + "tags": [], + "label": "results", + "description": [], + "signature": [ + "T[]" + ], + "path": "x-pack/packages/kbn-slo-schema/src/models/pagination.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/slo-schema", + "id": "def-common.Pagination", + "type": "Interface", + "tags": [], + "label": "Pagination", + "description": [], + "path": "x-pack/packages/kbn-slo-schema/src/models/pagination.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/slo-schema", + "id": "def-common.Pagination.page", + "type": "number", + "tags": [], + "label": "page", + "description": [], + "path": "x-pack/packages/kbn-slo-schema/src/models/pagination.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/slo-schema", + "id": "def-common.Pagination.perPage", + "type": "number", + "tags": [], + "label": "perPage", + "description": [], + "path": "x-pack/packages/kbn-slo-schema/src/models/pagination.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ], "enums": [ { "parentPluginId": "@kbn/slo-schema", @@ -621,17 +729,28 @@ }, { "parentPluginId": "@kbn/slo-schema", - "id": "def-common.FindSloDefinitionsResponse", + "id": "def-common.FindSLODefinitionsParams", "type": "Type", - "tags": [ - "private" - ], - "label": "FindSloDefinitionsResponse", - "description": [ - "\nThe response type for /internal/observability/slo/_definitions\n" + "tags": [], + "label": "FindSLODefinitionsParams", + "description": [], + "signature": [ + "{ search?: string | undefined; includeOutdatedOnly?: boolean | undefined; page?: string | undefined; perPage?: string | undefined; }" ], + "path": "x-pack/packages/kbn-slo-schema/src/rest_specs/slo.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/slo-schema", + "id": "def-common.FindSLODefinitionsResponse", + "type": "Type", + "tags": [], + "label": "FindSLODefinitionsResponse", + "description": [], "signature": [ - "({ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string; total: string; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"cardinality\" | \"last_value\" | \"std_deviation\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); } & { filter?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { syncDelay: string; frequency: string; }; enabled: boolean; tags: string[]; groupBy: string; createdAt: string; updatedAt: string; } & { instanceId?: string | undefined; })[]" + "{ page: number; perPage: number; total: number; results: ({ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string; total: string; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"cardinality\" | \"last_value\" | \"std_deviation\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); } & { filter?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { syncDelay: string; frequency: string; }; enabled: boolean; tags: string[]; groupBy: string; createdAt: string; updatedAt: string; version: number; } & { instanceId?: string | undefined; })[]; }" ], "path": "x-pack/packages/kbn-slo-schema/src/rest_specs/slo.ts", "deprecated": false, @@ -661,7 +780,7 @@ "label": "FindSLOResponse", "description": [], "signature": [ - "{ page: number; perPage: number; total: number; results: ({ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string; total: string; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"cardinality\" | \"last_value\" | \"std_deviation\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); } & { filter?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { syncDelay: string; frequency: string; }; enabled: boolean; tags: string[]; groupBy: string; createdAt: string; updatedAt: string; } & { instanceId?: string | undefined; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; })[]; }" + "{ page: number; perPage: number; total: number; results: ({ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string; total: string; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"cardinality\" | \"last_value\" | \"std_deviation\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); } & { filter?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { syncDelay: string; frequency: string; }; enabled: boolean; tags: string[]; groupBy: string; createdAt: string; updatedAt: string; version: number; } & { instanceId?: string | undefined; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; })[]; }" ], "path": "x-pack/packages/kbn-slo-schema/src/rest_specs/slo.ts", "deprecated": false, @@ -751,7 +870,7 @@ "label": "GetSLOResponse", "description": [], "signature": [ - "{ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string; total: string; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"cardinality\" | \"last_value\" | \"std_deviation\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); } & { filter?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { syncDelay: string; frequency: string; }; enabled: boolean; tags: string[]; groupBy: string; createdAt: string; updatedAt: string; } & { instanceId?: string | undefined; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; }" + "{ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string; total: string; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"cardinality\" | \"last_value\" | \"std_deviation\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); } & { filter?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { syncDelay: string; frequency: string; }; enabled: boolean; tags: string[]; groupBy: string; createdAt: string; updatedAt: string; version: number; } & { instanceId?: string | undefined; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; }" ], "path": "x-pack/packages/kbn-slo-schema/src/rest_specs/slo.ts", "deprecated": false, @@ -863,6 +982,36 @@ "trackAdoption": false, "initialIsOpen": false }, + { + "parentPluginId": "@kbn/slo-schema", + "id": "def-common.ResetSLOParams", + "type": "Type", + "tags": [], + "label": "ResetSLOParams", + "description": [], + "signature": [ + "{ id: string; }" + ], + "path": "x-pack/packages/kbn-slo-schema/src/rest_specs/slo.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/slo-schema", + "id": "def-common.ResetSLOResponse", + "type": "Type", + "tags": [], + "label": "ResetSLOResponse", + "description": [], + "signature": [ + "{ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string; total: string; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"cardinality\" | \"last_value\" | \"std_deviation\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); } & { filter?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { syncDelay: string; frequency: string; }; enabled: boolean; tags: string[]; groupBy: string; createdAt: string; updatedAt: string; version: number; } & { instanceId?: string | undefined; }" + ], + "path": "x-pack/packages/kbn-slo-schema/src/rest_specs/slo.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "@kbn/slo-schema", "id": "def-common.SLOResponse", @@ -871,7 +1020,7 @@ "label": "SLOResponse", "description": [], "signature": [ - "{ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string; total: string; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"cardinality\" | \"last_value\" | \"std_deviation\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); } & { filter?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { syncDelay: string; frequency: string; }; enabled: boolean; tags: string[]; groupBy: string; createdAt: string; updatedAt: string; } & { instanceId?: string | undefined; }" + "{ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string; total: string; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"cardinality\" | \"last_value\" | \"std_deviation\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); } & { filter?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { syncDelay: string; frequency: string; }; enabled: boolean; tags: string[]; groupBy: string; createdAt: string; updatedAt: string; version: number; } & { instanceId?: string | undefined; }" ], "path": "x-pack/packages/kbn-slo-schema/src/rest_specs/slo.ts", "deprecated": false, @@ -886,7 +1035,7 @@ "label": "SLOWithSummaryResponse", "description": [], "signature": [ - "{ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string; total: string; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"cardinality\" | \"last_value\" | \"std_deviation\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); } & { filter?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { syncDelay: string; frequency: string; }; enabled: boolean; tags: string[]; groupBy: string; createdAt: string; updatedAt: string; } & { instanceId?: string | undefined; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; }" + "{ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string; total: string; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"cardinality\" | \"last_value\" | \"std_deviation\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); } & { filter?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { syncDelay: string; frequency: string; }; enabled: boolean; tags: string[]; groupBy: string; createdAt: string; updatedAt: string; version: number; } & { instanceId?: string | undefined; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; }" ], "path": "x-pack/packages/kbn-slo-schema/src/rest_specs/slo.ts", "deprecated": false, @@ -1046,7 +1195,7 @@ "label": "UpdateSLOResponse", "description": [], "signature": [ - "{ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string; total: string; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"cardinality\" | \"last_value\" | \"std_deviation\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); } & { filter?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { syncDelay: string; frequency: string; }; enabled: boolean; tags: string[]; groupBy: string; createdAt: string; updatedAt: string; } & { instanceId?: string | undefined; }" + "{ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string; total: string; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"cardinality\" | \"last_value\" | \"std_deviation\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); } & { filter?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { syncDelay: string; frequency: string; }; enabled: boolean; tags: string[]; groupBy: string; createdAt: string; updatedAt: string; version: number; } & { instanceId?: string | undefined; }" ], "path": "x-pack/packages/kbn-slo-schema/src/rest_specs/slo.ts", "deprecated": false, @@ -1945,19 +2094,21 @@ "parentPluginId": "@kbn/slo-schema", "id": "def-common.findSloDefinitionsParamsSchema", "type": "Object", - "tags": [ - "private" - ], + "tags": [], "label": "findSloDefinitionsParamsSchema", - "description": [ - "\nThe query params schema for /internal/observability/slo/_definitions\n" - ], + "description": [], "signature": [ - "TypeC", + "PartialC", "<{ query: ", - "TypeC", + "PartialC", "<{ search: ", "StringC", + "; includeOutdatedOnly: ", + "Type", + "; page: ", + "StringC", + "; perPage: ", + "StringC", "; }>; }>" ], "path": "x-pack/packages/kbn-slo-schema/src/rest_specs/slo.ts", @@ -1969,14 +2120,18 @@ "parentPluginId": "@kbn/slo-schema", "id": "def-common.findSloDefinitionsResponseSchema", "type": "Object", - "tags": [ - "private" - ], + "tags": [], "label": "findSloDefinitionsResponseSchema", - "description": [ - "\nThe response schema for /internal/observability/slo/_definitions\n" - ], + "description": [], "signature": [ + "TypeC", + "<{ page: ", + "NumberC", + "; perPage: ", + "NumberC", + "; total: ", + "NumberC", + "; results: ", "ArrayC", "<", "IntersectionC", @@ -2414,7 +2569,9 @@ "Type", "; updatedAt: ", "Type", - "; }>, ", + "; version: ", + "NumberC", + "; }>, ", "PartialC", "<{ instanceId: ", "UnionC", @@ -2422,7 +2579,7 @@ "LiteralC", "<\"*\">, ", "StringC", - "]>; }>]>>" + "]>; }>]>>; }>" ], "path": "x-pack/packages/kbn-slo-schema/src/rest_specs/slo.ts", "deprecated": false, @@ -2924,7 +3081,9 @@ "Type", "; updatedAt: ", "Type", - "; }>, ", + "; version: ", + "NumberC", + "; }>, ", "PartialC", "<{ instanceId: ", "UnionC", @@ -3942,7 +4101,9 @@ "Type", "; updatedAt: ", "Type", - "; }>, ", + "; version: ", + "NumberC", + "; }>, ", "PartialC", "<{ instanceId: ", "UnionC", @@ -4905,105 +5066,30 @@ }, { "parentPluginId": "@kbn/slo-schema", - "id": "def-common.rollingTimeWindowSchema", + "id": "def-common.resetSLOParamsSchema", "type": "Object", "tags": [], - "label": "rollingTimeWindowSchema", + "label": "resetSLOParamsSchema", "description": [], "signature": [ "TypeC", - "<{ duration: ", - "Type", - "<", - { - "pluginId": "@kbn/slo-schema", - "scope": "common", - "docId": "kibKbnSloSchemaPluginApi", - "section": "def-common.Duration", - "text": "Duration" - }, - ", string, unknown>; type: ", - "LiteralC", - "<\"rolling\">; }>" - ], - "path": "x-pack/packages/kbn-slo-schema/src/schema/time_window.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/slo-schema", - "id": "def-common.rollingTimeWindowTypeSchema", - "type": "Object", - "tags": [], - "label": "rollingTimeWindowTypeSchema", - "description": [], - "signature": [ - "LiteralC", - "<\"rolling\">" - ], - "path": "x-pack/packages/kbn-slo-schema/src/schema/time_window.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/slo-schema", - "id": "def-common.settingsSchema", - "type": "Object", - "tags": [], - "label": "settingsSchema", - "description": [], - "signature": [ + "<{ path: ", "TypeC", - "<{ syncDelay: ", - "Type", - "<", - { - "pluginId": "@kbn/slo-schema", - "scope": "common", - "docId": "kibKbnSloSchemaPluginApi", - "section": "def-common.Duration", - "text": "Duration" - }, - ", string, unknown>; frequency: ", - "Type", - "<", - { - "pluginId": "@kbn/slo-schema", - "scope": "common", - "docId": "kibKbnSloSchemaPluginApi", - "section": "def-common.Duration", - "text": "Duration" - }, - ", string, unknown>; }>" - ], - "path": "x-pack/packages/kbn-slo-schema/src/schema/slo.ts", - "deprecated": false, - "trackAdoption": false, - "initialIsOpen": false - }, - { - "parentPluginId": "@kbn/slo-schema", - "id": "def-common.sloIdSchema", - "type": "Object", - "tags": [], - "label": "sloIdSchema", - "description": [], - "signature": [ - "StringC" + "<{ id: ", + "StringC", + "; }>; }>" ], - "path": "x-pack/packages/kbn-slo-schema/src/schema/slo.ts", + "path": "x-pack/packages/kbn-slo-schema/src/rest_specs/slo.ts", "deprecated": false, "trackAdoption": false, "initialIsOpen": false }, { "parentPluginId": "@kbn/slo-schema", - "id": "def-common.sloResponseSchema", + "id": "def-common.resetSLOResponseSchema", "type": "Object", "tags": [], - "label": "sloResponseSchema", + "label": "resetSLOResponseSchema", "description": [], "signature": [ "IntersectionC", @@ -5441,7 +5527,9 @@ "Type", "; updatedAt: ", "Type", - "; }>, ", + "; version: ", + "NumberC", + "; }>, ", "PartialC", "<{ instanceId: ", "UnionC", @@ -5458,28 +5546,583 @@ }, { "parentPluginId": "@kbn/slo-schema", - "id": "def-common.sloSchema", + "id": "def-common.rollingTimeWindowSchema", "type": "Object", "tags": [], - "label": "sloSchema", + "label": "rollingTimeWindowSchema", "description": [], "signature": [ "TypeC", - "<{ id: ", - "StringC", - "; name: ", - "StringC", - "; description: ", - "StringC", - "; indicator: ", - "UnionC", - "<[", - "TypeC", - "<{ type: ", - "LiteralC", - "<\"sli.apm.transactionDuration\">; params: ", - "IntersectionC", - "<[", + "<{ duration: ", + "Type", + "<", + { + "pluginId": "@kbn/slo-schema", + "scope": "common", + "docId": "kibKbnSloSchemaPluginApi", + "section": "def-common.Duration", + "text": "Duration" + }, + ", string, unknown>; type: ", + "LiteralC", + "<\"rolling\">; }>" + ], + "path": "x-pack/packages/kbn-slo-schema/src/schema/time_window.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/slo-schema", + "id": "def-common.rollingTimeWindowTypeSchema", + "type": "Object", + "tags": [], + "label": "rollingTimeWindowTypeSchema", + "description": [], + "signature": [ + "LiteralC", + "<\"rolling\">" + ], + "path": "x-pack/packages/kbn-slo-schema/src/schema/time_window.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/slo-schema", + "id": "def-common.settingsSchema", + "type": "Object", + "tags": [], + "label": "settingsSchema", + "description": [], + "signature": [ + "TypeC", + "<{ syncDelay: ", + "Type", + "<", + { + "pluginId": "@kbn/slo-schema", + "scope": "common", + "docId": "kibKbnSloSchemaPluginApi", + "section": "def-common.Duration", + "text": "Duration" + }, + ", string, unknown>; frequency: ", + "Type", + "<", + { + "pluginId": "@kbn/slo-schema", + "scope": "common", + "docId": "kibKbnSloSchemaPluginApi", + "section": "def-common.Duration", + "text": "Duration" + }, + ", string, unknown>; }>" + ], + "path": "x-pack/packages/kbn-slo-schema/src/schema/slo.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/slo-schema", + "id": "def-common.sloIdSchema", + "type": "Object", + "tags": [], + "label": "sloIdSchema", + "description": [], + "signature": [ + "StringC" + ], + "path": "x-pack/packages/kbn-slo-schema/src/schema/slo.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/slo-schema", + "id": "def-common.sloResponseSchema", + "type": "Object", + "tags": [], + "label": "sloResponseSchema", + "description": [], + "signature": [ + "IntersectionC", + "<[", + "TypeC", + "<{ id: ", + "StringC", + "; name: ", + "StringC", + "; description: ", + "StringC", + "; indicator: ", + "UnionC", + "<[", + "TypeC", + "<{ type: ", + "LiteralC", + "<\"sli.apm.transactionDuration\">; params: ", + "IntersectionC", + "<[", + "TypeC", + "<{ environment: ", + "UnionC", + "<[", + "LiteralC", + "<\"*\">, ", + "StringC", + "]>; service: ", + "UnionC", + "<[", + "LiteralC", + "<\"*\">, ", + "StringC", + "]>; transactionType: ", + "UnionC", + "<[", + "LiteralC", + "<\"*\">, ", + "StringC", + "]>; transactionName: ", + "UnionC", + "<[", + "LiteralC", + "<\"*\">, ", + "StringC", + "]>; threshold: ", + "NumberC", + "; index: ", + "StringC", + "; }>, ", + "PartialC", + "<{ filter: ", + "StringC", + "; }>]>; }>, ", + "TypeC", + "<{ type: ", + "LiteralC", + "<\"sli.apm.transactionErrorRate\">; params: ", + "IntersectionC", + "<[", + "TypeC", + "<{ environment: ", + "UnionC", + "<[", + "LiteralC", + "<\"*\">, ", + "StringC", + "]>; service: ", + "UnionC", + "<[", + "LiteralC", + "<\"*\">, ", + "StringC", + "]>; transactionType: ", + "UnionC", + "<[", + "LiteralC", + "<\"*\">, ", + "StringC", + "]>; transactionName: ", + "UnionC", + "<[", + "LiteralC", + "<\"*\">, ", + "StringC", + "]>; index: ", + "StringC", + "; }>, ", + "PartialC", + "<{ filter: ", + "StringC", + "; }>]>; }>, ", + "TypeC", + "<{ type: ", + "LiteralC", + "<\"sli.kql.custom\">; params: ", + "IntersectionC", + "<[", + "TypeC", + "<{ index: ", + "StringC", + "; good: ", + "StringC", + "; total: ", + "StringC", + "; timestampField: ", + "StringC", + "; }>, ", + "PartialC", + "<{ filter: ", + "StringC", + "; }>]>; }>, ", + "TypeC", + "<{ type: ", + "LiteralC", + "<\"sli.metric.custom\">; params: ", + "IntersectionC", + "<[", + "TypeC", + "<{ index: ", + "StringC", + "; good: ", + "TypeC", + "<{ metrics: ", + "ArrayC", + "<", + "UnionC", + "<[", + "IntersectionC", + "<[", + "TypeC", + "<{ name: ", + "StringC", + "; aggregation: ", + "LiteralC", + "<\"sum\">; field: ", + "StringC", + "; }>, ", + "PartialC", + "<{ filter: ", + "StringC", + "; }>]>, ", + "IntersectionC", + "<[", + "TypeC", + "<{ name: ", + "StringC", + "; aggregation: ", + "LiteralC", + "<\"doc_count\">; }>, ", + "PartialC", + "<{ filter: ", + "StringC", + "; }>]>]>>; equation: ", + "StringC", + "; }>; total: ", + "TypeC", + "<{ metrics: ", + "ArrayC", + "<", + "UnionC", + "<[", + "IntersectionC", + "<[", + "TypeC", + "<{ name: ", + "StringC", + "; aggregation: ", + "LiteralC", + "<\"sum\">; field: ", + "StringC", + "; }>, ", + "PartialC", + "<{ filter: ", + "StringC", + "; }>]>, ", + "IntersectionC", + "<[", + "TypeC", + "<{ name: ", + "StringC", + "; aggregation: ", + "LiteralC", + "<\"doc_count\">; }>, ", + "PartialC", + "<{ filter: ", + "StringC", + "; }>]>]>>; equation: ", + "StringC", + "; }>; timestampField: ", + "StringC", + "; }>, ", + "PartialC", + "<{ filter: ", + "StringC", + "; }>]>; }>, ", + "TypeC", + "<{ type: ", + "LiteralC", + "<\"sli.metric.timeslice\">; params: ", + "IntersectionC", + "<[", + "TypeC", + "<{ index: ", + "StringC", + "; metric: ", + "TypeC", + "<{ metrics: ", + "ArrayC", + "<", + "UnionC", + "<[", + "IntersectionC", + "<[", + "TypeC", + "<{ name: ", + "StringC", + "; aggregation: ", + "KeyofC", + "<{ avg: boolean; max: boolean; min: boolean; sum: boolean; cardinality: boolean; last_value: boolean; std_deviation: boolean; }>; field: ", + "StringC", + "; }>, ", + "PartialC", + "<{ filter: ", + "StringC", + "; }>]>, ", + "IntersectionC", + "<[", + "TypeC", + "<{ name: ", + "StringC", + "; aggregation: ", + "LiteralC", + "<\"doc_count\">; }>, ", + "PartialC", + "<{ filter: ", + "StringC", + "; }>]>, ", + "IntersectionC", + "<[", + "TypeC", + "<{ name: ", + "StringC", + "; aggregation: ", + "LiteralC", + "<\"percentile\">; field: ", + "StringC", + "; percentile: ", + "NumberC", + "; }>, ", + "PartialC", + "<{ filter: ", + "StringC", + "; }>]>]>>; equation: ", + "StringC", + "; threshold: ", + "NumberC", + "; comparator: ", + "KeyofC", + "<{ GT: string; GTE: string; LT: string; LTE: string; }>; }>; timestampField: ", + "StringC", + "; }>, ", + "PartialC", + "<{ filter: ", + "StringC", + "; }>]>; }>, ", + "TypeC", + "<{ type: ", + "LiteralC", + "<\"sli.histogram.custom\">; params: ", + "IntersectionC", + "<[", + "TypeC", + "<{ index: ", + "StringC", + "; timestampField: ", + "StringC", + "; good: ", + "UnionC", + "<[", + "IntersectionC", + "<[", + "TypeC", + "<{ field: ", + "StringC", + "; aggregation: ", + "LiteralC", + "<\"value_count\">; }>, ", + "PartialC", + "<{ filter: ", + "StringC", + "; }>]>, ", + "IntersectionC", + "<[", + "TypeC", + "<{ field: ", + "StringC", + "; aggregation: ", + "LiteralC", + "<\"range\">; from: ", + "NumberC", + "; to: ", + "NumberC", + "; }>, ", + "PartialC", + "<{ filter: ", + "StringC", + "; }>]>]>; total: ", + "UnionC", + "<[", + "IntersectionC", + "<[", + "TypeC", + "<{ field: ", + "StringC", + "; aggregation: ", + "LiteralC", + "<\"value_count\">; }>, ", + "PartialC", + "<{ filter: ", + "StringC", + "; }>]>, ", + "IntersectionC", + "<[", + "TypeC", + "<{ field: ", + "StringC", + "; aggregation: ", + "LiteralC", + "<\"range\">; from: ", + "NumberC", + "; to: ", + "NumberC", + "; }>, ", + "PartialC", + "<{ filter: ", + "StringC", + "; }>]>]>; }>, ", + "PartialC", + "<{ filter: ", + "StringC", + "; }>]>; }>]>; timeWindow: ", + "UnionC", + "<[", + "TypeC", + "<{ duration: ", + "Type", + "<", + { + "pluginId": "@kbn/slo-schema", + "scope": "common", + "docId": "kibKbnSloSchemaPluginApi", + "section": "def-common.Duration", + "text": "Duration" + }, + ", string, unknown>; type: ", + "LiteralC", + "<\"rolling\">; }>, ", + "TypeC", + "<{ duration: ", + "Type", + "<", + { + "pluginId": "@kbn/slo-schema", + "scope": "common", + "docId": "kibKbnSloSchemaPluginApi", + "section": "def-common.Duration", + "text": "Duration" + }, + ", string, unknown>; type: ", + "LiteralC", + "<\"calendarAligned\">; }>]>; budgetingMethod: ", + "UnionC", + "<[", + "LiteralC", + "<\"occurrences\">, ", + "LiteralC", + "<\"timeslices\">]>; objective: ", + "IntersectionC", + "<[", + "TypeC", + "<{ target: ", + "NumberC", + "; }>, ", + "PartialC", + "<{ timesliceTarget: ", + "NumberC", + "; timesliceWindow: ", + "Type", + "<", + { + "pluginId": "@kbn/slo-schema", + "scope": "common", + "docId": "kibKbnSloSchemaPluginApi", + "section": "def-common.Duration", + "text": "Duration" + }, + ", string, unknown>; }>]>; revision: ", + "NumberC", + "; settings: ", + "TypeC", + "<{ syncDelay: ", + "Type", + "<", + { + "pluginId": "@kbn/slo-schema", + "scope": "common", + "docId": "kibKbnSloSchemaPluginApi", + "section": "def-common.Duration", + "text": "Duration" + }, + ", string, unknown>; frequency: ", + "Type", + "<", + { + "pluginId": "@kbn/slo-schema", + "scope": "common", + "docId": "kibKbnSloSchemaPluginApi", + "section": "def-common.Duration", + "text": "Duration" + }, + ", string, unknown>; }>; enabled: ", + "BooleanC", + "; tags: ", + "ArrayC", + "<", + "StringC", + ">; groupBy: ", + "UnionC", + "<[", + "LiteralC", + "<\"*\">, ", + "StringC", + "]>; createdAt: ", + "Type", + "; updatedAt: ", + "Type", + "; version: ", + "NumberC", + "; }>, ", + "PartialC", + "<{ instanceId: ", + "UnionC", + "<[", + "LiteralC", + "<\"*\">, ", + "StringC", + "]>; }>]>" + ], + "path": "x-pack/packages/kbn-slo-schema/src/rest_specs/slo.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/slo-schema", + "id": "def-common.sloSchema", + "type": "Object", + "tags": [], + "label": "sloSchema", + "description": [], + "signature": [ + "TypeC", + "<{ id: ", + "StringC", + "; name: ", + "StringC", + "; description: ", + "StringC", + "; indicator: ", + "UnionC", + "<[", + "TypeC", + "<{ type: ", + "LiteralC", + "<\"sli.apm.transactionDuration\">; params: ", + "IntersectionC", + "<[", "TypeC", "<{ environment: ", "UnionC", @@ -5897,7 +6540,9 @@ "LiteralC", "<\"*\">, ", "StringC", - "]>; }>" + "]>; version: ", + "NumberC", + "; }>" ], "path": "x-pack/packages/kbn-slo-schema/src/schema/slo.ts", "deprecated": false, @@ -6349,7 +6994,9 @@ "Type", "; updatedAt: ", "Type", - "; }>, ", + "; version: ", + "NumberC", + "; }>, ", "PartialC", "<{ instanceId: ", "UnionC", @@ -6833,7 +7480,9 @@ "LiteralC", "<\"*\">, ", "StringC", - "]>; }>, ", + "]>; version: ", + "NumberC", + "; }>, ", "TypeC", "<{ summary: ", "TypeC", @@ -8231,7 +8880,9 @@ "Type", "; updatedAt: ", "Type", - "; }>, ", + "; version: ", + "NumberC", + "; }>, ", "PartialC", "<{ instanceId: ", "UnionC", diff --git a/api_docs/kbn_slo_schema.mdx b/api_docs/kbn_slo_schema.mdx index 98d3783bb094d..11eed96017eea 100644 --- a/api_docs/kbn_slo_schema.mdx +++ b/api_docs/kbn_slo_schema.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-slo-schema title: "@kbn/slo-schema" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/slo-schema plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/slo-schema'] --- import kbnSloSchemaObj from './kbn_slo_schema.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/ | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 131 | 0 | 128 | 0 | +| 144 | 0 | 144 | 0 | ## Common @@ -34,6 +34,9 @@ Contact [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/ ### Classes +### Interfaces + + ### Enums diff --git a/api_docs/kbn_some_dev_log.mdx b/api_docs/kbn_some_dev_log.mdx index 932b727927668..3d2f76883bdaa 100644 --- a/api_docs/kbn_some_dev_log.mdx +++ b/api_docs/kbn_some_dev_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-some-dev-log title: "@kbn/some-dev-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/some-dev-log plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/some-dev-log'] --- import kbnSomeDevLogObj from './kbn_some_dev_log.devdocs.json'; diff --git a/api_docs/kbn_std.mdx b/api_docs/kbn_std.mdx index e90a0a291a535..98af8c79666c6 100644 --- a/api_docs/kbn_std.mdx +++ b/api_docs/kbn_std.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-std title: "@kbn/std" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/std plugin -date: 2023-12-06 +date: 2023-12-19 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 2b9748418bf39..057088a6a4f06 100644 --- a/api_docs/kbn_stdio_dev_helpers.mdx +++ b/api_docs/kbn_stdio_dev_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-stdio-dev-helpers title: "@kbn/stdio-dev-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/stdio-dev-helpers plugin -date: 2023-12-06 +date: 2023-12-19 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 346abc7b2d8d2..b4aea70a83c47 100644 --- a/api_docs/kbn_storybook.mdx +++ b/api_docs/kbn_storybook.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-storybook title: "@kbn/storybook" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/storybook plugin -date: 2023-12-06 +date: 2023-12-19 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 51e70b51a1c7c..8f05560bbf3f4 100644 --- a/api_docs/kbn_telemetry_tools.mdx +++ b/api_docs/kbn_telemetry_tools.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-telemetry-tools title: "@kbn/telemetry-tools" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/telemetry-tools plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/telemetry-tools'] --- import kbnTelemetryToolsObj from './kbn_telemetry_tools.devdocs.json'; diff --git a/api_docs/kbn_test.devdocs.json b/api_docs/kbn_test.devdocs.json index 68147342a7a5e..5b01c5762fe46 100644 --- a/api_docs/kbn_test.devdocs.json +++ b/api_docs/kbn_test.devdocs.json @@ -1651,6 +1651,156 @@ } ], "initialIsOpen": false + }, + { + "parentPluginId": "@kbn/test", + "id": "def-common.SamlSessionManager", + "type": "Class", + "tags": [], + "label": "SamlSessionManager", + "description": [ + "\nManages cookies associated with user roles" + ], + "path": "packages/kbn-test/src/auth/session_manager.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/test", + "id": "def-common.SamlSessionManager.Unnamed", + "type": "Function", + "tags": [], + "label": "Constructor", + "description": [], + "signature": [ + "any" + ], + "path": "packages/kbn-test/src/auth/session_manager.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/test", + "id": "def-common.SamlSessionManager.Unnamed.$1", + "type": "Object", + "tags": [], + "label": "options", + "description": [], + "signature": [ + { + "pluginId": "@kbn/test", + "scope": "common", + "docId": "kibKbnTestPluginApi", + "section": "def-common.SamlSessionManagerOptions", + "text": "SamlSessionManagerOptions" + } + ], + "path": "packages/kbn-test/src/auth/session_manager.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/test", + "id": "def-common.SamlSessionManager.getApiCredentialsForRole", + "type": "Function", + "tags": [], + "label": "getApiCredentialsForRole", + "description": [], + "signature": [ + "(role: string) => Promise<{ Cookie: string; }>" + ], + "path": "packages/kbn-test/src/auth/session_manager.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/test", + "id": "def-common.SamlSessionManager.getApiCredentialsForRole.$1", + "type": "string", + "tags": [], + "label": "role", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-test/src/auth/session_manager.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/test", + "id": "def-common.SamlSessionManager.getSessionCookieForRole", + "type": "Function", + "tags": [], + "label": "getSessionCookieForRole", + "description": [], + "signature": [ + "(role: string) => Promise" + ], + "path": "packages/kbn-test/src/auth/session_manager.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/test", + "id": "def-common.SamlSessionManager.getSessionCookieForRole.$1", + "type": "string", + "tags": [], + "label": "role", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-test/src/auth/session_manager.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/test", + "id": "def-common.SamlSessionManager.getUserData", + "type": "Function", + "tags": [], + "label": "getUserData", + "description": [], + "signature": [ + "(role: string) => Promise<{ email: string; fullname: string; }>" + ], + "path": "packages/kbn-test/src/auth/session_manager.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/test", + "id": "def-common.SamlSessionManager.getUserData.$1", + "type": "string", + "tags": [], + "label": "role", + "description": [], + "signature": [ + "string" + ], + "path": "packages/kbn-test/src/auth/session_manager.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false } ], "functions": [ @@ -2398,6 +2548,23 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/test", + "id": "def-common.runCheckFtrCodeOwnersCli", + "type": "Function", + "tags": [], + "label": "runCheckFtrCodeOwnersCli", + "description": [], + "signature": [ + "() => Promise" + ], + "path": "packages/kbn-test/src/functional_test_runner/run_check_ftr_code_owners.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/test", "id": "def-common.runCheckFtrConfigsCli", @@ -4507,6 +4674,81 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/test", + "id": "def-common.HostOptions", + "type": "Interface", + "tags": [], + "label": "HostOptions", + "description": [], + "path": "packages/kbn-test/src/auth/session_manager.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/test", + "id": "def-common.HostOptions.protocol", + "type": "CompoundType", + "tags": [], + "label": "protocol", + "description": [], + "signature": [ + "\"http\" | \"https\"" + ], + "path": "packages/kbn-test/src/auth/session_manager.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/test", + "id": "def-common.HostOptions.hostname", + "type": "string", + "tags": [], + "label": "hostname", + "description": [], + "path": "packages/kbn-test/src/auth/session_manager.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/test", + "id": "def-common.HostOptions.port", + "type": "number", + "tags": [], + "label": "port", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "packages/kbn-test/src/auth/session_manager.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/test", + "id": "def-common.HostOptions.username", + "type": "string", + "tags": [], + "label": "username", + "description": [], + "path": "packages/kbn-test/src/auth/session_manager.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/test", + "id": "def-common.HostOptions.password", + "type": "string", + "tags": [], + "label": "password", + "description": [], + "path": "packages/kbn-test/src/auth/session_manager.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/test", "id": "def-common.ICluster", @@ -4741,6 +4983,71 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/test", + "id": "def-common.SamlSessionManagerOptions", + "type": "Interface", + "tags": [], + "label": "SamlSessionManagerOptions", + "description": [], + "path": "packages/kbn-test/src/auth/session_manager.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/test", + "id": "def-common.SamlSessionManagerOptions.hostOptions", + "type": "Object", + "tags": [], + "label": "hostOptions", + "description": [], + "signature": [ + { + "pluginId": "@kbn/test", + "scope": "common", + "docId": "kibKbnTestPluginApi", + "section": "def-common.HostOptions", + "text": "HostOptions" + } + ], + "path": "packages/kbn-test/src/auth/session_manager.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/test", + "id": "def-common.SamlSessionManagerOptions.isCloud", + "type": "boolean", + "tags": [], + "label": "isCloud", + "description": [], + "path": "packages/kbn-test/src/auth/session_manager.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "@kbn/test", + "id": "def-common.SamlSessionManagerOptions.log", + "type": "Object", + "tags": [], + "label": "log", + "description": [], + "signature": [ + { + "pluginId": "@kbn/tooling-log", + "scope": "common", + "docId": "kibKbnToolingLogPluginApi", + "section": "def-common.ToolingLog", + "text": "ToolingLog" + } + ], + "path": "packages/kbn-test/src/auth/session_manager.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/test", "id": "def-common.Suite", diff --git a/api_docs/kbn_test.mdx b/api_docs/kbn_test.mdx index 987eace82b833..448022835f596 100644 --- a/api_docs/kbn_test.mdx +++ b/api_docs/kbn_test.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test title: "@kbn/test" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/test'] --- import kbnTestObj from './kbn_test.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kiban | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 291 | 4 | 244 | 12 | +| 311 | 4 | 263 | 12 | ## Common diff --git a/api_docs/kbn_test_jest_helpers.mdx b/api_docs/kbn_test_jest_helpers.mdx index 5f83977035c19..4e160b6370f4a 100644 --- a/api_docs/kbn_test_jest_helpers.mdx +++ b/api_docs/kbn_test_jest_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-jest-helpers title: "@kbn/test-jest-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-jest-helpers plugin -date: 2023-12-06 +date: 2023-12-19 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 f04ca978b127f..ce2174dc13b0a 100644 --- a/api_docs/kbn_test_subj_selector.mdx +++ b/api_docs/kbn_test_subj_selector.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-test-subj-selector title: "@kbn/test-subj-selector" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/test-subj-selector plugin -date: 2023-12-06 +date: 2023-12-19 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 237746026fda9..8ddb863af8150 100644 --- a/api_docs/kbn_text_based_editor.mdx +++ b/api_docs/kbn_text_based_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-text-based-editor title: "@kbn/text-based-editor" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/text-based-editor plugin -date: 2023-12-06 +date: 2023-12-19 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 71b538c3cdd46..f1f81a9613b6a 100644 --- a/api_docs/kbn_tooling_log.mdx +++ b/api_docs/kbn_tooling_log.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-tooling-log title: "@kbn/tooling-log" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/tooling-log plugin -date: 2023-12-06 +date: 2023-12-19 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 426019405e51a..d9721df8ef064 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: 2023-12-06 +date: 2023-12-19 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 b5b2ddf20a839..48d52200d7b9b 100644 --- a/api_docs/kbn_ts_projects.mdx +++ b/api_docs/kbn_ts_projects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ts-projects title: "@kbn/ts-projects" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ts-projects plugin -date: 2023-12-06 +date: 2023-12-19 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 c1b7bc738666b..4b7075ff79d57 100644 --- a/api_docs/kbn_typed_react_router_config.mdx +++ b/api_docs/kbn_typed_react_router_config.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-typed-react-router-config title: "@kbn/typed-react-router-config" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/typed-react-router-config plugin -date: 2023-12-06 +date: 2023-12-19 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 db5905219cb2c..6d9ee3960407e 100644 --- a/api_docs/kbn_ui_actions_browser.mdx +++ b/api_docs/kbn_ui_actions_browser.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-actions-browser title: "@kbn/ui-actions-browser" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-actions-browser plugin -date: 2023-12-06 +date: 2023-12-19 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.devdocs.json b/api_docs/kbn_ui_shared_deps_src.devdocs.json index 3a9e249b17a58..43ebafeb195cf 100644 --- a/api_docs/kbn_ui_shared_deps_src.devdocs.json +++ b/api_docs/kbn_ui_shared_deps_src.devdocs.json @@ -546,6 +546,17 @@ "deprecated": false, "trackAdoption": false }, + { + "parentPluginId": "@kbn/ui-shared-deps-src", + "id": "def-common.externals.kbnsearcherrors", + "type": "string", + "tags": [], + "label": "'@kbn/search-errors'", + "description": [], + "path": "packages/kbn-ui-shared-deps-src/src/definitions.js", + "deprecated": false, + "trackAdoption": false + }, { "parentPluginId": "@kbn/ui-shared-deps-src", "id": "def-common.externals.kbnstd", diff --git a/api_docs/kbn_ui_shared_deps_src.mdx b/api_docs/kbn_ui_shared_deps_src.mdx index e3b8b07062622..42aa906ced28c 100644 --- a/api_docs/kbn_ui_shared_deps_src.mdx +++ b/api_docs/kbn_ui_shared_deps_src.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-shared-deps-src title: "@kbn/ui-shared-deps-src" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-shared-deps-src plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/ui-shared-deps-src'] --- import kbnUiSharedDepsSrcObj from './kbn_ui_shared_deps_src.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kiban | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 53 | 0 | 44 | 0 | +| 54 | 0 | 45 | 0 | ## Common diff --git a/api_docs/kbn_ui_theme.mdx b/api_docs/kbn_ui_theme.mdx index fd7909c3f8eb8..61ded720e8839 100644 --- a/api_docs/kbn_ui_theme.mdx +++ b/api_docs/kbn_ui_theme.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-ui-theme title: "@kbn/ui-theme" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/ui-theme plugin -date: 2023-12-06 +date: 2023-12-19 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 46dac5205b919..ed071e5d18601 100644 --- a/api_docs/kbn_unified_data_table.mdx +++ b/api_docs/kbn_unified_data_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-data-table title: "@kbn/unified-data-table" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-data-table plugin -date: 2023-12-06 +date: 2023-12-19 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.devdocs.json b/api_docs/kbn_unified_doc_viewer.devdocs.json index da1c2b438bd70..297fa3033699f 100644 --- a/api_docs/kbn_unified_doc_viewer.devdocs.json +++ b/api_docs/kbn_unified_doc_viewer.devdocs.json @@ -31,18 +31,75 @@ "children": [ { "parentPluginId": "@kbn/unified-doc-viewer", - "id": "def-common.DocViewsRegistry.addDocView", + "id": "def-common.DocViewsRegistry.Unnamed", "type": "Function", "tags": [], - "label": "addDocView", - "description": [ - "\nExtends and adds the given doc view to the registry array" + "label": "Constructor", + "description": [], + "signature": [ + "any" + ], + "path": "packages/kbn-unified-doc-viewer/src/services/doc_views_registry.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/unified-doc-viewer", + "id": "def-common.DocViewsRegistry.Unnamed.$1", + "type": "CompoundType", + "tags": [], + "label": "initialValue", + "description": [], + "signature": [ + { + "pluginId": "@kbn/unified-doc-viewer", + "scope": "common", + "docId": "kibKbnUnifiedDocViewerPluginApi", + "section": "def-common.DocViewsRegistry", + "text": "DocViewsRegistry" + }, + " | ", + "DocView", + "[] | undefined" + ], + "path": "packages/kbn-unified-doc-viewer/src/services/doc_views_registry.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": false + } ], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/unified-doc-viewer", + "id": "def-common.DocViewsRegistry.getAll", + "type": "Function", + "tags": [], + "label": "getAll", + "description": [], + "signature": [ + "() => ", + "DocView", + "[]" + ], + "path": "packages/kbn-unified-doc-viewer/src/services/doc_views_registry.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "@kbn/unified-doc-viewer", + "id": "def-common.DocViewsRegistry.add", + "type": "Function", + "tags": [], + "label": "add", + "description": [], "signature": [ "(docViewRaw: ", - "DocViewInput", + "DocView", " | ", - "DocViewInputFn", + "DocViewFactory", ") => void" ], "path": "packages/kbn-unified-doc-viewer/src/services/doc_views_registry.ts", @@ -51,15 +108,15 @@ "children": [ { "parentPluginId": "@kbn/unified-doc-viewer", - "id": "def-common.DocViewsRegistry.addDocView.$1", + "id": "def-common.DocViewsRegistry.add.$1", "type": "CompoundType", "tags": [], "label": "docViewRaw", "description": [], "signature": [ - "DocViewInput", + "DocView", " | ", - "DocViewInputFn" + "DocViewFactory" ], "path": "packages/kbn-unified-doc-viewer/src/services/doc_views_registry.ts", "deprecated": false, @@ -71,19 +128,13 @@ }, { "parentPluginId": "@kbn/unified-doc-viewer", - "id": "def-common.DocViewsRegistry.getDocViewsSorted", + "id": "def-common.DocViewsRegistry.removeById", "type": "Function", "tags": [], - "label": "getDocViewsSorted", - "description": [ - "\nReturns a sorted array of doc_views for rendering tabs" - ], + "label": "removeById", + "description": [], "signature": [ - "(hit: ", - "DataTableRecord", - ") => ", - "DocView", - "[]" + "(id: string) => void" ], "path": "packages/kbn-unified-doc-viewer/src/services/doc_views_registry.ts", "deprecated": false, @@ -91,13 +142,13 @@ "children": [ { "parentPluginId": "@kbn/unified-doc-viewer", - "id": "def-common.DocViewsRegistry.getDocViewsSorted.$1", - "type": "Object", + "id": "def-common.DocViewsRegistry.removeById.$1", + "type": "string", "tags": [], - "label": "hit", + "label": "id", "description": [], "signature": [ - "DataTableRecord" + "string" ], "path": "packages/kbn-unified-doc-viewer/src/services/doc_views_registry.ts", "deprecated": false, @@ -106,6 +157,29 @@ } ], "returnComment": [] + }, + { + "parentPluginId": "@kbn/unified-doc-viewer", + "id": "def-common.DocViewsRegistry.clone", + "type": "Function", + "tags": [], + "label": "clone", + "description": [], + "signature": [ + "() => ", + { + "pluginId": "@kbn/unified-doc-viewer", + "scope": "common", + "docId": "kibKbnUnifiedDocViewerPluginApi", + "section": "def-common.DocViewsRegistry", + "text": "DocViewsRegistry" + } + ], + "path": "packages/kbn-unified-doc-viewer/src/services/doc_views_registry.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] } ], "initialIsOpen": false diff --git a/api_docs/kbn_unified_doc_viewer.mdx b/api_docs/kbn_unified_doc_viewer.mdx index d180bf0c7bdce..5a0dc633d1edd 100644 --- a/api_docs/kbn_unified_doc_viewer.mdx +++ b/api_docs/kbn_unified_doc_viewer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-doc-viewer title: "@kbn/unified-doc-viewer" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-doc-viewer plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unified-doc-viewer'] --- import kbnUnifiedDocViewerObj from './kbn_unified_doc_viewer.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 | |-------------------|-----------|------------------------|-----------------| -| 10 | 0 | 7 | 7 | +| 14 | 0 | 13 | 6 | ## Common diff --git a/api_docs/kbn_unified_field_list.devdocs.json b/api_docs/kbn_unified_field_list.devdocs.json index 35da28a598da8..541b20f210c13 100644 --- a/api_docs/kbn_unified_field_list.devdocs.json +++ b/api_docs/kbn_unified_field_list.devdocs.json @@ -648,8 +648,8 @@ "pluginId": "uiActions", "scope": "public", "docId": "kibUiActionsPluginApi", - "section": "def-public.UiActionsStart", - "text": "UiActionsStart" + "section": "def-public.UiActionsPublicStart", + "text": "UiActionsPublicStart" }, ", field: ", { @@ -701,8 +701,8 @@ "pluginId": "uiActions", "scope": "public", "docId": "kibUiActionsPluginApi", - "section": "def-public.UiActionsStart", - "text": "UiActionsStart" + "section": "def-public.UiActionsPublicStart", + "text": "UiActionsPublicStart" } ], "path": "packages/kbn-unified-field-list/src/components/field_visualize_button/visualize_trigger_utils.ts", @@ -979,8 +979,8 @@ "pluginId": "uiActions", "scope": "public", "docId": "kibUiActionsPluginApi", - "section": "def-public.UiActionsStart", - "text": "UiActionsStart" + "section": "def-public.UiActionsPublicStart", + "text": "UiActionsPublicStart" }, ", field: ", { @@ -1016,8 +1016,8 @@ "pluginId": "uiActions", "scope": "public", "docId": "kibUiActionsPluginApi", - "section": "def-public.UiActionsStart", - "text": "UiActionsStart" + "section": "def-public.UiActionsPublicStart", + "text": "UiActionsPublicStart" } ], "path": "packages/kbn-unified-field-list/src/components/field_visualize_button/visualize_trigger_utils.ts", @@ -1115,8 +1115,8 @@ "pluginId": "uiActions", "scope": "public", "docId": "kibUiActionsPluginApi", - "section": "def-public.UiActionsStart", - "text": "UiActionsStart" + "section": "def-public.UiActionsPublicStart", + "text": "UiActionsPublicStart" }, ", contextualFields: string[], originatingApp: string, dataView: ", { @@ -1152,8 +1152,8 @@ "pluginId": "uiActions", "scope": "public", "docId": "kibUiActionsPluginApi", - "section": "def-public.UiActionsStart", - "text": "UiActionsStart" + "section": "def-public.UiActionsPublicStart", + "text": "UiActionsPublicStart" } ], "path": "packages/kbn-unified-field-list/src/components/field_visualize_button/visualize_trigger_utils.ts", @@ -1270,8 +1270,8 @@ "pluginId": "uiActions", "scope": "public", "docId": "kibUiActionsPluginApi", - "section": "def-public.UiActionsStart", - "text": "UiActionsStart" + "section": "def-public.UiActionsPublicStart", + "text": "UiActionsPublicStart" }, " | undefined; } & { dataViewFieldEditor?: ", { @@ -5419,8 +5419,8 @@ "pluginId": "uiActions", "scope": "public", "docId": "kibUiActionsPluginApi", - "section": "def-public.UiActionsStart", - "text": "UiActionsStart" + "section": "def-public.UiActionsPublicStart", + "text": "UiActionsPublicStart" }, " | undefined; } & { dataViewFieldEditor?: ", { diff --git a/api_docs/kbn_unified_field_list.mdx b/api_docs/kbn_unified_field_list.mdx index 50606bd8b51b7..697396ee7d6cc 100644 --- a/api_docs/kbn_unified_field_list.mdx +++ b/api_docs/kbn_unified_field_list.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unified-field-list title: "@kbn/unified-field-list" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unified-field-list plugin -date: 2023-12-06 +date: 2023-12-19 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 bcc4ed005e261..722bb74668663 100644 --- a/api_docs/kbn_unsaved_changes_badge.mdx +++ b/api_docs/kbn_unsaved_changes_badge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-unsaved-changes-badge title: "@kbn/unsaved-changes-badge" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/unsaved-changes-badge plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/unsaved-changes-badge'] --- import kbnUnsavedChangesBadgeObj from './kbn_unsaved_changes_badge.devdocs.json'; diff --git a/api_docs/kbn_url_state.mdx b/api_docs/kbn_url_state.mdx index e07d7b4d0fce8..941ef48209ce6 100644 --- a/api_docs/kbn_url_state.mdx +++ b/api_docs/kbn_url_state.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-url-state title: "@kbn/url-state" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/url-state plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/url-state'] --- import kbnUrlStateObj from './kbn_url_state.devdocs.json'; diff --git a/api_docs/kbn_use_tracked_promise.mdx b/api_docs/kbn_use_tracked_promise.mdx index d2e3c142f52ac..39ff4379240a1 100644 --- a/api_docs/kbn_use_tracked_promise.mdx +++ b/api_docs/kbn_use_tracked_promise.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-use-tracked-promise title: "@kbn/use-tracked-promise" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/use-tracked-promise plugin -date: 2023-12-06 +date: 2023-12-19 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 0659c9a9738a4..3fa957596dd5c 100644 --- a/api_docs/kbn_user_profile_components.mdx +++ b/api_docs/kbn_user_profile_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-user-profile-components title: "@kbn/user-profile-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/user-profile-components plugin -date: 2023-12-06 +date: 2023-12-19 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 e35245edccae8..28a91f585e883 100644 --- a/api_docs/kbn_utility_types.mdx +++ b/api_docs/kbn_utility_types.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types title: "@kbn/utility-types" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types plugin -date: 2023-12-06 +date: 2023-12-19 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 f47bdd5051461..4a068cbca3973 100644 --- a/api_docs/kbn_utility_types_jest.mdx +++ b/api_docs/kbn_utility_types_jest.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utility-types-jest title: "@kbn/utility-types-jest" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utility-types-jest plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utility-types-jest'] --- import kbnUtilityTypesJestObj from './kbn_utility_types_jest.devdocs.json'; diff --git a/api_docs/kbn_utils.devdocs.json b/api_docs/kbn_utils.devdocs.json index 88821550bc555..c2f4eefd1be61 100644 --- a/api_docs/kbn_utils.devdocs.json +++ b/api_docs/kbn_utils.devdocs.json @@ -19,23 +19,6 @@ "common": { "classes": [], "functions": [ - { - "parentPluginId": "@kbn/utils", - "id": "def-common.buildDataPaths", - "type": "Function", - "tags": [], - "label": "buildDataPaths", - "description": [], - "signature": [ - "() => string[]" - ], - "path": "packages/kbn-utils/src/path/index.ts", - "deprecated": false, - "trackAdoption": false, - "children": [], - "returnComment": [], - "initialIsOpen": false - }, { "parentPluginId": "@kbn/utils", "id": "def-common.concatStreamProviders", diff --git a/api_docs/kbn_utils.mdx b/api_docs/kbn_utils.mdx index d59758cb2eae8..a76610e6ee9f2 100644 --- a/api_docs/kbn_utils.mdx +++ b/api_docs/kbn_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-utils title: "@kbn/utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/utils plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/utils'] --- import kbnUtilsObj from './kbn_utils.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kiban | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 25 | 0 | 15 | 0 | +| 24 | 0 | 14 | 0 | ## Common diff --git a/api_docs/kbn_visualization_ui_components.mdx b/api_docs/kbn_visualization_ui_components.mdx index c7485b672eff4..6f58db9e72165 100644 --- a/api_docs/kbn_visualization_ui_components.mdx +++ b/api_docs/kbn_visualization_ui_components.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-visualization-ui-components title: "@kbn/visualization-ui-components" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/visualization-ui-components plugin -date: 2023-12-06 +date: 2023-12-19 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.devdocs.json b/api_docs/kbn_visualization_utils.devdocs.json new file mode 100644 index 0000000000000..d76530afc1e28 --- /dev/null +++ b/api_docs/kbn_visualization_utils.devdocs.json @@ -0,0 +1,77 @@ +{ + "id": "@kbn/visualization-utils", + "client": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [], + "functions": [ + { + "parentPluginId": "@kbn/visualization-utils", + "id": "def-common.getTimeZone", + "type": "Function", + "tags": [], + "label": "getTimeZone", + "description": [ + "\nGet timeZone from uiSettings" + ], + "signature": [ + "(uiSettings: ", + { + "pluginId": "@kbn/core-ui-settings-browser", + "scope": "common", + "docId": "kibKbnCoreUiSettingsBrowserPluginApi", + "section": "def-common.IUiSettingsClient", + "text": "IUiSettingsClient" + }, + ") => string" + ], + "path": "packages/kbn-visualization-utils/src/get_timezone.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "@kbn/visualization-utils", + "id": "def-common.getTimeZone.$1", + "type": "Object", + "tags": [], + "label": "uiSettings", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-ui-settings-browser", + "scope": "common", + "docId": "kibKbnCoreUiSettingsBrowserPluginApi", + "section": "def-common.IUiSettingsClient", + "text": "IUiSettingsClient" + } + ], + "path": "packages/kbn-visualization-utils/src/get_timezone.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + } +} \ No newline at end of file diff --git a/api_docs/kbn_visualization_utils.mdx b/api_docs/kbn_visualization_utils.mdx new file mode 100644 index 0000000000000..4fb2e91f6f3c2 --- /dev/null +++ b/api_docs/kbn_visualization_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: kibKbnVisualizationUtilsPluginApi +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: 2023-12-19 +tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/visualization-utils'] +--- +import kbnVisualizationUtilsObj from './kbn_visualization_utils.devdocs.json'; + + + +Contact [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) for questions regarding this plugin. + +**Code health stats** + +| Public API count | Any count | Items lacking comments | Missing exports | +|-------------------|-----------|------------------------|-----------------| +| 2 | 0 | 1 | 0 | + +## Common + +### Functions + + diff --git a/api_docs/kbn_xstate_utils.devdocs.json b/api_docs/kbn_xstate_utils.devdocs.json index b67854e217961..7c3dbf7d10fdc 100644 --- a/api_docs/kbn_xstate_utils.devdocs.json +++ b/api_docs/kbn_xstate_utils.devdocs.json @@ -62,6 +62,23 @@ "returnComment": [], "initialIsOpen": false }, + { + "parentPluginId": "@kbn/xstate-utils", + "id": "def-common.getDevToolsOptions", + "type": "Function", + "tags": [], + "label": "getDevToolsOptions", + "description": [], + "signature": [ + "() => boolean | object" + ], + "path": "packages/kbn-xstate-utils/src/dev_tools.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [], + "initialIsOpen": false + }, { "parentPluginId": "@kbn/xstate-utils", "id": "def-common.isDevMode", diff --git a/api_docs/kbn_xstate_utils.mdx b/api_docs/kbn_xstate_utils.mdx index 9af72b4d86cac..bfebbfd19bc91 100644 --- a/api_docs/kbn_xstate_utils.mdx +++ b/api_docs/kbn_xstate_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-xstate-utils title: "@kbn/xstate-utils" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/xstate-utils plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', '@kbn/xstate-utils'] --- import kbnXstateUtilsObj from './kbn_xstate_utils.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 12 | 0 | 12 | 0 | +| 13 | 0 | 13 | 0 | ## Common diff --git a/api_docs/kbn_yarn_lock_validator.mdx b/api_docs/kbn_yarn_lock_validator.mdx index ff97032b401a4..eeec2e6e6dd73 100644 --- a/api_docs/kbn_yarn_lock_validator.mdx +++ b/api_docs/kbn_yarn_lock_validator.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-yarn-lock-validator title: "@kbn/yarn-lock-validator" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/yarn-lock-validator plugin -date: 2023-12-06 +date: 2023-12-19 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 89390eefff8d8..6f8f072bac598 100644 --- a/api_docs/kbn_zod_helpers.mdx +++ b/api_docs/kbn_zod_helpers.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kbn-zod-helpers title: "@kbn/zod-helpers" image: https://source.unsplash.com/400x175/?github description: API docs for the @kbn/zod-helpers plugin -date: 2023-12-06 +date: 2023-12-19 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 f185eb010689d..7a76aba27b04d 100644 --- a/api_docs/kibana_overview.mdx +++ b/api_docs/kibana_overview.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaOverview title: "kibanaOverview" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaOverview plugin -date: 2023-12-06 +date: 2023-12-19 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 06032a3c41846..e23a4fb2f5783 100644 --- a/api_docs/kibana_react.devdocs.json +++ b/api_docs/kibana_react.devdocs.json @@ -811,54 +811,6 @@ "plugin": "visualizations", "path": "src/plugins/visualizations/public/visualize_app/index.tsx" }, - { - "plugin": "controls", - "path": "src/plugins/controls/public/options_list/embeddable/options_list_embeddable.tsx" - }, - { - "plugin": "controls", - "path": "src/plugins/controls/public/options_list/embeddable/options_list_embeddable.tsx" - }, - { - "plugin": "controls", - "path": "src/plugins/controls/public/options_list/embeddable/options_list_embeddable.tsx" - }, - { - "plugin": "controls", - "path": "src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.tsx" - }, - { - "plugin": "controls", - "path": "src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.tsx" - }, - { - "plugin": "controls", - "path": "src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.tsx" - }, - { - "plugin": "controls", - "path": "src/plugins/controls/public/control_group/embeddable/control_group_container.tsx" - }, - { - "plugin": "controls", - "path": "src/plugins/controls/public/control_group/embeddable/control_group_container.tsx" - }, - { - "plugin": "controls", - "path": "src/plugins/controls/public/control_group/embeddable/control_group_container.tsx" - }, - { - "plugin": "controls", - "path": "src/plugins/controls/public/time_slider/embeddable/time_slider_embeddable.tsx" - }, - { - "plugin": "controls", - "path": "src/plugins/controls/public/time_slider/embeddable/time_slider_embeddable.tsx" - }, - { - "plugin": "controls", - "path": "src/plugins/controls/public/time_slider/embeddable/time_slider_embeddable.tsx" - }, { "plugin": "dashboard", "path": "src/plugins/dashboard/public/dashboard_container/embeddable/dashboard_container.tsx" @@ -1395,6 +1347,58 @@ "plugin": "ml", "path": "x-pack/plugins/ml/public/application/management/jobs_list/components/jobs_list_page/jobs_list_page.tsx" }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/public/shared_components/osquery_results/osquery_result_wrapper.tsx" + }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/public/shared_components/osquery_results/osquery_result_wrapper.tsx" + }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/public/shared_components/osquery_results/osquery_result_wrapper.tsx" + }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/public/shared_imports.ts" + }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/public/shared_components/osquery_results/osquery_results.tsx" + }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/public/shared_components/osquery_results/osquery_results.tsx" + }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/public/shared_components/osquery_results/osquery_results.tsx" + }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/public/shared_components/services_wrapper.tsx" + }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/public/shared_components/services_wrapper.tsx" + }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/public/shared_components/services_wrapper.tsx" + }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/public/application.tsx" + }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/public/application.tsx" + }, + { + "plugin": "osquery", + "path": "x-pack/plugins/osquery/public/application.tsx" + }, { "plugin": "profiling", "path": "x-pack/plugins/profiling/public/app.tsx" @@ -1531,18 +1535,6 @@ "plugin": "crossClusterReplication", "path": "x-pack/plugins/cross_cluster_replication/public/app/index.tsx" }, - { - "plugin": "enterpriseSearch", - "path": "x-pack/plugins/enterprise_search/public/applications/index.tsx" - }, - { - "plugin": "enterpriseSearch", - "path": "x-pack/plugins/enterprise_search/public/applications/index.tsx" - }, - { - "plugin": "enterpriseSearch", - "path": "x-pack/plugins/enterprise_search/public/applications/index.tsx" - }, { "plugin": "globalSearchBar", "path": "x-pack/plugins/global_search_bar/public/plugin.tsx" @@ -1651,58 +1643,6 @@ "plugin": "observabilityOnboarding", "path": "x-pack/plugins/observability_onboarding/public/application/app.tsx" }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/public/shared_components/osquery_results/osquery_result_wrapper.tsx" - }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/public/shared_components/osquery_results/osquery_result_wrapper.tsx" - }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/public/shared_components/osquery_results/osquery_result_wrapper.tsx" - }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/public/shared_imports.ts" - }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/public/shared_components/osquery_results/osquery_results.tsx" - }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/public/shared_components/osquery_results/osquery_results.tsx" - }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/public/shared_components/osquery_results/osquery_results.tsx" - }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/public/shared_components/services_wrapper.tsx" - }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/public/shared_components/services_wrapper.tsx" - }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/public/shared_components/services_wrapper.tsx" - }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/public/application.tsx" - }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/public/application.tsx" - }, - { - "plugin": "osquery", - "path": "x-pack/plugins/osquery/public/application.tsx" - }, { "plugin": "devTools", "path": "src/plugins/dev_tools/public/application.tsx" @@ -2704,30 +2644,6 @@ "plugin": "visualizations", "path": "src/plugins/visualizations/public/visualize_app/index.tsx" }, - { - "plugin": "controls", - "path": "src/plugins/controls/public/control_group/editor/open_add_data_control_flyout.tsx" - }, - { - "plugin": "controls", - "path": "src/plugins/controls/public/control_group/editor/open_add_data_control_flyout.tsx" - }, - { - "plugin": "controls", - "path": "src/plugins/controls/public/control_group/editor/open_edit_control_group_flyout.tsx" - }, - { - "plugin": "controls", - "path": "src/plugins/controls/public/control_group/editor/open_edit_control_group_flyout.tsx" - }, - { - "plugin": "controls", - "path": "src/plugins/controls/public/control_group/actions/edit_control_action.tsx" - }, - { - "plugin": "controls", - "path": "src/plugins/controls/public/control_group/actions/edit_control_action.tsx" - }, { "plugin": "dashboard", "path": "src/plugins/dashboard/public/dashboard_container/embeddable/api/show_settings.tsx" @@ -3084,6 +3000,14 @@ "plugin": "maps", "path": "x-pack/plugins/maps/public/render_app.tsx" }, + { + "plugin": "timelines", + "path": "x-pack/plugins/timelines/public/components/hover_actions/actions/add_to_timeline.tsx" + }, + { + "plugin": "timelines", + "path": "x-pack/plugins/timelines/public/components/hover_actions/actions/add_to_timeline.tsx" + }, { "plugin": "banners", "path": "x-pack/plugins/banners/public/plugin.tsx" @@ -3168,14 +3092,6 @@ "plugin": "reporting", "path": "x-pack/plugins/reporting/public/share_context_menu/reporting_panel_content/reporting_panel_content.tsx" }, - { - "plugin": "timelines", - "path": "x-pack/plugins/timelines/public/components/hover_actions/actions/add_to_timeline.tsx" - }, - { - "plugin": "timelines", - "path": "x-pack/plugins/timelines/public/components/hover_actions/actions/add_to_timeline.tsx" - }, { "plugin": "cloudSecurityPosture", "path": "x-pack/plugins/cloud_security_posture/public/components/take_action.tsx" diff --git a/api_docs/kibana_react.mdx b/api_docs/kibana_react.mdx index 0c00c0c9c975a..eab29c588112f 100644 --- a/api_docs/kibana_react.mdx +++ b/api_docs/kibana_react.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaReact title: "kibanaReact" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaReact plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaReact'] --- import kibanaReactObj from './kibana_react.devdocs.json'; diff --git a/api_docs/kibana_utils.devdocs.json b/api_docs/kibana_utils.devdocs.json index a0c1a249a615d..96bf6a42450b0 100644 --- a/api_docs/kibana_utils.devdocs.json +++ b/api_docs/kibana_utils.devdocs.json @@ -6943,10 +6943,10 @@ ], "setup": { "parentPluginId": "kibanaUtils", - "id": "def-public.KibanaUtilsSetup", + "id": "def-public.KibanaUtilsPublicSetup", "type": "Interface", "tags": [], - "label": "KibanaUtilsSetup", + "label": "KibanaUtilsPublicSetup", "description": [], "path": "src/plugins/kibana_utils/public/plugin.ts", "deprecated": false, @@ -6954,7 +6954,7 @@ "children": [ { "parentPluginId": "kibanaUtils", - "id": "def-public.KibanaUtilsSetup.setVersion", + "id": "def-public.KibanaUtilsPublicSetup.setVersion", "type": "Function", "tags": [], "label": "setVersion", @@ -6970,7 +6970,7 @@ "children": [ { "parentPluginId": "kibanaUtils", - "id": "def-public.KibanaUtilsSetup.setVersion.$1", + "id": "def-public.KibanaUtilsPublicSetup.setVersion.$1", "type": "Object", "tags": [], "label": "history", @@ -6991,6 +6991,22 @@ ], "lifecycle": "setup", "initialIsOpen": true + }, + "start": { + "parentPluginId": "kibanaUtils", + "id": "def-public.KibanaUtilsPublicStart", + "type": "Type", + "tags": [], + "label": "KibanaUtilsPublicStart", + "description": [], + "signature": [ + "undefined" + ], + "path": "src/plugins/kibana_utils/public/plugin.ts", + "deprecated": false, + "trackAdoption": false, + "lifecycle": "start", + "initialIsOpen": true } }, "server": { diff --git a/api_docs/kibana_utils.mdx b/api_docs/kibana_utils.mdx index 37a4b162798ff..35e1ab28fb3d4 100644 --- a/api_docs/kibana_utils.mdx +++ b/api_docs/kibana_utils.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kibanaUtils title: "kibanaUtils" image: https://source.unsplash.com/400x175/?github description: API docs for the kibanaUtils plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kibanaUtils'] --- import kibanaUtilsObj from './kibana_utils.devdocs.json'; @@ -21,13 +21,16 @@ Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sh | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 610 | 3 | 417 | 9 | +| 611 | 3 | 418 | 9 | ## Client ### Setup +### Start + + ### Objects diff --git a/api_docs/kubernetes_security.mdx b/api_docs/kubernetes_security.mdx index d40f2b4b63069..e58be7d1de896 100644 --- a/api_docs/kubernetes_security.mdx +++ b/api_docs/kubernetes_security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/kubernetesSecurity title: "kubernetesSecurity" image: https://source.unsplash.com/400x175/?github description: API docs for the kubernetesSecurity plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'kubernetesSecurity'] --- import kubernetesSecurityObj from './kubernetes_security.devdocs.json'; diff --git a/api_docs/lens.devdocs.json b/api_docs/lens.devdocs.json index ab352fb83b015..48dc406105739 100644 --- a/api_docs/lens.devdocs.json +++ b/api_docs/lens.devdocs.json @@ -5315,6 +5315,142 @@ ], "initialIsOpen": false }, + { + "parentPluginId": "lens", + "id": "def-public.TagcloudState", + "type": "Interface", + "tags": [], + "label": "TagcloudState", + "description": [], + "path": "x-pack/plugins/lens/public/visualizations/tagcloud/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "lens", + "id": "def-public.TagcloudState.layerId", + "type": "string", + "tags": [], + "label": "layerId", + "description": [], + "path": "x-pack/plugins/lens/public/visualizations/tagcloud/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "lens", + "id": "def-public.TagcloudState.tagAccessor", + "type": "string", + "tags": [], + "label": "tagAccessor", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "x-pack/plugins/lens/public/visualizations/tagcloud/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "lens", + "id": "def-public.TagcloudState.valueAccessor", + "type": "string", + "tags": [], + "label": "valueAccessor", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "x-pack/plugins/lens/public/visualizations/tagcloud/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "lens", + "id": "def-public.TagcloudState.maxFontSize", + "type": "number", + "tags": [], + "label": "maxFontSize", + "description": [], + "path": "x-pack/plugins/lens/public/visualizations/tagcloud/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "lens", + "id": "def-public.TagcloudState.minFontSize", + "type": "number", + "tags": [], + "label": "minFontSize", + "description": [], + "path": "x-pack/plugins/lens/public/visualizations/tagcloud/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "lens", + "id": "def-public.TagcloudState.orientation", + "type": "CompoundType", + "tags": [], + "label": "orientation", + "description": [], + "signature": [ + "\"single\" | \"right angled\" | \"multiple\"" + ], + "path": "x-pack/plugins/lens/public/visualizations/tagcloud/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "lens", + "id": "def-public.TagcloudState.palette", + "type": "Object", + "tags": [], + "label": "palette", + "description": [], + "signature": [ + { + "pluginId": "@kbn/coloring", + "scope": "common", + "docId": "kibKbnColoringPluginApi", + "section": "def-common.PaletteOutput", + "text": "PaletteOutput" + }, + "<{ [key: string]: unknown; }> | undefined" + ], + "path": "x-pack/plugins/lens/public/visualizations/tagcloud/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "lens", + "id": "def-public.TagcloudState.showLabel", + "type": "boolean", + "tags": [], + "label": "showLabel", + "description": [], + "path": "x-pack/plugins/lens/public/visualizations/tagcloud/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "lens", + "id": "def-public.TagcloudState.colorMapping", + "type": "Object", + "tags": [], + "label": "colorMapping", + "description": [], + "signature": [ + "Config", + " | undefined" + ], + "path": "x-pack/plugins/lens/public/visualizations/tagcloud/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, { "parentPluginId": "lens", "id": "def-public.TermsIndexPatternColumn", @@ -8744,7 +8880,7 @@ "CustomXDomain", " | undefined>; ariaDescription?: string | undefined; ariaDescribedBy?: string | undefined; ariaLabelledBy?: string | undefined; ariaTableCaption?: string | undefined; legendAction?: \"ignore\" | undefined; legendStrategy?: ", "LegendStrategy", - " | undefined; onLegendItemClick?: \"ignore\" | undefined; customLegend?: \"ignore\" | undefined; onLegendItemMinusClick?: \"ignore\" | undefined; onLegendItemOut?: \"ignore\" | undefined; onLegendItemOver?: \"ignore\" | undefined; onLegendItemPlusClick?: \"ignore\" | undefined; debugState?: boolean | undefined; onProjectionClick?: \"ignore\" | undefined; onElementClick?: \"ignore\" | undefined; onElementOver?: \"ignore\" | undefined; onElementOut?: \"ignore\" | undefined; onBrushEnd?: \"ignore\" | undefined; onProjectionAreaChange?: \"ignore\" | undefined; onAnnotationClick?: \"ignore\" | undefined; pointerUpdateDebounce?: number | undefined; roundHistogramBrushValues?: boolean | undefined; noResults?: React.ReactChild | React.ComponentType<{}> | undefined; legendSort?: \"ignore\" | undefined; }>> & Partial>) | undefined" + " | undefined; onLegendItemClick?: \"ignore\" | undefined; customLegend?: \"ignore\" | undefined; onLegendItemMinusClick?: \"ignore\" | undefined; onLegendItemOut?: \"ignore\" | undefined; onLegendItemOver?: \"ignore\" | undefined; onLegendItemPlusClick?: \"ignore\" | undefined; debugState?: boolean | undefined; onProjectionClick?: \"ignore\" | undefined; onElementClick?: \"ignore\" | undefined; onElementOver?: \"ignore\" | undefined; onElementOut?: \"ignore\" | undefined; onBrushEnd?: \"ignore\" | undefined; onResize?: \"ignore\" | undefined; onWillRender?: \"ignore\" | undefined; onProjectionAreaChange?: \"ignore\" | undefined; onAnnotationClick?: \"ignore\" | undefined; pointerUpdateDebounce?: number | undefined; roundHistogramBrushValues?: boolean | undefined; noResults?: React.ReactChild | React.ComponentType<{}> | undefined; legendSort?: \"ignore\" | undefined; }>> & Partial>) | undefined" ], "path": "src/plugins/chart_expressions/expression_xy/common/types/expression_renderers.ts", "deprecated": false, @@ -10755,7 +10891,7 @@ "CustomXDomain", " | undefined>; ariaDescription?: string | undefined; ariaDescribedBy?: string | undefined; ariaLabelledBy?: string | undefined; ariaTableCaption?: string | undefined; legendAction?: \"ignore\" | undefined; legendStrategy?: ", "LegendStrategy", - " | undefined; onLegendItemClick?: \"ignore\" | undefined; customLegend?: \"ignore\" | undefined; onLegendItemMinusClick?: \"ignore\" | undefined; onLegendItemOut?: \"ignore\" | undefined; onLegendItemOver?: \"ignore\" | undefined; onLegendItemPlusClick?: \"ignore\" | undefined; debugState?: boolean | undefined; onProjectionClick?: \"ignore\" | undefined; onElementClick?: \"ignore\" | undefined; onElementOver?: \"ignore\" | undefined; onElementOut?: \"ignore\" | undefined; onBrushEnd?: \"ignore\" | undefined; onProjectionAreaChange?: \"ignore\" | undefined; onAnnotationClick?: \"ignore\" | undefined; pointerUpdateDebounce?: number | undefined; roundHistogramBrushValues?: boolean | undefined; noResults?: React.ReactChild | React.ComponentType<{}> | undefined; legendSort?: \"ignore\" | undefined; }>> | Partial | undefined; legendSort?: \"ignore\" | undefined; }>> | Partial string[] | undefined" + ], + "path": "x-pack/plugins/log_explorer/public/utils/convert_discover_app_state.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "logExplorer", - "id": "def-public.LogExplorerCustomizations.flyout", + "id": "def-public.getDiscoverColumnsFromDisplayOptions.$1", "type": "Object", "tags": [], - "label": "flyout", + "label": "displayOptions", "description": [], "signature": [ - "{ renderContent?: ", - "FlyoutRenderContent", - " | undefined; } | undefined" + { + "pluginId": "logExplorer", + "scope": "common", + "docId": "kibLogExplorerPluginApi", + "section": "def-common.DisplayOptions", + "text": "DisplayOptions" + } ], - "path": "x-pack/plugins/log_explorer/public/components/log_explorer/types.ts", + "path": "x-pack/plugins/log_explorer/public/utils/convert_discover_app_state.ts", "deprecated": false, - "trackAdoption": false + "trackAdoption": false, + "isRequired": true } ], + "returnComment": [], "initialIsOpen": false }, { "parentPluginId": "logExplorer", - "id": "def-public.LogExplorerFlyoutContentProps", - "type": "Interface", + "id": "def-public.getDiscoverFiltersFromState", + "type": "Function", "tags": [], - "label": "LogExplorerFlyoutContentProps", + "label": "getDiscoverFiltersFromState", "description": [], - "path": "x-pack/plugins/log_explorer/public/components/log_explorer/types.ts", + "signature": [ + "(filters?: ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[], controls?: ", + "ControlOptions", + " | undefined) => ", + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[]" + ], + "path": "x-pack/plugins/log_explorer/public/utils/convert_discover_app_state.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "logExplorer", - "id": "def-public.LogExplorerFlyoutContentProps.doc", + "id": "def-public.getDiscoverFiltersFromState.$1", + "type": "Array", + "tags": [], + "label": "filters", + "description": [], + "signature": [ + { + "pluginId": "@kbn/es-query", + "scope": "common", + "docId": "kibKbnEsQueryPluginApi", + "section": "def-common.Filter", + "text": "Filter" + }, + "[]" + ], + "path": "x-pack/plugins/log_explorer/public/utils/convert_discover_app_state.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "logExplorer", + "id": "def-public.getDiscoverFiltersFromState.$2", "type": "Object", "tags": [], - "label": "doc", + "label": "controls", "description": [], "signature": [ - "DataTableRecord" + "ControlOptions", + " | undefined" ], - "path": "x-pack/plugins/log_explorer/public/components/log_explorer/types.ts", + "path": "x-pack/plugins/log_explorer/public/utils/convert_discover_app_state.ts", "deprecated": false, - "trackAdoption": false + "trackAdoption": false, + "isRequired": false } ], + "returnComment": [], "initialIsOpen": false }, { "parentPluginId": "logExplorer", - "id": "def-public.LogExplorerStateContainer", - "type": "Interface", + "id": "def-public.getDiscoverGridFromDisplayOptions", + "type": "Function", "tags": [], - "label": "LogExplorerStateContainer", + "label": "getDiscoverGridFromDisplayOptions", "description": [], - "path": "x-pack/plugins/log_explorer/public/components/log_explorer/log_explorer.tsx", + "signature": [ + "(displayOptions: ", + { + "pluginId": "logExplorer", + "scope": "common", + "docId": "kibLogExplorerPluginApi", + "section": "def-common.DisplayOptions", + "text": "DisplayOptions" + }, + ") => ", + { + "pluginId": "@kbn/unified-data-table", + "scope": "common", + "docId": "kibKbnUnifiedDataTablePluginApi", + "section": "def-common.UnifiedDataTableSettings", + "text": "UnifiedDataTableSettings" + }, + " | undefined" + ], + "path": "x-pack/plugins/log_explorer/public/utils/convert_discover_app_state.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "logExplorer", - "id": "def-public.LogExplorerStateContainer.appState", + "id": "def-public.getDiscoverGridFromDisplayOptions.$1", "type": "Object", "tags": [], - "label": "appState", + "label": "displayOptions", "description": [], "signature": [ { - "pluginId": "discover", - "scope": "public", - "docId": "kibDiscoverPluginApi", - "section": "def-public.DiscoverAppState", - "text": "DiscoverAppState" - }, - " | undefined" - ], - "path": "x-pack/plugins/log_explorer/public/components/log_explorer/log_explorer.tsx", - "deprecated": false, - "trackAdoption": false - }, - { - "parentPluginId": "logExplorer", - "id": "def-public.LogExplorerStateContainer.logExplorerState", - "type": "CompoundType", - "tags": [], - "label": "logExplorerState", - "description": [], - "signature": [ - "Partial<", - "WithDatasetSelection", - " | (", - "WithDatasetSelection", - " & ", - "WithControlPanels", - ") | (", - "WithDatasetSelection", - " & ", - "WithControlPanelGroupAPI", - " & ", - "WithControlPanels", - ")> | undefined" + "pluginId": "logExplorer", + "scope": "common", + "docId": "kibLogExplorerPluginApi", + "section": "def-common.DisplayOptions", + "text": "DisplayOptions" + } ], - "path": "x-pack/plugins/log_explorer/public/components/log_explorer/log_explorer.tsx", + "path": "x-pack/plugins/log_explorer/public/utils/convert_discover_app_state.ts", "deprecated": false, - "trackAdoption": false + "trackAdoption": false, + "isRequired": true } ], + "returnComment": [], "initialIsOpen": false } ], - "enums": [], - "misc": [], - "objects": [], - "setup": { - "parentPluginId": "logExplorer", - "id": "def-public.LogExplorerPluginSetup", - "type": "Interface", - "tags": [], - "label": "LogExplorerPluginSetup", - "description": [], - "path": "x-pack/plugins/log_explorer/public/types.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "logExplorer", - "id": "def-public.LogExplorerPluginSetup.locators", - "type": "Object", - "tags": [], - "label": "locators", - "description": [], - "signature": [ - "LogExplorerLocators" - ], - "path": "x-pack/plugins/log_explorer/public/types.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "lifecycle": "setup", - "initialIsOpen": true - }, - "start": { - "parentPluginId": "logExplorer", - "id": "def-public.LogExplorerPluginStart", - "type": "Interface", - "tags": [], - "label": "LogExplorerPluginStart", - "description": [], - "path": "x-pack/plugins/log_explorer/public/types.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "logExplorer", - "id": "def-public.LogExplorerPluginStart.LogExplorer", - "type": "CompoundType", - "tags": [], - "label": "LogExplorer", - "description": [], - "signature": [ - "React.ComponentClass<", - "LogExplorerProps", - ", any> | React.FunctionComponent<", - "LogExplorerProps", - ">" - ], - "path": "x-pack/plugins/log_explorer/public/types.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "lifecycle": "start", - "initialIsOpen": true - } - }, - "server": { - "classes": [], - "functions": [], - "interfaces": [], - "enums": [], - "misc": [], - "objects": [] - }, - "common": { - "classes": [ + "interfaces": [ { "parentPluginId": "logExplorer", - "id": "def-common.AllDatasetSelection", - "type": "Class", + "id": "def-public.LogExplorerController", + "type": "Interface", "tags": [], - "label": "AllDatasetSelection", + "label": "LogExplorerController", "description": [], - "signature": [ - { - "pluginId": "logExplorer", - "scope": "common", - "docId": "kibLogExplorerPluginApi", - "section": "def-common.AllDatasetSelection", - "text": "AllDatasetSelection" - }, - " implements ", - "DatasetSelectionStrategy" - ], - "path": "x-pack/plugins/log_explorer/common/dataset_selection/all_dataset_selection.ts", + "path": "x-pack/plugins/log_explorer/public/controller/types.ts", "deprecated": false, "trackAdoption": false, "children": [ { "parentPluginId": "logExplorer", - "id": "def-common.AllDatasetSelection.selectionType", - "type": "string", + "id": "def-public.LogExplorerController.actions", + "type": "Object", "tags": [], - "label": "selectionType", + "label": "actions", "description": [], "signature": [ - "\"all\"" + "{}" ], - "path": "x-pack/plugins/log_explorer/common/dataset_selection/all_dataset_selection.ts", + "path": "x-pack/plugins/log_explorer/public/controller/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "logExplorer", - "id": "def-common.AllDatasetSelection.selection", + "id": "def-public.LogExplorerController.customizations", "type": "Object", "tags": [], - "label": "selection", + "label": "customizations", "description": [], "signature": [ - "{ dataset: ", - "Dataset", - "; }" + { + "pluginId": "logExplorer", + "scope": "public", + "docId": "kibLogExplorerPluginApi", + "section": "def-public.LogExplorerCustomizations", + "text": "LogExplorerCustomizations" + } ], - "path": "x-pack/plugins/log_explorer/common/dataset_selection/all_dataset_selection.ts", + "path": "x-pack/plugins/log_explorer/public/controller/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "logExplorer", - "id": "def-common.AllDatasetSelection.toDataviewSpec", - "type": "Function", + "id": "def-public.LogExplorerController.datasetsClient", + "type": "Object", "tags": [], - "label": "toDataviewSpec", + "label": "datasetsClient", "description": [], "signature": [ - "() => { id: string; name: string | undefined; title: string | undefined; }" + "IDatasetsClient" ], - "path": "x-pack/plugins/log_explorer/common/dataset_selection/all_dataset_selection.ts", + "path": "x-pack/plugins/log_explorer/public/controller/types.ts", "deprecated": false, - "trackAdoption": false, - "children": [], - "returnComment": [] + "trackAdoption": false }, { "parentPluginId": "logExplorer", - "id": "def-common.AllDatasetSelection.toURLSelectionId", - "type": "Function", + "id": "def-public.LogExplorerController.discoverServices", + "type": "CompoundType", "tags": [], - "label": "toURLSelectionId", + "label": "discoverServices", "description": [], "signature": [ - "() => string" + "Pick>, \"data\" | \"history\" | \"uiSettings\" | \"timefilter\" | \"filterManager\"> & { urlStateStorage: ", + { + "pluginId": "kibanaUtils", + "scope": "public", + "docId": "kibKibanaUtilsPluginApi", + "section": "def-public.IKbnUrlStateStorage", + "text": "IKbnUrlStateStorage" + }, + "; }" ], - "path": "x-pack/plugins/log_explorer/common/dataset_selection/all_dataset_selection.ts", + "path": "x-pack/plugins/log_explorer/public/controller/types.ts", "deprecated": false, - "trackAdoption": false, - "children": [], - "returnComment": [] + "trackAdoption": false }, { "parentPluginId": "logExplorer", - "id": "def-common.AllDatasetSelection.create", - "type": "Function", + "id": "def-public.LogExplorerController.event$", + "type": "Object", "tags": [], - "label": "create", + "label": "event$", "description": [], "signature": [ - "() => ", + "Observable", + "" + ], + "path": "x-pack/plugins/log_explorer/public/controller/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-public.LogExplorerController.service", + "type": "Object", + "tags": [], + "label": "service", + "description": [], + "signature": [ + "Interpreter", + "<(", + "WithDatasetSelection", + " & ", + "WithControlPanels", + " & ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.QueryState", + "text": "QueryState" + }, + " & ", { "pluginId": "logExplorer", "scope": "common", "docId": "kibLogExplorerPluginApi", - "section": "def-common.AllDatasetSelection", - "text": "AllDatasetSelection" - } - ], - "path": "x-pack/plugins/log_explorer/common/dataset_selection/all_dataset_selection.ts", - "deprecated": false, - "trackAdoption": false, - "children": [], - "returnComment": [] - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "logExplorer", - "id": "def-common.UnresolvedDatasetSelection", - "type": "Class", - "tags": [], - "label": "UnresolvedDatasetSelection", - "description": [], - "signature": [ - { - "pluginId": "logExplorer", - "scope": "common", - "docId": "kibLogExplorerPluginApi", - "section": "def-common.UnresolvedDatasetSelection", - "text": "UnresolvedDatasetSelection" + "section": "def-common.DisplayOptions", + "text": "DisplayOptions" + }, + ") | (", + "WithDatasetSelection", + " & ", + "WithControlPanels", + " & ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.QueryState", + "text": "QueryState" + }, + " & ", + { + "pluginId": "logExplorer", + "scope": "common", + "docId": "kibLogExplorerPluginApi", + "section": "def-common.DisplayOptions", + "text": "DisplayOptions" + }, + " & ", + "WithDiscoverStateContainer", + ") | (", + "WithDatasetSelection", + " & ", + "WithControlPanelGroupAPI", + " & ", + "WithControlPanels", + " & ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.QueryState", + "text": "QueryState" + }, + " & ", + { + "pluginId": "logExplorer", + "scope": "common", + "docId": "kibLogExplorerPluginApi", + "section": "def-common.DisplayOptions", + "text": "DisplayOptions" + }, + " & ", + "WithDiscoverStateContainer", + "), any, ", + "LogExplorerControllerEvent", + ", ", + "LogExplorerControllerTypeState", + ", ", + "ResolveTypegenMeta", + "<", + "TypegenDisabled", + ", ", + "LogExplorerControllerEvent", + ", ", + "BaseActionObject", + ", ", + "ServiceMap", + ">>" + ], + "path": "x-pack/plugins/log_explorer/public/controller/types.ts", + "deprecated": false, + "trackAdoption": false }, - " implements ", - "DatasetSelectionStrategy" - ], - "path": "x-pack/plugins/log_explorer/common/dataset_selection/unresolved_dataset_selection.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ { "parentPluginId": "logExplorer", - "id": "def-common.UnresolvedDatasetSelection.selectionType", - "type": "string", + "id": "def-public.LogExplorerController.state$", + "type": "Object", "tags": [], - "label": "selectionType", + "label": "state$", "description": [], "signature": [ - "\"unresolved\"" + "Observable", + "<", + { + "pluginId": "logExplorer", + "scope": "public", + "docId": "kibLogExplorerPluginApi", + "section": "def-public.LogExplorerPublicState", + "text": "LogExplorerPublicState" + }, + ">" ], - "path": "x-pack/plugins/log_explorer/common/dataset_selection/unresolved_dataset_selection.ts", + "path": "x-pack/plugins/log_explorer/public/controller/types.ts", "deprecated": false, "trackAdoption": false }, { "parentPluginId": "logExplorer", - "id": "def-common.UnresolvedDatasetSelection.selection", + "id": "def-public.LogExplorerController.stateMachine", "type": "Object", "tags": [], - "label": "selection", + "label": "stateMachine", "description": [], "signature": [ - "{ name?: string | undefined; dataset: ", - "Dataset", - "; }" + "StateMachine", + "<(", + "WithDatasetSelection", + " & ", + "WithControlPanels", + " & ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.QueryState", + "text": "QueryState" + }, + " & ", + { + "pluginId": "logExplorer", + "scope": "common", + "docId": "kibLogExplorerPluginApi", + "section": "def-common.DisplayOptions", + "text": "DisplayOptions" + }, + ") | (", + "WithDatasetSelection", + " & ", + "WithControlPanels", + " & ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.QueryState", + "text": "QueryState" + }, + " & ", + { + "pluginId": "logExplorer", + "scope": "common", + "docId": "kibLogExplorerPluginApi", + "section": "def-common.DisplayOptions", + "text": "DisplayOptions" + }, + " & ", + "WithDiscoverStateContainer", + ") | (", + "WithDatasetSelection", + " & ", + "WithControlPanelGroupAPI", + " & ", + "WithControlPanels", + " & ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.QueryState", + "text": "QueryState" + }, + " & ", + { + "pluginId": "logExplorer", + "scope": "common", + "docId": "kibLogExplorerPluginApi", + "section": "def-common.DisplayOptions", + "text": "DisplayOptions" + }, + " & ", + "WithDiscoverStateContainer", + "), any, ", + "LogExplorerControllerEvent", + ", ", + "LogExplorerControllerTypeState", + ", ", + "BaseActionObject", + ", ", + "ServiceMap", + ", ", + "ResolveTypegenMeta", + "<", + "TypegenDisabled", + ", ", + "LogExplorerControllerEvent", + ", ", + "BaseActionObject", + ", ", + "ServiceMap", + ">>" ], - "path": "x-pack/plugins/log_explorer/common/dataset_selection/unresolved_dataset_selection.ts", + "path": "x-pack/plugins/log_explorer/public/controller/types.ts", "deprecated": false, "trackAdoption": false - }, + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-public.LogExplorerCustomizations", + "type": "Interface", + "tags": [], + "label": "LogExplorerCustomizations", + "description": [], + "path": "x-pack/plugins/log_explorer/public/controller/controller_customizations.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ { "parentPluginId": "logExplorer", - "id": "def-common.UnresolvedDatasetSelection.toDataviewSpec", - "type": "Function", + "id": "def-public.LogExplorerCustomizations.flyout", + "type": "Object", "tags": [], - "label": "toDataviewSpec", + "label": "flyout", "description": [], "signature": [ - "() => { id: string; name: string | undefined; title: string | undefined; }" + "{ renderContent?: ", + "RenderContentCustomization", + "<", + { + "pluginId": "logExplorer", + "scope": "public", + "docId": "kibLogExplorerPluginApi", + "section": "def-public.LogExplorerFlyoutContentProps", + "text": "LogExplorerFlyoutContentProps" + }, + "> | undefined; } | undefined" ], - "path": "x-pack/plugins/log_explorer/common/dataset_selection/unresolved_dataset_selection.ts", + "path": "x-pack/plugins/log_explorer/public/controller/controller_customizations.ts", "deprecated": false, - "trackAdoption": false, - "children": [], - "returnComment": [] - }, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-public.LogExplorerFlyoutContentProps", + "type": "Interface", + "tags": [], + "label": "LogExplorerFlyoutContentProps", + "description": [], + "path": "x-pack/plugins/log_explorer/public/controller/controller_customizations.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ { "parentPluginId": "logExplorer", - "id": "def-common.UnresolvedDatasetSelection.toURLSelectionId", - "type": "Function", + "id": "def-public.LogExplorerFlyoutContentProps.actions", + "type": "Object", "tags": [], - "label": "toURLSelectionId", + "label": "actions", "description": [], "signature": [ - "() => string" + "{ addFilter: ", + "DocViewFilterFn", + " | undefined; addColumn: ((columnName: string) => void) | undefined; removeColumn: ((columnName: string) => void) | undefined; }" ], - "path": "x-pack/plugins/log_explorer/common/dataset_selection/unresolved_dataset_selection.ts", + "path": "x-pack/plugins/log_explorer/public/controller/controller_customizations.ts", "deprecated": false, - "trackAdoption": false, - "children": [], - "returnComment": [] + "trackAdoption": false }, { "parentPluginId": "logExplorer", - "id": "def-common.UnresolvedDatasetSelection.fromSelection", - "type": "Function", + "id": "def-public.LogExplorerFlyoutContentProps.dataView", + "type": "Object", "tags": [], - "label": "fromSelection", + "label": "dataView", "description": [], "signature": [ - "(selection: { name?: string | undefined; } & { dataset: { name: ", - "Branded", - "; } & { title?: string | undefined; }; }) => ", { - "pluginId": "logExplorer", + "pluginId": "dataViews", "scope": "common", - "docId": "kibLogExplorerPluginApi", - "section": "def-common.UnresolvedDatasetSelection", - "text": "UnresolvedDatasetSelection" + "docId": "kibDataViewsPluginApi", + "section": "def-common.DataView", + "text": "DataView" } ], - "path": "x-pack/plugins/log_explorer/common/dataset_selection/unresolved_dataset_selection.ts", + "path": "x-pack/plugins/log_explorer/public/controller/controller_customizations.ts", "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "logExplorer", - "id": "def-common.UnresolvedDatasetSelection.fromSelection.$1", - "type": "CompoundType", - "tags": [], - "label": "selection", - "description": [], - "signature": [ - "{ name?: string | undefined; } & { dataset: { name: ", - "Branded", - "; } & { title?: string | undefined; }; }" - ], - "path": "x-pack/plugins/log_explorer/common/dataset_selection/unresolved_dataset_selection.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] + "trackAdoption": false }, { "parentPluginId": "logExplorer", - "id": "def-common.UnresolvedDatasetSelection.create", - "type": "Function", + "id": "def-public.LogExplorerFlyoutContentProps.doc", + "type": "Object", "tags": [], - "label": "create", + "label": "doc", "description": [], "signature": [ - "(dataset: ", - "Dataset", - ") => ", - { - "pluginId": "logExplorer", - "scope": "common", - "docId": "kibLogExplorerPluginApi", - "section": "def-common.UnresolvedDatasetSelection", - "text": "UnresolvedDatasetSelection" - } + "LogDocument" ], - "path": "x-pack/plugins/log_explorer/common/dataset_selection/unresolved_dataset_selection.ts", + "path": "x-pack/plugins/log_explorer/public/controller/controller_customizations.ts", "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "logExplorer", - "id": "def-common.UnresolvedDatasetSelection.create.$1", - "type": "Object", - "tags": [], - "label": "dataset", - "description": [], - "signature": [ - "Dataset" - ], - "path": "x-pack/plugins/log_explorer/common/dataset_selection/unresolved_dataset_selection.ts", - "deprecated": false, - "trackAdoption": false, - "isRequired": true - } - ], - "returnComment": [] + "trackAdoption": false } ], "initialIsOpen": false } ], - "functions": [], - "interfaces": [], "enums": [], - "misc": [], - "objects": [] + "misc": [ + { + "parentPluginId": "logExplorer", + "id": "def-public.CreateLogExplorerController", + "type": "Type", + "tags": [], + "label": "CreateLogExplorerController", + "description": [], + "signature": [ + "({ customizations, initialState, }: { customizations?: ", + { + "pluginId": "logExplorer", + "scope": "public", + "docId": "kibLogExplorerPluginApi", + "section": "def-public.LogExplorerCustomizations", + "text": "LogExplorerCustomizations" + }, + " | undefined; initialState?: ", + { + "pluginId": "logExplorer", + "scope": "public", + "docId": "kibLogExplorerPluginApi", + "section": "def-public.LogExplorerPublicStateUpdate", + "text": "LogExplorerPublicStateUpdate" + }, + " | undefined; }) => Promise<", + { + "pluginId": "logExplorer", + "scope": "public", + "docId": "kibLogExplorerPluginApi", + "section": "def-public.LogExplorerController", + "text": "LogExplorerController" + }, + ">" + ], + "path": "x-pack/plugins/log_explorer/public/controller/create_controller.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "logExplorer", + "id": "def-public.CreateLogExplorerController.$1", + "type": "Object", + "tags": [], + "label": "__0", + "description": [], + "signature": [ + "{ customizations?: ", + { + "pluginId": "logExplorer", + "scope": "public", + "docId": "kibLogExplorerPluginApi", + "section": "def-public.LogExplorerCustomizations", + "text": "LogExplorerCustomizations" + }, + " | undefined; initialState?: ", + { + "pluginId": "logExplorer", + "scope": "public", + "docId": "kibLogExplorerPluginApi", + "section": "def-public.LogExplorerPublicStateUpdate", + "text": "LogExplorerPublicStateUpdate" + }, + " | undefined; }" + ], + "path": "x-pack/plugins/log_explorer/public/controller/create_controller.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-public.LogExplorerControllerContext", + "type": "Type", + "tags": [], + "label": "LogExplorerControllerContext", + "description": [], + "signature": [ + "(", + "WithDatasetSelection", + " & ", + "WithControlPanels", + " & ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.QueryState", + "text": "QueryState" + }, + " & ", + { + "pluginId": "logExplorer", + "scope": "common", + "docId": "kibLogExplorerPluginApi", + "section": "def-common.DisplayOptions", + "text": "DisplayOptions" + }, + ") | (", + "WithDatasetSelection", + " & ", + "WithControlPanels", + " & ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.QueryState", + "text": "QueryState" + }, + " & ", + { + "pluginId": "logExplorer", + "scope": "common", + "docId": "kibLogExplorerPluginApi", + "section": "def-common.DisplayOptions", + "text": "DisplayOptions" + }, + " & ", + "WithDiscoverStateContainer", + ") | (", + "WithDatasetSelection", + " & ", + "WithControlPanelGroupAPI", + " & ", + "WithControlPanels", + " & ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.QueryState", + "text": "QueryState" + }, + " & ", + { + "pluginId": "logExplorer", + "scope": "common", + "docId": "kibLogExplorerPluginApi", + "section": "def-common.DisplayOptions", + "text": "DisplayOptions" + }, + " & ", + "WithDiscoverStateContainer", + ")" + ], + "path": "x-pack/plugins/log_explorer/public/state_machines/log_explorer_controller/src/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-public.LogExplorerPublicState", + "type": "Type", + "tags": [], + "label": "LogExplorerPublicState", + "description": [], + "signature": [ + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.QueryState", + "text": "QueryState" + }, + " & ", + { + "pluginId": "logExplorer", + "scope": "common", + "docId": "kibLogExplorerPluginApi", + "section": "def-common.DisplayOptions", + "text": "DisplayOptions" + }, + " & { controls: ", + "ControlOptions", + "; datasetSelection: { selectionType: \"all\"; } | { selectionType: \"single\"; selection: { name?: string | undefined; } & { title?: string | undefined; } & { version?: string | undefined; } & { dataset: { name: ", + "Branded", + "; } & { title?: string | undefined; }; }; } | { selectionType: \"unresolved\"; selection: { name?: string | undefined; } & { dataset: { name: ", + "Branded", + "; } & { title?: string | undefined; }; }; }; }" + ], + "path": "x-pack/plugins/log_explorer/public/controller/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-public.LogExplorerPublicStateUpdate", + "type": "Type", + "tags": [], + "label": "LogExplorerPublicStateUpdate", + "description": [], + "signature": [ + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataQueryPluginApi", + "section": "def-common.QueryState", + "text": "QueryState" + }, + " & ", + { + "pluginId": "logExplorer", + "scope": "common", + "docId": "kibLogExplorerPluginApi", + "section": "def-common.PartialDisplayOptions", + "text": "PartialDisplayOptions" + }, + " & { controls?: ", + "ControlOptions", + " | undefined; datasetSelection?: { selectionType: \"all\"; } | { selectionType: \"single\"; selection: { name?: string | undefined; } & { title?: string | undefined; } & { version?: string | undefined; } & { dataset: { name: ", + "Branded", + "; } & { title?: string | undefined; }; }; } | { selectionType: \"unresolved\"; selection: { name?: string | undefined; } & { dataset: { name: ", + "Branded", + "; } & { title?: string | undefined; }; }; } | undefined; }" + ], + "path": "x-pack/plugins/log_explorer/public/controller/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [], + "setup": { + "parentPluginId": "logExplorer", + "id": "def-public.LogExplorerPluginSetup", + "type": "Interface", + "tags": [], + "label": "LogExplorerPluginSetup", + "description": [], + "path": "x-pack/plugins/log_explorer/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "logExplorer", + "id": "def-public.LogExplorerPluginSetup.locators", + "type": "Object", + "tags": [], + "label": "locators", + "description": [], + "signature": [ + "LogExplorerLocators" + ], + "path": "x-pack/plugins/log_explorer/public/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "lifecycle": "setup", + "initialIsOpen": true + }, + "start": { + "parentPluginId": "logExplorer", + "id": "def-public.LogExplorerPluginStart", + "type": "Interface", + "tags": [], + "label": "LogExplorerPluginStart", + "description": [], + "path": "x-pack/plugins/log_explorer/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "logExplorer", + "id": "def-public.LogExplorerPluginStart.LogExplorer", + "type": "CompoundType", + "tags": [], + "label": "LogExplorer", + "description": [], + "signature": [ + "React.ComponentClass<", + "LogExplorerProps", + ", any> | React.FunctionComponent<", + "LogExplorerProps", + ">" + ], + "path": "x-pack/plugins/log_explorer/public/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-public.LogExplorerPluginStart.createLogExplorerController", + "type": "Function", + "tags": [], + "label": "createLogExplorerController", + "description": [], + "signature": [ + "({ customizations, initialState, }: { customizations?: ", + { + "pluginId": "logExplorer", + "scope": "public", + "docId": "kibLogExplorerPluginApi", + "section": "def-public.LogExplorerCustomizations", + "text": "LogExplorerCustomizations" + }, + " | undefined; initialState?: ", + { + "pluginId": "logExplorer", + "scope": "public", + "docId": "kibLogExplorerPluginApi", + "section": "def-public.LogExplorerPublicStateUpdate", + "text": "LogExplorerPublicStateUpdate" + }, + " | undefined; }) => Promise<", + { + "pluginId": "logExplorer", + "scope": "public", + "docId": "kibLogExplorerPluginApi", + "section": "def-public.LogExplorerController", + "text": "LogExplorerController" + }, + ">" + ], + "path": "x-pack/plugins/log_explorer/public/types.ts", + "deprecated": false, + "trackAdoption": false, + "returnComment": [], + "children": [ + { + "parentPluginId": "logExplorer", + "id": "def-public.LogExplorerPluginStart.createLogExplorerController.$1", + "type": "Object", + "tags": [], + "label": "__0", + "description": [], + "signature": [ + "{ customizations?: ", + { + "pluginId": "logExplorer", + "scope": "public", + "docId": "kibLogExplorerPluginApi", + "section": "def-public.LogExplorerCustomizations", + "text": "LogExplorerCustomizations" + }, + " | undefined; initialState?: ", + { + "pluginId": "logExplorer", + "scope": "public", + "docId": "kibLogExplorerPluginApi", + "section": "def-public.LogExplorerPublicStateUpdate", + "text": "LogExplorerPublicStateUpdate" + }, + " | undefined; }" + ], + "path": "x-pack/plugins/log_explorer/public/controller/create_controller.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "lifecycle": "start", + "initialIsOpen": true + } + }, + "server": { + "classes": [], + "functions": [], + "interfaces": [], + "enums": [], + "misc": [], + "objects": [] + }, + "common": { + "classes": [ + { + "parentPluginId": "logExplorer", + "id": "def-common.AllDatasetSelection", + "type": "Class", + "tags": [], + "label": "AllDatasetSelection", + "description": [], + "signature": [ + { + "pluginId": "logExplorer", + "scope": "common", + "docId": "kibLogExplorerPluginApi", + "section": "def-common.AllDatasetSelection", + "text": "AllDatasetSelection" + }, + " implements ", + "DatasetSelectionStrategy" + ], + "path": "x-pack/plugins/log_explorer/common/dataset_selection/all_dataset_selection.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "logExplorer", + "id": "def-common.AllDatasetSelection.selectionType", + "type": "string", + "tags": [], + "label": "selectionType", + "description": [], + "signature": [ + "\"all\"" + ], + "path": "x-pack/plugins/log_explorer/common/dataset_selection/all_dataset_selection.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-common.AllDatasetSelection.selection", + "type": "Object", + "tags": [], + "label": "selection", + "description": [], + "signature": [ + "{ dataset: ", + "Dataset", + "; }" + ], + "path": "x-pack/plugins/log_explorer/common/dataset_selection/all_dataset_selection.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-common.AllDatasetSelection.toDataviewSpec", + "type": "Function", + "tags": [], + "label": "toDataviewSpec", + "description": [], + "signature": [ + "() => ", + "DataViewSpecWithId" + ], + "path": "x-pack/plugins/log_explorer/common/dataset_selection/all_dataset_selection.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "logExplorer", + "id": "def-common.AllDatasetSelection.toPlainSelection", + "type": "Function", + "tags": [], + "label": "toPlainSelection", + "description": [], + "signature": [ + "() => { selectionType: \"all\"; }" + ], + "path": "x-pack/plugins/log_explorer/common/dataset_selection/all_dataset_selection.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "logExplorer", + "id": "def-common.AllDatasetSelection.create", + "type": "Function", + "tags": [], + "label": "create", + "description": [], + "signature": [ + "() => ", + { + "pluginId": "logExplorer", + "scope": "common", + "docId": "kibLogExplorerPluginApi", + "section": "def-common.AllDatasetSelection", + "text": "AllDatasetSelection" + } + ], + "path": "x-pack/plugins/log_explorer/common/dataset_selection/all_dataset_selection.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-common.UnresolvedDatasetSelection", + "type": "Class", + "tags": [], + "label": "UnresolvedDatasetSelection", + "description": [], + "signature": [ + { + "pluginId": "logExplorer", + "scope": "common", + "docId": "kibLogExplorerPluginApi", + "section": "def-common.UnresolvedDatasetSelection", + "text": "UnresolvedDatasetSelection" + }, + " implements ", + "DatasetSelectionStrategy" + ], + "path": "x-pack/plugins/log_explorer/common/dataset_selection/unresolved_dataset_selection.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "logExplorer", + "id": "def-common.UnresolvedDatasetSelection.selectionType", + "type": "string", + "tags": [], + "label": "selectionType", + "description": [], + "signature": [ + "\"unresolved\"" + ], + "path": "x-pack/plugins/log_explorer/common/dataset_selection/unresolved_dataset_selection.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-common.UnresolvedDatasetSelection.selection", + "type": "Object", + "tags": [], + "label": "selection", + "description": [], + "signature": [ + "{ name?: string | undefined; dataset: ", + "Dataset", + "; }" + ], + "path": "x-pack/plugins/log_explorer/common/dataset_selection/unresolved_dataset_selection.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-common.UnresolvedDatasetSelection.toDataviewSpec", + "type": "Function", + "tags": [], + "label": "toDataviewSpec", + "description": [], + "signature": [ + "() => ", + "DataViewSpecWithId" + ], + "path": "x-pack/plugins/log_explorer/common/dataset_selection/unresolved_dataset_selection.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "logExplorer", + "id": "def-common.UnresolvedDatasetSelection.toPlainSelection", + "type": "Function", + "tags": [], + "label": "toPlainSelection", + "description": [], + "signature": [ + "() => { selectionType: \"unresolved\"; selection: { name: string | undefined; dataset: { name: ", + "Branded", + "; title: string; }; }; }" + ], + "path": "x-pack/plugins/log_explorer/common/dataset_selection/unresolved_dataset_selection.ts", + "deprecated": false, + "trackAdoption": false, + "children": [], + "returnComment": [] + }, + { + "parentPluginId": "logExplorer", + "id": "def-common.UnresolvedDatasetSelection.fromSelection", + "type": "Function", + "tags": [], + "label": "fromSelection", + "description": [], + "signature": [ + "(selection: { name?: string | undefined; } & { dataset: { name: ", + "Branded", + "; } & { title?: string | undefined; }; }) => ", + { + "pluginId": "logExplorer", + "scope": "common", + "docId": "kibLogExplorerPluginApi", + "section": "def-common.UnresolvedDatasetSelection", + "text": "UnresolvedDatasetSelection" + } + ], + "path": "x-pack/plugins/log_explorer/common/dataset_selection/unresolved_dataset_selection.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "logExplorer", + "id": "def-common.UnresolvedDatasetSelection.fromSelection.$1", + "type": "CompoundType", + "tags": [], + "label": "selection", + "description": [], + "signature": [ + "{ name?: string | undefined; } & { dataset: { name: ", + "Branded", + "; } & { title?: string | undefined; }; }" + ], + "path": "x-pack/plugins/log_explorer/common/dataset_selection/unresolved_dataset_selection.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + }, + { + "parentPluginId": "logExplorer", + "id": "def-common.UnresolvedDatasetSelection.create", + "type": "Function", + "tags": [], + "label": "create", + "description": [], + "signature": [ + "(dataset: ", + "Dataset", + ") => ", + { + "pluginId": "logExplorer", + "scope": "common", + "docId": "kibLogExplorerPluginApi", + "section": "def-common.UnresolvedDatasetSelection", + "text": "UnresolvedDatasetSelection" + } + ], + "path": "x-pack/plugins/log_explorer/common/dataset_selection/unresolved_dataset_selection.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "logExplorer", + "id": "def-common.UnresolvedDatasetSelection.create.$1", + "type": "Object", + "tags": [], + "label": "dataset", + "description": [], + "signature": [ + "Dataset" + ], + "path": "x-pack/plugins/log_explorer/common/dataset_selection/unresolved_dataset_selection.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [] + } + ], + "initialIsOpen": false + } + ], + "functions": [ + { + "parentPluginId": "logExplorer", + "id": "def-common.hydrateDatasetSelection", + "type": "Function", + "tags": [], + "label": "hydrateDatasetSelection", + "description": [], + "signature": [ + "(datasetSelection: { selectionType: \"all\"; } | { selectionType: \"single\"; selection: { name?: string | undefined; } & { title?: string | undefined; } & { version?: string | undefined; } & { dataset: { name: ", + "Branded", + "; } & { title?: string | undefined; }; }; } | { selectionType: \"unresolved\"; selection: { name?: string | undefined; } & { dataset: { name: ", + "Branded", + "; } & { title?: string | undefined; }; }; }) => ", + { + "pluginId": "logExplorer", + "scope": "common", + "docId": "kibLogExplorerPluginApi", + "section": "def-common.AllDatasetSelection", + "text": "AllDatasetSelection" + }, + " | ", + "SingleDatasetSelection", + " | ", + { + "pluginId": "logExplorer", + "scope": "common", + "docId": "kibLogExplorerPluginApi", + "section": "def-common.UnresolvedDatasetSelection", + "text": "UnresolvedDatasetSelection" + } + ], + "path": "x-pack/plugins/log_explorer/common/dataset_selection/hydrate_dataset_selection.ts.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "logExplorer", + "id": "def-common.hydrateDatasetSelection.$1", + "type": "CompoundType", + "tags": [], + "label": "datasetSelection", + "description": [], + "signature": [ + "{ selectionType: \"all\"; } | { selectionType: \"single\"; selection: { name?: string | undefined; } & { title?: string | undefined; } & { version?: string | undefined; } & { dataset: { name: ", + "Branded", + "; } & { title?: string | undefined; }; }; } | { selectionType: \"unresolved\"; selection: { name?: string | undefined; } & { dataset: { name: ", + "Branded", + "; } & { title?: string | undefined; }; }; }" + ], + "path": "x-pack/plugins/log_explorer/common/dataset_selection/hydrate_dataset_selection.ts.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], + "interfaces": [ + { + "parentPluginId": "logExplorer", + "id": "def-common.ChartDisplayOptions", + "type": "Interface", + "tags": [], + "label": "ChartDisplayOptions", + "description": [], + "path": "x-pack/plugins/log_explorer/common/display_options/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "logExplorer", + "id": "def-common.ChartDisplayOptions.breakdownField", + "type": "CompoundType", + "tags": [], + "label": "breakdownField", + "description": [], + "signature": [ + "string | null" + ], + "path": "x-pack/plugins/log_explorer/common/display_options/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-common.DisplayOptions", + "type": "Interface", + "tags": [], + "label": "DisplayOptions", + "description": [], + "path": "x-pack/plugins/log_explorer/common/display_options/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "logExplorer", + "id": "def-common.DisplayOptions.grid", + "type": "Object", + "tags": [], + "label": "grid", + "description": [], + "signature": [ + { + "pluginId": "logExplorer", + "scope": "common", + "docId": "kibLogExplorerPluginApi", + "section": "def-common.GridDisplayOptions", + "text": "GridDisplayOptions" + } + ], + "path": "x-pack/plugins/log_explorer/common/display_options/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-common.DisplayOptions.chart", + "type": "Object", + "tags": [], + "label": "chart", + "description": [], + "signature": [ + { + "pluginId": "logExplorer", + "scope": "common", + "docId": "kibLogExplorerPluginApi", + "section": "def-common.ChartDisplayOptions", + "text": "ChartDisplayOptions" + } + ], + "path": "x-pack/plugins/log_explorer/common/display_options/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-common.GridColumnDisplayOptions", + "type": "Interface", + "tags": [], + "label": "GridColumnDisplayOptions", + "description": [], + "path": "x-pack/plugins/log_explorer/common/display_options/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "logExplorer", + "id": "def-common.GridColumnDisplayOptions.field", + "type": "string", + "tags": [], + "label": "field", + "description": [], + "path": "x-pack/plugins/log_explorer/common/display_options/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-common.GridColumnDisplayOptions.width", + "type": "number", + "tags": [], + "label": "width", + "description": [], + "signature": [ + "number | undefined" + ], + "path": "x-pack/plugins/log_explorer/common/display_options/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-common.GridDisplayOptions", + "type": "Interface", + "tags": [], + "label": "GridDisplayOptions", + "description": [], + "path": "x-pack/plugins/log_explorer/common/display_options/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "logExplorer", + "id": "def-common.GridDisplayOptions.columns", + "type": "Array", + "tags": [], + "label": "columns", + "description": [], + "signature": [ + { + "pluginId": "logExplorer", + "scope": "common", + "docId": "kibLogExplorerPluginApi", + "section": "def-common.GridColumnDisplayOptions", + "text": "GridColumnDisplayOptions" + }, + "[]" + ], + "path": "x-pack/plugins/log_explorer/common/display_options/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-common.GridDisplayOptions.rows", + "type": "Object", + "tags": [], + "label": "rows", + "description": [], + "signature": [ + { + "pluginId": "logExplorer", + "scope": "common", + "docId": "kibLogExplorerPluginApi", + "section": "def-common.GridRowsDisplayOptions", + "text": "GridRowsDisplayOptions" + } + ], + "path": "x-pack/plugins/log_explorer/common/display_options/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-common.GridRowsDisplayOptions", + "type": "Interface", + "tags": [], + "label": "GridRowsDisplayOptions", + "description": [], + "path": "x-pack/plugins/log_explorer/common/display_options/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "logExplorer", + "id": "def-common.GridRowsDisplayOptions.rowHeight", + "type": "number", + "tags": [], + "label": "rowHeight", + "description": [], + "path": "x-pack/plugins/log_explorer/common/display_options/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-common.GridRowsDisplayOptions.rowsPerPage", + "type": "number", + "tags": [], + "label": "rowsPerPage", + "description": [], + "path": "x-pack/plugins/log_explorer/common/display_options/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-common.PartialDisplayOptions", + "type": "Interface", + "tags": [], + "label": "PartialDisplayOptions", + "description": [], + "path": "x-pack/plugins/log_explorer/common/display_options/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "logExplorer", + "id": "def-common.PartialDisplayOptions.grid", + "type": "Object", + "tags": [], + "label": "grid", + "description": [], + "signature": [ + "Partial & { rows?: Partial<", + { + "pluginId": "logExplorer", + "scope": "common", + "docId": "kibLogExplorerPluginApi", + "section": "def-common.GridRowsDisplayOptions", + "text": "GridRowsDisplayOptions" + }, + "> | undefined; }> | undefined" + ], + "path": "x-pack/plugins/log_explorer/common/display_options/types.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-common.PartialDisplayOptions.chart", + "type": "Object", + "tags": [], + "label": "chart", + "description": [], + "signature": [ + "Partial<", + { + "pluginId": "logExplorer", + "scope": "common", + "docId": "kibLogExplorerPluginApi", + "section": "def-common.ChartDisplayOptions", + "text": "ChartDisplayOptions" + }, + "> | undefined" + ], + "path": "x-pack/plugins/log_explorer/common/display_options/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "initialIsOpen": false + } + ], + "enums": [], + "misc": [ + { + "parentPluginId": "logExplorer", + "id": "def-common.availableControlPanelFields", + "type": "Array", + "tags": [], + "label": "availableControlPanelFields", + "description": [], + "signature": [ + "\"data_stream.namespace\"[]" + ], + "path": "x-pack/plugins/log_explorer/common/control_panels/available_control_panels.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-common.AvailableControlPanels", + "type": "Type", + "tags": [], + "label": "AvailableControlPanels", + "description": [], + "signature": [ + "{ readonly NAMESPACE: \"data_stream.namespace\"; }" + ], + "path": "x-pack/plugins/log_explorer/common/control_panels/available_control_panels.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-common.ControlPanels", + "type": "Type", + "tags": [], + "label": "ControlPanels", + "description": [], + "signature": [ + "{ [x: string]: { order: number; width: \"small\" | \"medium\" | \"large\"; grow: boolean; type: string; explicitInput: { id: string; } & { dataViewId?: string | undefined; exclude?: boolean | undefined; existsSelected?: boolean | undefined; fieldName?: string | undefined; selectedOptions?: string[] | undefined; title?: string | undefined; }; }; }" + ], + "path": "x-pack/plugins/log_explorer/common/control_panels/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-common.DatasetSelectionPlain", + "type": "Type", + "tags": [], + "label": "DatasetSelectionPlain", + "description": [], + "signature": [ + "{ selectionType: \"all\"; } | { selectionType: \"single\"; selection: { name?: string | undefined; } & { title?: string | undefined; } & { version?: string | undefined; } & { dataset: { name: ", + "Branded", + "; } & { title?: string | undefined; }; }; } | { selectionType: \"unresolved\"; selection: { name?: string | undefined; } & { dataset: { name: ", + "Branded", + "; } & { title?: string | undefined; }; }; }" + ], + "path": "x-pack/plugins/log_explorer/common/dataset_selection/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-common.PartialChartDisplayOptions", + "type": "Type", + "tags": [], + "label": "PartialChartDisplayOptions", + "description": [], + "signature": [ + "{ breakdownField?: string | null | undefined; }" + ], + "path": "x-pack/plugins/log_explorer/common/display_options/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-common.PartialGridDisplayOptions", + "type": "Type", + "tags": [], + "label": "PartialGridDisplayOptions", + "description": [], + "signature": [ + "{ columns?: ", + { + "pluginId": "logExplorer", + "scope": "common", + "docId": "kibLogExplorerPluginApi", + "section": "def-common.GridColumnDisplayOptions", + "text": "GridColumnDisplayOptions" + }, + "[] | undefined; rows?: Partial<", + { + "pluginId": "logExplorer", + "scope": "common", + "docId": "kibLogExplorerPluginApi", + "section": "def-common.GridRowsDisplayOptions", + "text": "GridRowsDisplayOptions" + }, + "> | undefined; }" + ], + "path": "x-pack/plugins/log_explorer/common/display_options/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-common.PartialGridRowsDisplayOptions", + "type": "Type", + "tags": [], + "label": "PartialGridRowsDisplayOptions", + "description": [], + "signature": [ + "{ rowHeight?: number | undefined; rowsPerPage?: number | undefined; }" + ], + "path": "x-pack/plugins/log_explorer/common/display_options/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], + "objects": [ + { + "parentPluginId": "logExplorer", + "id": "def-common.availableControlsPanels", + "type": "Object", + "tags": [], + "label": "availableControlsPanels", + "description": [], + "signature": [ + "{ readonly NAMESPACE: \"data_stream.namespace\"; }" + ], + "path": "x-pack/plugins/log_explorer/common/control_panels/available_control_panels.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-common.controlPanelConfigs", + "type": "Object", + "tags": [], + "label": "controlPanelConfigs", + "description": [], + "path": "x-pack/plugins/log_explorer/common/control_panels/available_control_panels.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "logExplorer", + "id": "def-common.controlPanelConfigs.availableControlsPanels.NAMESPACE", + "type": "Object", + "tags": [], + "label": "[availableControlsPanels.NAMESPACE]", + "description": [], + "path": "x-pack/plugins/log_explorer/common/control_panels/available_control_panels.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "logExplorer", + "id": "def-common.controlPanelConfigs.availableControlsPanels.NAMESPACE.order", + "type": "number", + "tags": [], + "label": "order", + "description": [], + "path": "x-pack/plugins/log_explorer/common/control_panels/available_control_panels.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-common.controlPanelConfigs.availableControlsPanels.NAMESPACE.width", + "type": "string", + "tags": [], + "label": "width", + "description": [], + "signature": [ + "\"medium\"" + ], + "path": "x-pack/plugins/log_explorer/common/control_panels/available_control_panels.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-common.controlPanelConfigs.availableControlsPanels.NAMESPACE.grow", + "type": "boolean", + "tags": [], + "label": "grow", + "description": [], + "signature": [ + "false" + ], + "path": "x-pack/plugins/log_explorer/common/control_panels/available_control_panels.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-common.controlPanelConfigs.availableControlsPanels.NAMESPACE.type", + "type": "string", + "tags": [], + "label": "type", + "description": [], + "path": "x-pack/plugins/log_explorer/common/control_panels/available_control_panels.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-common.controlPanelConfigs.availableControlsPanels.NAMESPACE.explicitInput", + "type": "Object", + "tags": [], + "label": "explicitInput", + "description": [], + "path": "x-pack/plugins/log_explorer/common/control_panels/available_control_panels.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "logExplorer", + "id": "def-common.controlPanelConfigs.availableControlsPanels.NAMESPACE.explicitInput.id", + "type": "string", + "tags": [], + "label": "id", + "description": [], + "signature": [ + "\"data_stream.namespace\"" + ], + "path": "x-pack/plugins/log_explorer/common/control_panels/available_control_panels.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-common.controlPanelConfigs.availableControlsPanels.NAMESPACE.explicitInput.fieldName", + "type": "string", + "tags": [], + "label": "fieldName", + "description": [], + "signature": [ + "\"data_stream.namespace\"" + ], + "path": "x-pack/plugins/log_explorer/common/control_panels/available_control_panels.ts", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-common.controlPanelConfigs.availableControlsPanels.NAMESPACE.explicitInput.title", + "type": "string", + "tags": [], + "label": "title", + "description": [], + "path": "x-pack/plugins/log_explorer/common/control_panels/available_control_panels.ts", + "deprecated": false, + "trackAdoption": false + } + ] + } + ] + } + ], + "initialIsOpen": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-common.ControlPanelRT", + "type": "Object", + "tags": [], + "label": "ControlPanelRT", + "description": [], + "signature": [ + "RecordC", + "<", + "StringC", + ", ", + "TypeC", + "<{ order: ", + "NumberC", + "; width: ", + "UnionC", + "<[", + "LiteralC", + "<\"medium\">, ", + "LiteralC", + "<\"small\">, ", + "LiteralC", + "<\"large\">]>; grow: ", + "BooleanC", + "; type: ", + "StringC", + "; explicitInput: ", + "IntersectionC", + "<[", + "TypeC", + "<{ id: ", + "StringC", + "; }>, ", + "PartialC", + "<{ dataViewId: ", + "StringC", + "; exclude: ", + "BooleanC", + "; existsSelected: ", + "BooleanC", + "; fieldName: ", + "StringC", + "; selectedOptions: ", + "ArrayC", + "<", + "StringC", + ">; title: ", + "UnionC", + "<[", + "StringC", + ", ", + "UndefinedC", + "]>; }>]>; }>>" + ], + "path": "x-pack/plugins/log_explorer/common/control_panels/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, + { + "parentPluginId": "logExplorer", + "id": "def-common.datasetSelectionPlainRT", + "type": "Object", + "tags": [], + "label": "datasetSelectionPlainRT", + "description": [], + "signature": [ + "UnionC", + "<[", + "TypeC", + "<{ selectionType: ", + "LiteralC", + "<\"all\">; }>, ", + "TypeC", + "<{ selectionType: ", + "LiteralC", + "<\"single\">; selection: ", + "IntersectionC", + "<[", + "PartialC", + "<{ name: ", + "StringC", + "; }>, ", + "PartialC", + "<{ title: ", + "StringC", + "; }>, ", + "PartialC", + "<{ version: ", + "StringC", + "; }>, ", + "TypeC", + "<{ dataset: ", + "ExactC", + "<", + "IntersectionC", + "<[", + "TypeC", + "<{ name: ", + "BrandC", + "<", + "StringC", + ", ", + "IndexPatternBrand", + ">; }>, ", + "PartialC", + "<{ title: ", + "StringC", + "; }>]>>; }>]>; }>, ", + "TypeC", + "<{ selectionType: ", + "LiteralC", + "<\"unresolved\">; selection: ", + "IntersectionC", + "<[", + "PartialC", + "<{ name: ", + "StringC", + "; }>, ", + "TypeC", + "<{ dataset: ", + "ExactC", + "<", + "IntersectionC", + "<[", + "TypeC", + "<{ name: ", + "BrandC", + "<", + "StringC", + ", ", + "IndexPatternBrand", + ">; }>, ", + "PartialC", + "<{ title: ", + "StringC", + "; }>]>>; }>]>; }>]>" + ], + "path": "x-pack/plugins/log_explorer/common/dataset_selection/types.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ] } } \ No newline at end of file diff --git a/api_docs/log_explorer.mdx b/api_docs/log_explorer.mdx index 783ebbe5f8230..cdee6cfd13309 100644 --- a/api_docs/log_explorer.mdx +++ b/api_docs/log_explorer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/logExplorer title: "logExplorer" image: https://source.unsplash.com/400x175/?github description: API docs for the logExplorer plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'logExplorer'] --- import logExplorerObj from './log_explorer.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 26 | 0 | 26 | 8 | +| 87 | 0 | 87 | 16 | ## Client @@ -31,11 +31,29 @@ Contact [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux ### Start +### Functions + + ### Interfaces +### Consts, variables and types + + ## Common +### Objects + + +### Functions + + ### Classes +### Interfaces + + +### Consts, variables and types + + diff --git a/api_docs/logs_shared.mdx b/api_docs/logs_shared.mdx index 138e859ea3562..4516bb866f0a3 100644 --- a/api_docs/logs_shared.mdx +++ b/api_docs/logs_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/logsShared title: "logsShared" image: https://source.unsplash.com/400x175/?github description: API docs for the logsShared plugin -date: 2023-12-06 +date: 2023-12-19 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 aab750ff0097c..27b0ec442692c 100644 --- a/api_docs/management.mdx +++ b/api_docs/management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/management title: "management" image: https://source.unsplash.com/400x175/?github description: API docs for the management plugin -date: 2023-12-06 +date: 2023-12-19 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 9eebd149f52bb..d9aa39d149115 100644 --- a/api_docs/maps.mdx +++ b/api_docs/maps.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/maps title: "maps" image: https://source.unsplash.com/400x175/?github description: API docs for the maps plugin -date: 2023-12-06 +date: 2023-12-19 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 4db816d33f3b3..6dac20d0d9caa 100644 --- a/api_docs/maps_ems.mdx +++ b/api_docs/maps_ems.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/mapsEms title: "mapsEms" image: https://source.unsplash.com/400x175/?github description: API docs for the mapsEms plugin -date: 2023-12-06 +date: 2023-12-19 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 b72d87a2f1c48..f5d983b5b3dfd 100644 --- a/api_docs/metrics_data_access.mdx +++ b/api_docs/metrics_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/metricsDataAccess title: "metricsDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the metricsDataAccess plugin -date: 2023-12-06 +date: 2023-12-19 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 92843b6328b8a..f7b212e4bec46 100644 --- a/api_docs/ml.mdx +++ b/api_docs/ml.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ml title: "ml" image: https://source.unsplash.com/400x175/?github description: API docs for the ml plugin -date: 2023-12-06 +date: 2023-12-19 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 aeeabb3551660..555309035859b 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: 2023-12-06 +date: 2023-12-19 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 d973f7b0f292f..9757bd03e27d5 100644 --- a/api_docs/monitoring.mdx +++ b/api_docs/monitoring.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoring title: "monitoring" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoring plugin -date: 2023-12-06 +date: 2023-12-19 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 d943a77f50ed1..f3df3d670f0b3 100644 --- a/api_docs/monitoring_collection.mdx +++ b/api_docs/monitoring_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/monitoringCollection title: "monitoringCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the monitoringCollection plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'monitoringCollection'] --- import monitoringCollectionObj from './monitoring_collection.devdocs.json'; diff --git a/api_docs/navigation.devdocs.json b/api_docs/navigation.devdocs.json index 00f1ae69aaf03..245711ed0a0e7 100644 --- a/api_docs/navigation.devdocs.json +++ b/api_docs/navigation.devdocs.json @@ -30,18 +30,22 @@ "pluginId": "navigation", "scope": "public", "docId": "kibNavigationPluginApi", - "section": "def-public.NavigationPublicPluginSetup", - "text": "NavigationPublicPluginSetup" + "section": "def-public.NavigationPublicSetup", + "text": "NavigationPublicSetup" }, ", ", { "pluginId": "navigation", "scope": "public", "docId": "kibNavigationPluginApi", - "section": "def-public.NavigationPublicPluginStart", - "text": "NavigationPublicPluginStart" + "section": "def-public.NavigationPublicStart", + "text": "NavigationPublicStart" }, - ", object, object>" + ", ", + "NavigationPublicSetupDependencies", + ", ", + "NavigationPublicStartDependencies", + ">" ], "path": "src/plugins/navigation/public/plugin.ts", "deprecated": false, @@ -66,7 +70,7 @@ "id": "def-public.NavigationPublicPlugin.Unnamed.$1", "type": "Object", "tags": [], - "label": "initializerContext", + "label": "_initializerContext", "description": [], "signature": [ { @@ -94,7 +98,7 @@ "label": "setup", "description": [], "signature": [ - "(core: ", + "(_core: ", { "pluginId": "@kbn/core-lifecycle-browser", "scope": "common", @@ -107,8 +111,8 @@ "pluginId": "navigation", "scope": "public", "docId": "kibNavigationPluginApi", - "section": "def-public.NavigationPublicPluginSetup", - "text": "NavigationPublicPluginSetup" + "section": "def-public.NavigationPublicSetup", + "text": "NavigationPublicSetup" } ], "path": "src/plugins/navigation/public/plugin.ts", @@ -120,7 +124,7 @@ "id": "def-public.NavigationPublicPlugin.setup.$1", "type": "Object", "tags": [], - "label": "core", + "label": "_core", "description": [], "signature": [ { @@ -148,7 +152,7 @@ "label": "start", "description": [], "signature": [ - "(core: ", + "(_core: ", { "pluginId": "@kbn/core-lifecycle-browser", "scope": "common", @@ -157,14 +161,14 @@ "text": "CoreStart" }, ", { unifiedSearch }: ", - "NavigationPluginStartDependencies", + "NavigationPublicStartDependencies", ") => ", { "pluginId": "navigation", "scope": "public", "docId": "kibNavigationPluginApi", - "section": "def-public.NavigationPublicPluginStart", - "text": "NavigationPublicPluginStart" + "section": "def-public.NavigationPublicStart", + "text": "NavigationPublicStart" } ], "path": "src/plugins/navigation/public/plugin.ts", @@ -176,7 +180,7 @@ "id": "def-public.NavigationPublicPlugin.start.$1", "type": "Object", "tags": [], - "label": "core", + "label": "_core", "description": [], "signature": [ { @@ -200,7 +204,7 @@ "label": "{ unifiedSearch }", "description": [], "signature": [ - "NavigationPluginStartDependencies" + "NavigationPublicStartDependencies" ], "path": "src/plugins/navigation/public/plugin.ts", "deprecated": false, @@ -278,6 +282,140 @@ ], "returnComment": [], "initialIsOpen": false + }, + { + "parentPluginId": "navigation", + "id": "def-public.TopNavMenuBadges", + "type": "Function", + "tags": [], + "label": "TopNavMenuBadges", + "description": [], + "signature": [ + "({ badges }: { badges: ", + { + "pluginId": "navigation", + "scope": "public", + "docId": "kibNavigationPluginApi", + "section": "def-public.TopNavMenuBadgeProps", + "text": "TopNavMenuBadgeProps" + }, + "[] | undefined; }) => JSX.Element | null" + ], + "path": "src/plugins/navigation/public/top_nav_menu/top_nav_menu_badges.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "navigation", + "id": "def-public.TopNavMenuBadges.$1", + "type": "Object", + "tags": [], + "label": "{ badges }", + "description": [], + "path": "src/plugins/navigation/public/top_nav_menu/top_nav_menu_badges.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "navigation", + "id": "def-public.TopNavMenuBadges.$1.badges", + "type": "Array", + "tags": [], + "label": "badges", + "description": [], + "signature": [ + { + "pluginId": "navigation", + "scope": "public", + "docId": "kibNavigationPluginApi", + "section": "def-public.TopNavMenuBadgeProps", + "text": "TopNavMenuBadgeProps" + }, + "[] | undefined" + ], + "path": "src/plugins/navigation/public/top_nav_menu/top_nav_menu_badges.tsx", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "returnComment": [], + "initialIsOpen": false + }, + { + "parentPluginId": "navigation", + "id": "def-public.TopNavMenuItems", + "type": "Function", + "tags": [], + "label": "TopNavMenuItems", + "description": [], + "signature": [ + "({ config, className, }: { config: ", + { + "pluginId": "navigation", + "scope": "public", + "docId": "kibNavigationPluginApi", + "section": "def-public.TopNavMenuData", + "text": "TopNavMenuData" + }, + "[] | undefined; className?: string | undefined; }) => JSX.Element | null" + ], + "path": "src/plugins/navigation/public/top_nav_menu/top_nav_menu_items.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "navigation", + "id": "def-public.TopNavMenuItems.$1", + "type": "Object", + "tags": [], + "label": "{\n config,\n className,\n}", + "description": [], + "path": "src/plugins/navigation/public/top_nav_menu/top_nav_menu_items.tsx", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "navigation", + "id": "def-public.TopNavMenuItems.$1.config", + "type": "Array", + "tags": [], + "label": "config", + "description": [], + "signature": [ + { + "pluginId": "navigation", + "scope": "public", + "docId": "kibNavigationPluginApi", + "section": "def-public.TopNavMenuData", + "text": "TopNavMenuData" + }, + "[] | undefined" + ], + "path": "src/plugins/navigation/public/top_nav_menu/top_nav_menu_items.tsx", + "deprecated": false, + "trackAdoption": false + }, + { + "parentPluginId": "navigation", + "id": "def-public.TopNavMenuItems.$1.className", + "type": "string", + "tags": [], + "label": "className", + "description": [], + "signature": [ + "string | undefined" + ], + "path": "src/plugins/navigation/public/top_nav_menu/top_nav_menu_items.tsx", + "deprecated": false, + "trackAdoption": false + } + ] + } + ], + "returnComment": [], + "initialIsOpen": false } ], "interfaces": [ @@ -685,7 +823,7 @@ "EuiToolTipProps", "> | undefined; renderCustomBadge?: ((props: { badgeText: string; }) => React.ReactElement>) | undefined; }" ], - "path": "src/plugins/navigation/public/top_nav_menu/top_nav_menu.tsx", + "path": "src/plugins/navigation/public/top_nav_menu/top_nav_menu_badges.tsx", "deprecated": false, "trackAdoption": false, "initialIsOpen": false @@ -749,10 +887,10 @@ "objects": [], "setup": { "parentPluginId": "navigation", - "id": "def-public.NavigationPublicPluginSetup", + "id": "def-public.NavigationPublicSetup", "type": "Interface", "tags": [], - "label": "NavigationPublicPluginSetup", + "label": "NavigationPublicSetup", "description": [], "path": "src/plugins/navigation/public/types.ts", "deprecated": false, @@ -760,7 +898,7 @@ "children": [ { "parentPluginId": "navigation", - "id": "def-public.NavigationPublicPluginSetup.registerMenuItem", + "id": "def-public.NavigationPublicSetup.registerMenuItem", "type": "Function", "tags": [], "label": "registerMenuItem", @@ -777,7 +915,7 @@ "children": [ { "parentPluginId": "navigation", - "id": "def-public.NavigationPublicPluginSetup.registerMenuItem.$1", + "id": "def-public.NavigationPublicSetup.registerMenuItem.$1", "type": "Object", "tags": [], "label": "menuItem", @@ -797,10 +935,10 @@ }, "start": { "parentPluginId": "navigation", - "id": "def-public.NavigationPublicPluginStart", + "id": "def-public.NavigationPublicStart", "type": "Interface", "tags": [], - "label": "NavigationPublicPluginStart", + "label": "NavigationPublicStart", "description": [], "path": "src/plugins/navigation/public/types.ts", "deprecated": false, @@ -808,7 +946,7 @@ "children": [ { "parentPluginId": "navigation", - "id": "def-public.NavigationPublicPluginStart.ui", + "id": "def-public.NavigationPublicStart.ui", "type": "Object", "tags": [], "label": "ui", diff --git a/api_docs/navigation.mdx b/api_docs/navigation.mdx index ee704e09120e0..c03f835f59531 100644 --- a/api_docs/navigation.mdx +++ b/api_docs/navigation.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/navigation title: "navigation" image: https://source.unsplash.com/400x175/?github description: API docs for the navigation plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'navigation'] --- import navigationObj from './navigation.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sh | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 35 | 0 | 35 | 2 | +| 42 | 0 | 42 | 3 | ## Client diff --git a/api_docs/newsfeed.mdx b/api_docs/newsfeed.mdx index 8b0b68d06fde6..5a97365c2f929 100644 --- a/api_docs/newsfeed.mdx +++ b/api_docs/newsfeed.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/newsfeed title: "newsfeed" image: https://source.unsplash.com/400x175/?github description: API docs for the newsfeed plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'newsfeed'] --- import newsfeedObj from './newsfeed.devdocs.json'; diff --git a/api_docs/no_data_page.devdocs.json b/api_docs/no_data_page.devdocs.json index 0bbeb2e20defc..6820a673cb7a9 100644 --- a/api_docs/no_data_page.devdocs.json +++ b/api_docs/no_data_page.devdocs.json @@ -7,26 +7,26 @@ "enums": [], "misc": [], "objects": [], - "setup": { + "start": { "parentPluginId": "noDataPage", - "id": "def-public.NoDataPagePluginStart", + "id": "def-public.NoDataPagePublicStart", "type": "Type", "tags": [], - "label": "NoDataPagePluginStart", + "label": "NoDataPagePublicStart", "description": [], "signature": [ { "pluginId": "noDataPage", "scope": "public", "docId": "kibNoDataPagePluginApi", - "section": "def-public.NoDataPagePluginSetup", - "text": "NoDataPagePluginSetup" + "section": "def-public.NoDataPagePublicSetup", + "text": "NoDataPagePublicSetup" } ], "path": "src/plugins/no_data_page/public/types.ts", "deprecated": false, "trackAdoption": false, - "lifecycle": "setup", + "lifecycle": "start", "initialIsOpen": true } }, diff --git a/api_docs/no_data_page.mdx b/api_docs/no_data_page.mdx index d01c14a6ad234..d157b958d0b5e 100644 --- a/api_docs/no_data_page.mdx +++ b/api_docs/no_data_page.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/noDataPage title: "noDataPage" image: https://source.unsplash.com/400x175/?github description: API docs for the noDataPage plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'noDataPage'] --- import noDataPageObj from './no_data_page.devdocs.json'; @@ -25,6 +25,6 @@ Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sh ## Client -### Setup - +### Start + diff --git a/api_docs/notifications.devdocs.json b/api_docs/notifications.devdocs.json index f7508b16dcdba..0e43337827072 100644 --- a/api_docs/notifications.devdocs.json +++ b/api_docs/notifications.devdocs.json @@ -17,10 +17,10 @@ "objects": [], "start": { "parentPluginId": "notifications", - "id": "def-server.NotificationsPluginStart", + "id": "def-server.NotificationsServerStart", "type": "Type", "tags": [], - "label": "NotificationsPluginStart", + "label": "NotificationsServerStart", "description": [], "signature": [ "EmailServiceStart" diff --git a/api_docs/notifications.mdx b/api_docs/notifications.mdx index 16b4f60589a3a..385dacb9c00bf 100644 --- a/api_docs/notifications.mdx +++ b/api_docs/notifications.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/notifications title: "notifications" image: https://source.unsplash.com/400x175/?github description: API docs for the notifications plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'notifications'] --- import notificationsObj from './notifications.devdocs.json'; diff --git a/api_docs/observability.devdocs.json b/api_docs/observability.devdocs.json index acc287a66644b..10fc931bda74a 100644 --- a/api_docs/observability.devdocs.json +++ b/api_docs/observability.devdocs.json @@ -2442,37 +2442,7 @@ "label": "observabilityShared", "description": [], "signature": [ - "{ locators: { profiling: { flamegraphLocator: ", - { - "pluginId": "share", - "scope": "common", - "docId": "kibSharePluginApi", - "section": "def-common.LocatorPublic", - "text": "LocatorPublic" - }, - "<", - "FlamegraphLocatorParams", - ">; topNFunctionsLocator: ", - { - "pluginId": "share", - "scope": "common", - "docId": "kibSharePluginApi", - "section": "def-common.LocatorPublic", - "text": "LocatorPublic" - }, - "<", - "TopNFunctionsLocatorParams", - ">; stacktracesLocator: ", - { - "pluginId": "share", - "scope": "common", - "docId": "kibSharePluginApi", - "section": "def-common.LocatorPublic", - "text": "LocatorPublic" - }, - "<", - "StacktracesLocatorParams", - ">; }; }; navigation: { registerSections: (sections$: ", + "{ locators: ObservabilitySharedLocators; navigation: { registerSections: (sections$: ", "Observable", "<", { @@ -2702,6 +2672,27 @@ "path": "x-pack/plugins/observability/public/plugin.ts", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "observability", + "id": "def-public.ObservabilityPublicPluginsSetup.serverless", + "type": "Object", + "tags": [], + "label": "serverless", + "description": [], + "signature": [ + { + "pluginId": "serverless", + "scope": "public", + "docId": "kibServerlessPluginApi", + "section": "def-public.ServerlessPluginSetup", + "text": "ServerlessPluginSetup" + }, + " | undefined" + ], + "path": "x-pack/plugins/observability/public/plugin.ts", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -3029,7 +3020,7 @@ "label": "observabilityShared", "description": [], "signature": [ - "{ navigation: { PageTemplate: (pageTemplateProps: ", + "{ locators: ObservabilitySharedLocators; navigation: { PageTemplate: (pageTemplateProps: ", "WrappedPageTemplateProps", ") => JSX.Element; registerSections: (sections$: ", "Observable", @@ -8242,7 +8233,29 @@ "label": "ObservabilityAPIReturnType", "description": [], "signature": [ - "{ \"GET /internal/observability/slos/{id}/_instances\": { endpoint: \"GET /internal/observability/slos/{id}/_instances\"; params?: ", + "{ \"POST /api/observability/slos/{id}/_reset 2023-10-31\": { endpoint: \"POST /api/observability/slos/{id}/_reset 2023-10-31\"; params?: ", + "TypeC", + "<{ path: ", + "TypeC", + "<{ id: ", + "StringC", + "; }>; }> | undefined; handler: ({}: ", + { + "pluginId": "observability", + "scope": "server", + "docId": "kibObservabilityPluginApi", + "section": "def-server.ObservabilityRouteHandlerResources", + "text": "ObservabilityRouteHandlerResources" + }, + " & { params: { path: { id: string; }; }; }) => Promise<{ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string; total: string; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"cardinality\" | \"last_value\" | \"std_deviation\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); } & { filter?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { syncDelay: string; frequency: string; }; enabled: boolean; tags: string[]; groupBy: string; createdAt: string; updatedAt: string; version: number; } & { instanceId?: string | undefined; }>; } & ", + { + "pluginId": "observability", + "scope": "server", + "docId": "kibObservabilityPluginApi", + "section": "def-server.ObservabilityRouteCreateOptions", + "text": "ObservabilityRouteCreateOptions" + }, + "; \"GET /internal/observability/slos/{id}/_instances\": { endpoint: \"GET /internal/observability/slos/{id}/_instances\"; params?: ", "TypeC", "<{ path: ", "TypeC", @@ -9174,7 +9187,7 @@ "section": "def-common.Duration", "text": "Duration" }, - " | undefined; } | undefined; tags?: string[] | undefined; groupBy?: string | undefined; }; }; }) => Promise<{ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string; total: string; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"cardinality\" | \"last_value\" | \"std_deviation\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); } & { filter?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { syncDelay: string; frequency: string; }; enabled: boolean; tags: string[]; groupBy: string; createdAt: string; updatedAt: string; } & { instanceId?: string | undefined; }>; } & ", + " | undefined; } | undefined; tags?: string[] | undefined; groupBy?: string | undefined; }; }; }) => Promise<{ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string; total: string; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"cardinality\" | \"last_value\" | \"std_deviation\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); } & { filter?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { syncDelay: string; frequency: string; }; enabled: boolean; tags: string[]; groupBy: string; createdAt: string; updatedAt: string; version: number; } & { instanceId?: string | undefined; }>; } & ", { "pluginId": "observability", "scope": "server", @@ -9208,7 +9221,7 @@ "section": "def-server.ObservabilityRouteHandlerResources", "text": "ObservabilityRouteHandlerResources" }, - " & { params: { path: { id: string; }; } & { query?: { instanceId?: string | undefined; } | undefined; }; }) => Promise<{ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string; total: string; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"cardinality\" | \"last_value\" | \"std_deviation\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); } & { filter?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { syncDelay: string; frequency: string; }; enabled: boolean; tags: string[]; groupBy: string; createdAt: string; updatedAt: string; } & { instanceId?: string | undefined; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; }>; } & ", + " & { params: { path: { id: string; }; } & { query?: { instanceId?: string | undefined; } | undefined; }; }) => Promise<{ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string; total: string; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"cardinality\" | \"last_value\" | \"std_deviation\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); } & { filter?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { syncDelay: string; frequency: string; }; enabled: boolean; tags: string[]; groupBy: string; createdAt: string; updatedAt: string; version: number; } & { instanceId?: string | undefined; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; }>; } & ", { "pluginId": "observability", "scope": "server", @@ -9250,7 +9263,7 @@ "section": "def-server.ObservabilityRouteHandlerResources", "text": "ObservabilityRouteHandlerResources" }, - " & { params?: { query?: { kqlQuery?: string | undefined; page?: string | undefined; perPage?: string | undefined; sortBy?: \"status\" | \"error_budget_consumed\" | \"error_budget_remaining\" | \"sli_value\" | undefined; sortDirection?: \"asc\" | \"desc\" | undefined; } | undefined; } | undefined; }) => Promise<{ page: number; perPage: number; total: number; results: ({ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string; total: string; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"cardinality\" | \"last_value\" | \"std_deviation\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); } & { filter?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { syncDelay: string; frequency: string; }; enabled: boolean; tags: string[]; groupBy: string; createdAt: string; updatedAt: string; } & { instanceId?: string | undefined; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; })[]; }>; } & ", + " & { params?: { query?: { kqlQuery?: string | undefined; page?: string | undefined; perPage?: string | undefined; sortBy?: \"status\" | \"error_budget_consumed\" | \"error_budget_remaining\" | \"sli_value\" | undefined; sortDirection?: \"asc\" | \"desc\" | undefined; } | undefined; } | undefined; }) => Promise<{ page: number; perPage: number; total: number; results: ({ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string; total: string; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"cardinality\" | \"last_value\" | \"std_deviation\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); } & { filter?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { syncDelay: string; frequency: string; }; enabled: boolean; tags: string[]; groupBy: string; createdAt: string; updatedAt: string; version: number; } & { instanceId?: string | undefined; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; })[]; }>; } & ", { "pluginId": "observability", "scope": "server", @@ -9258,12 +9271,18 @@ "section": "def-server.ObservabilityRouteCreateOptions", "text": "ObservabilityRouteCreateOptions" }, - "; \"GET /internal/observability/slos/_definitions\": { endpoint: \"GET /internal/observability/slos/_definitions\"; params?: ", - "TypeC", + "; \"GET /api/observability/slos/_definitions 2023-10-31\": { endpoint: \"GET /api/observability/slos/_definitions 2023-10-31\"; params?: ", + "PartialC", "<{ query: ", - "TypeC", + "PartialC", "<{ search: ", "StringC", + "; includeOutdatedOnly: ", + "Type", + "; page: ", + "StringC", + "; perPage: ", + "StringC", "; }>; }> | undefined; handler: ({}: ", { "pluginId": "observability", @@ -9272,7 +9291,7 @@ "section": "def-server.ObservabilityRouteHandlerResources", "text": "ObservabilityRouteHandlerResources" }, - " & { params: { query: { search: string; }; }; }) => Promise<({ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string; total: string; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"cardinality\" | \"last_value\" | \"std_deviation\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); } & { filter?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { syncDelay: string; frequency: string; }; enabled: boolean; tags: string[]; groupBy: string; createdAt: string; updatedAt: string; } & { instanceId?: string | undefined; })[]>; } & ", + " & { params?: { query?: { search?: string | undefined; includeOutdatedOnly?: boolean | undefined; page?: string | undefined; perPage?: string | undefined; } | undefined; } | undefined; }) => Promise<{ page: number; perPage: number; total: number; results: ({ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string; total: string; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"cardinality\" | \"last_value\" | \"std_deviation\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); } & { filter?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { syncDelay: string; frequency: string; }; enabled: boolean; tags: string[]; groupBy: string; createdAt: string; updatedAt: string; version: number; } & { instanceId?: string | undefined; })[]; }>; } & ", { "pluginId": "observability", "scope": "server", @@ -9952,7 +9971,29 @@ "label": "ObservabilityServerRouteRepository", "description": [], "signature": [ - "{ \"GET /internal/observability/slos/{id}/_instances\": { endpoint: \"GET /internal/observability/slos/{id}/_instances\"; params?: ", + "{ \"POST /api/observability/slos/{id}/_reset 2023-10-31\": { endpoint: \"POST /api/observability/slos/{id}/_reset 2023-10-31\"; params?: ", + "TypeC", + "<{ path: ", + "TypeC", + "<{ id: ", + "StringC", + "; }>; }> | undefined; handler: ({}: ", + { + "pluginId": "observability", + "scope": "server", + "docId": "kibObservabilityPluginApi", + "section": "def-server.ObservabilityRouteHandlerResources", + "text": "ObservabilityRouteHandlerResources" + }, + " & { params: { path: { id: string; }; }; }) => Promise<{ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string; total: string; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"cardinality\" | \"last_value\" | \"std_deviation\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); } & { filter?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { syncDelay: string; frequency: string; }; enabled: boolean; tags: string[]; groupBy: string; createdAt: string; updatedAt: string; version: number; } & { instanceId?: string | undefined; }>; } & ", + { + "pluginId": "observability", + "scope": "server", + "docId": "kibObservabilityPluginApi", + "section": "def-server.ObservabilityRouteCreateOptions", + "text": "ObservabilityRouteCreateOptions" + }, + "; \"GET /internal/observability/slos/{id}/_instances\": { endpoint: \"GET /internal/observability/slos/{id}/_instances\"; params?: ", "TypeC", "<{ path: ", "TypeC", @@ -10884,7 +10925,7 @@ "section": "def-common.Duration", "text": "Duration" }, - " | undefined; } | undefined; tags?: string[] | undefined; groupBy?: string | undefined; }; }; }) => Promise<{ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string; total: string; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"cardinality\" | \"last_value\" | \"std_deviation\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); } & { filter?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { syncDelay: string; frequency: string; }; enabled: boolean; tags: string[]; groupBy: string; createdAt: string; updatedAt: string; } & { instanceId?: string | undefined; }>; } & ", + " | undefined; } | undefined; tags?: string[] | undefined; groupBy?: string | undefined; }; }; }) => Promise<{ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string; total: string; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"cardinality\" | \"last_value\" | \"std_deviation\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); } & { filter?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { syncDelay: string; frequency: string; }; enabled: boolean; tags: string[]; groupBy: string; createdAt: string; updatedAt: string; version: number; } & { instanceId?: string | undefined; }>; } & ", { "pluginId": "observability", "scope": "server", @@ -10918,7 +10959,7 @@ "section": "def-server.ObservabilityRouteHandlerResources", "text": "ObservabilityRouteHandlerResources" }, - " & { params: { path: { id: string; }; } & { query?: { instanceId?: string | undefined; } | undefined; }; }) => Promise<{ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string; total: string; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"cardinality\" | \"last_value\" | \"std_deviation\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); } & { filter?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { syncDelay: string; frequency: string; }; enabled: boolean; tags: string[]; groupBy: string; createdAt: string; updatedAt: string; } & { instanceId?: string | undefined; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; }>; } & ", + " & { params: { path: { id: string; }; } & { query?: { instanceId?: string | undefined; } | undefined; }; }) => Promise<{ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string; total: string; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"cardinality\" | \"last_value\" | \"std_deviation\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); } & { filter?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { syncDelay: string; frequency: string; }; enabled: boolean; tags: string[]; groupBy: string; createdAt: string; updatedAt: string; version: number; } & { instanceId?: string | undefined; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; }>; } & ", { "pluginId": "observability", "scope": "server", @@ -10960,7 +11001,7 @@ "section": "def-server.ObservabilityRouteHandlerResources", "text": "ObservabilityRouteHandlerResources" }, - " & { params?: { query?: { kqlQuery?: string | undefined; page?: string | undefined; perPage?: string | undefined; sortBy?: \"status\" | \"error_budget_consumed\" | \"error_budget_remaining\" | \"sli_value\" | undefined; sortDirection?: \"asc\" | \"desc\" | undefined; } | undefined; } | undefined; }) => Promise<{ page: number; perPage: number; total: number; results: ({ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string; total: string; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"cardinality\" | \"last_value\" | \"std_deviation\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); } & { filter?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { syncDelay: string; frequency: string; }; enabled: boolean; tags: string[]; groupBy: string; createdAt: string; updatedAt: string; } & { instanceId?: string | undefined; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; })[]; }>; } & ", + " & { params?: { query?: { kqlQuery?: string | undefined; page?: string | undefined; perPage?: string | undefined; sortBy?: \"status\" | \"error_budget_consumed\" | \"error_budget_remaining\" | \"sli_value\" | undefined; sortDirection?: \"asc\" | \"desc\" | undefined; } | undefined; } | undefined; }) => Promise<{ page: number; perPage: number; total: number; results: ({ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string; total: string; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"cardinality\" | \"last_value\" | \"std_deviation\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); } & { filter?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { syncDelay: string; frequency: string; }; enabled: boolean; tags: string[]; groupBy: string; createdAt: string; updatedAt: string; version: number; } & { instanceId?: string | undefined; } & { summary: { status: \"HEALTHY\" | \"NO_DATA\" | \"DEGRADING\" | \"VIOLATED\"; sliValue: number; errorBudget: { initial: number; consumed: number; remaining: number; isEstimated: boolean; }; }; })[]; }>; } & ", { "pluginId": "observability", "scope": "server", @@ -10968,12 +11009,18 @@ "section": "def-server.ObservabilityRouteCreateOptions", "text": "ObservabilityRouteCreateOptions" }, - "; \"GET /internal/observability/slos/_definitions\": { endpoint: \"GET /internal/observability/slos/_definitions\"; params?: ", - "TypeC", + "; \"GET /api/observability/slos/_definitions 2023-10-31\": { endpoint: \"GET /api/observability/slos/_definitions 2023-10-31\"; params?: ", + "PartialC", "<{ query: ", - "TypeC", + "PartialC", "<{ search: ", "StringC", + "; includeOutdatedOnly: ", + "Type", + "; page: ", + "StringC", + "; perPage: ", + "StringC", "; }>; }> | undefined; handler: ({}: ", { "pluginId": "observability", @@ -10982,7 +11029,7 @@ "section": "def-server.ObservabilityRouteHandlerResources", "text": "ObservabilityRouteHandlerResources" }, - " & { params: { query: { search: string; }; }; }) => Promise<({ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string; total: string; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"cardinality\" | \"last_value\" | \"std_deviation\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); } & { filter?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { syncDelay: string; frequency: string; }; enabled: boolean; tags: string[]; groupBy: string; createdAt: string; updatedAt: string; } & { instanceId?: string | undefined; })[]>; } & ", + " & { params?: { query?: { search?: string | undefined; includeOutdatedOnly?: boolean | undefined; page?: string | undefined; perPage?: string | undefined; } | undefined; } | undefined; }) => Promise<{ page: number; perPage: number; total: number; results: ({ id: string; name: string; description: string; indicator: { type: \"sli.apm.transactionDuration\"; params: { environment: string; service: string; transactionType: string; transactionName: string; threshold: number; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.apm.transactionErrorRate\"; params: { environment: string; service: string; transactionType: string; transactionName: string; index: string; } & { filter?: string | undefined; }; } | { type: \"sli.kql.custom\"; params: { index: string; good: string; total: string; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.custom\"; params: { index: string; good: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; total: { metrics: (({ name: string; aggregation: \"sum\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }))[]; equation: string; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.metric.timeslice\"; params: { index: string; metric: { metrics: (({ name: string; aggregation: \"min\" | \"max\" | \"sum\" | \"avg\" | \"cardinality\" | \"last_value\" | \"std_deviation\"; field: string; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"doc_count\"; } & { filter?: string | undefined; }) | ({ name: string; aggregation: \"percentile\"; field: string; percentile: number; } & { filter?: string | undefined; }))[]; equation: string; threshold: number; comparator: \"GT\" | \"GTE\" | \"LT\" | \"LTE\"; }; timestampField: string; } & { filter?: string | undefined; }; } | { type: \"sli.histogram.custom\"; params: { index: string; timestampField: string; good: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); total: ({ field: string; aggregation: \"value_count\"; } & { filter?: string | undefined; }) | ({ field: string; aggregation: \"range\"; from: number; to: number; } & { filter?: string | undefined; }); } & { filter?: string | undefined; }; }; timeWindow: { duration: string; type: \"rolling\"; } | { duration: string; type: \"calendarAligned\"; }; budgetingMethod: \"occurrences\" | \"timeslices\"; objective: { target: number; } & { timesliceTarget?: number | undefined; timesliceWindow?: string | undefined; }; revision: number; settings: { syncDelay: string; frequency: string; }; enabled: boolean; tags: string[]; groupBy: string; createdAt: string; updatedAt: string; version: number; } & { instanceId?: string | undefined; })[]; }>; } & ", { "pluginId": "observability", "scope": "server", @@ -16261,7 +16308,7 @@ "label": "observabilityPaths", "description": [], "signature": [ - "{ alerts: string; alertDetails: (alertId: string) => string; rules: string; ruleDetails: (ruleId: string) => string; slos: string; slosWelcome: string; sloCreate: string; sloCreateWithEncodedForm: (encodedParams: string) => string; sloEdit: (sloId: string) => string; sloEditWithEncodedForm: (sloId: string, encodedParams: string) => string; sloDetails: (sloId: string, instanceId?: string | undefined) => string; }" + "{ alerts: string; alertDetails: (alertId: string) => string; rules: string; ruleDetails: (ruleId: string) => string; slos: string; slosWelcome: string; slosOutdatedDefinitions: string; sloCreate: string; sloCreateWithEncodedForm: (encodedParams: string) => string; sloEdit: (sloId: string) => string; sloEditWithEncodedForm: (sloId: string, encodedParams: string) => string; sloDetails: (sloId: string, instanceId?: string | undefined) => string; }" ], "path": "x-pack/plugins/observability/common/index.ts", "deprecated": false, diff --git a/api_docs/observability.mdx b/api_docs/observability.mdx index 89166e8475e7a..d78e87accec3f 100644 --- a/api_docs/observability.mdx +++ b/api_docs/observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observability title: "observability" image: https://source.unsplash.com/400x175/?github description: API docs for the observability plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observability'] --- import observabilityObj from './observability.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/ | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 599 | 2 | 590 | 17 | +| 600 | 2 | 591 | 17 | ## Client diff --git a/api_docs/observability_a_i_assistant.devdocs.json b/api_docs/observability_a_i_assistant.devdocs.json index 11e7584727bc5..e8ddc39eb1e86 100644 --- a/api_docs/observability_a_i_assistant.devdocs.json +++ b/api_docs/observability_a_i_assistant.devdocs.json @@ -540,7 +540,7 @@ "label": "callApi", "description": [], "signature": [ - "(endpoint: TEndpoint, ...args: MaybeOptionalArgs<", + "(endpoint: TEndpoint, ...args: MaybeOptionalArgs<", { "pluginId": "@kbn/server-route-repository", "scope": "common", @@ -684,54 +684,16 @@ "ObservabilityAIAssistantRouteCreateOptions", "; \"GET /internal/observability_ai_assistant/kb/status\": { endpoint: \"GET /internal/observability_ai_assistant/kb/status\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", - ") => Promise<{ ready: boolean; error?: any; deployment_state?: string | undefined; allocation_state?: string | undefined; }>; } & ", + ") => Promise<{ ready: boolean; error?: any; deployment_state?: ", + "MlDeploymentState", + " | undefined; allocation_state?: ", + "MlDeploymentAllocationState", + " | undefined; model_name?: string | undefined; }>; } & ", "ObservabilityAIAssistantRouteCreateOptions", "; \"POST /internal/observability_ai_assistant/kb/setup\": { endpoint: \"POST /internal/observability_ai_assistant/kb/setup\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", ") => Promise<{}>; } & ", "ObservabilityAIAssistantRouteCreateOptions", - "; \"POST /internal/observability_ai_assistant/functions/get_dataset_info\": { endpoint: \"POST /internal/observability_ai_assistant/functions/get_dataset_info\"; params?: ", - "TypeC", - "<{ body: ", - "TypeC", - "<{ index: ", - "StringC", - "; }>; }> | undefined; handler: ({}: ", - "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { body: { index: string; }; }; }) => Promise<{ indices: string[]; fields: { name: string; description: string; type: string; }[]; }>; } & ", - "ObservabilityAIAssistantRouteCreateOptions", - "; \"POST /internal/observability_ai_assistant/functions/alerts\": { endpoint: \"POST /internal/observability_ai_assistant/functions/alerts\"; params?: ", - "TypeC", - "<{ body: ", - "IntersectionC", - "<[", - "TypeC", - "<{ featureIds: ", - "ArrayC", - "<", - "StringC", - ">; start: ", - "StringC", - "; end: ", - "StringC", - "; }>, ", - "PartialC", - "<{ filter: ", - "StringC", - "; includeRecovered: ", - "Type", - "; }>]>; }> | undefined; handler: ({}: ", - "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { body: { featureIds: string[]; start: string; end: string; } & { filter?: string | undefined; includeRecovered?: boolean | undefined; }; }; }) => Promise<{ content: { total: number; alerts: OutputOf>[]; }; }>; } & ", - "ObservabilityAIAssistantRouteCreateOptions", "; \"POST /internal/observability_ai_assistant/functions/summarize\": { endpoint: \"POST /internal/observability_ai_assistant/functions/summarize\"; params?: ", "TypeC", "<{ body: ", @@ -824,33 +786,15 @@ "RecalledEntry", "[]; }>; } & ", "ObservabilityAIAssistantRouteCreateOptions", - "; \"POST /internal/observability_ai_assistant/functions/elasticsearch\": { endpoint: \"POST /internal/observability_ai_assistant/functions/elasticsearch\"; params?: ", - "TypeC", - "<{ body: ", - "IntersectionC", - "<[", - "TypeC", - "<{ method: ", - "UnionC", - "<[", - "LiteralC", - "<\"GET\">, ", - "LiteralC", - "<\"POST\">, ", - "LiteralC", - "<\"PATCH\">, ", - "LiteralC", - "<\"PUT\">, ", - "LiteralC", - "<\"DELETE\">]>; path: ", - "StringC", - "; }>, ", - "PartialC", - "<{ body: ", - "AnyC", - "; }>]>; }> | undefined; handler: ({}: ", + "; \"GET /internal/observability_ai_assistant/functions\": { endpoint: \"GET /internal/observability_ai_assistant/functions\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { body: { method: \"GET\" | \"DELETE\" | \"POST\" | \"PUT\" | \"PATCH\"; path: string; } & { body?: any; }; }; }) => Promise; } & ", + ") => Promise<{ functionDefinitions: ", + "FunctionDefinition", + "<", + "CompatibleJSONSchema", + ">[]; contextDefinitions: ", + "ContextDefinition", + "[]; }>; } & ", "ObservabilityAIAssistantRouteCreateOptions", "; \"GET /internal/observability_ai_assistant/connectors\": { endpoint: \"GET /internal/observability_ai_assistant/connectors\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", @@ -896,28 +840,6 @@ }, ">; } & ", "ObservabilityAIAssistantRouteCreateOptions", - "; \"PUT /internal/observability_ai_assistant/conversation/{conversationId}/auto_title\": { endpoint: \"PUT /internal/observability_ai_assistant/conversation/{conversationId}/auto_title\"; params?: ", - "TypeC", - "<{ path: ", - "TypeC", - "<{ conversationId: ", - "StringC", - "; }>; body: ", - "TypeC", - "<{ connectorId: ", - "StringC", - "; }>; }> | undefined; handler: ({}: ", - "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { path: { conversationId: string; }; body: { connectorId: string; }; }; }) => Promise<", - { - "pluginId": "observabilityAIAssistant", - "scope": "common", - "docId": "kibObservabilityAIAssistantPluginApi", - "section": "def-common.Conversation", - "text": "Conversation" - }, - ">; } & ", - "ObservabilityAIAssistantRouteCreateOptions", "; \"PUT /internal/observability_ai_assistant/conversation/{conversationId}\": { endpoint: \"PUT /internal/observability_ai_assistant/conversation/{conversationId}\"; params?: ", "TypeC", "<{ path: ", @@ -1006,6 +928,58 @@ }, ">; } & ", "ObservabilityAIAssistantRouteCreateOptions", + "; \"POST /internal/observability_ai_assistant/chat/complete\": { endpoint: \"POST /internal/observability_ai_assistant/chat/complete\"; params?: ", + "TypeC", + "<{ body: ", + "IntersectionC", + "<[", + "TypeC", + "<{ messages: ", + "ArrayC", + "<", + "Type", + "<", + { + "pluginId": "observabilityAIAssistant", + "scope": "common", + "docId": "kibObservabilityAIAssistantPluginApi", + "section": "def-common.Message", + "text": "Message" + }, + ", ", + { + "pluginId": "observabilityAIAssistant", + "scope": "common", + "docId": "kibObservabilityAIAssistantPluginApi", + "section": "def-common.Message", + "text": "Message" + }, + ", unknown>>; connectorId: ", + "StringC", + "; persist: ", + "Type", + "; }>, ", + "PartialC", + "<{ conversationId: ", + "StringC", + "; title: ", + "StringC", + "; }>]>; }> | undefined; handler: ({}: ", + "ObservabilityAIAssistantRouteHandlerResources", + " & { params: { body: { messages: ", + { + "pluginId": "observabilityAIAssistant", + "scope": "common", + "docId": "kibObservabilityAIAssistantPluginApi", + "section": "def-common.Message", + "text": "Message" + }, + "[]; connectorId: string; persist: boolean; } & { conversationId?: string | undefined; title?: string | undefined; }; }; }) => Promise<", + "Readable", + " | ", + "CreateChatCompletionResponse", + ">; } & ", + "ObservabilityAIAssistantRouteCreateOptions", "; \"POST /internal/observability_ai_assistant/chat\": { endpoint: \"POST /internal/observability_ai_assistant/chat\"; params?: ", "IntersectionC", "<[", @@ -1216,54 +1190,16 @@ "ObservabilityAIAssistantRouteCreateOptions", "; \"GET /internal/observability_ai_assistant/kb/status\": { endpoint: \"GET /internal/observability_ai_assistant/kb/status\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", - ") => Promise<{ ready: boolean; error?: any; deployment_state?: string | undefined; allocation_state?: string | undefined; }>; } & ", + ") => Promise<{ ready: boolean; error?: any; deployment_state?: ", + "MlDeploymentState", + " | undefined; allocation_state?: ", + "MlDeploymentAllocationState", + " | undefined; model_name?: string | undefined; }>; } & ", "ObservabilityAIAssistantRouteCreateOptions", "; \"POST /internal/observability_ai_assistant/kb/setup\": { endpoint: \"POST /internal/observability_ai_assistant/kb/setup\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", ") => Promise<{}>; } & ", "ObservabilityAIAssistantRouteCreateOptions", - "; \"POST /internal/observability_ai_assistant/functions/get_dataset_info\": { endpoint: \"POST /internal/observability_ai_assistant/functions/get_dataset_info\"; params?: ", - "TypeC", - "<{ body: ", - "TypeC", - "<{ index: ", - "StringC", - "; }>; }> | undefined; handler: ({}: ", - "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { body: { index: string; }; }; }) => Promise<{ indices: string[]; fields: { name: string; description: string; type: string; }[]; }>; } & ", - "ObservabilityAIAssistantRouteCreateOptions", - "; \"POST /internal/observability_ai_assistant/functions/alerts\": { endpoint: \"POST /internal/observability_ai_assistant/functions/alerts\"; params?: ", - "TypeC", - "<{ body: ", - "IntersectionC", - "<[", - "TypeC", - "<{ featureIds: ", - "ArrayC", - "<", - "StringC", - ">; start: ", - "StringC", - "; end: ", - "StringC", - "; }>, ", - "PartialC", - "<{ filter: ", - "StringC", - "; includeRecovered: ", - "Type", - "; }>]>; }> | undefined; handler: ({}: ", - "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { body: { featureIds: string[]; start: string; end: string; } & { filter?: string | undefined; includeRecovered?: boolean | undefined; }; }; }) => Promise<{ content: { total: number; alerts: OutputOf>[]; }; }>; } & ", - "ObservabilityAIAssistantRouteCreateOptions", "; \"POST /internal/observability_ai_assistant/functions/summarize\": { endpoint: \"POST /internal/observability_ai_assistant/functions/summarize\"; params?: ", "TypeC", "<{ body: ", @@ -1356,33 +1292,15 @@ "RecalledEntry", "[]; }>; } & ", "ObservabilityAIAssistantRouteCreateOptions", - "; \"POST /internal/observability_ai_assistant/functions/elasticsearch\": { endpoint: \"POST /internal/observability_ai_assistant/functions/elasticsearch\"; params?: ", - "TypeC", - "<{ body: ", - "IntersectionC", - "<[", - "TypeC", - "<{ method: ", - "UnionC", - "<[", - "LiteralC", - "<\"GET\">, ", - "LiteralC", - "<\"POST\">, ", - "LiteralC", - "<\"PATCH\">, ", - "LiteralC", - "<\"PUT\">, ", - "LiteralC", - "<\"DELETE\">]>; path: ", - "StringC", - "; }>, ", - "PartialC", - "<{ body: ", - "AnyC", - "; }>]>; }> | undefined; handler: ({}: ", + "; \"GET /internal/observability_ai_assistant/functions\": { endpoint: \"GET /internal/observability_ai_assistant/functions\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { body: { method: \"GET\" | \"DELETE\" | \"POST\" | \"PUT\" | \"PATCH\"; path: string; } & { body?: any; }; }; }) => Promise; } & ", + ") => Promise<{ functionDefinitions: ", + "FunctionDefinition", + "<", + "CompatibleJSONSchema", + ">[]; contextDefinitions: ", + "ContextDefinition", + "[]; }>; } & ", "ObservabilityAIAssistantRouteCreateOptions", "; \"GET /internal/observability_ai_assistant/connectors\": { endpoint: \"GET /internal/observability_ai_assistant/connectors\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", @@ -1428,28 +1346,6 @@ }, ">; } & ", "ObservabilityAIAssistantRouteCreateOptions", - "; \"PUT /internal/observability_ai_assistant/conversation/{conversationId}/auto_title\": { endpoint: \"PUT /internal/observability_ai_assistant/conversation/{conversationId}/auto_title\"; params?: ", - "TypeC", - "<{ path: ", - "TypeC", - "<{ conversationId: ", - "StringC", - "; }>; body: ", - "TypeC", - "<{ connectorId: ", - "StringC", - "; }>; }> | undefined; handler: ({}: ", - "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { path: { conversationId: string; }; body: { connectorId: string; }; }; }) => Promise<", - { - "pluginId": "observabilityAIAssistant", - "scope": "common", - "docId": "kibObservabilityAIAssistantPluginApi", - "section": "def-common.Conversation", - "text": "Conversation" - }, - ">; } & ", - "ObservabilityAIAssistantRouteCreateOptions", "; \"PUT /internal/observability_ai_assistant/conversation/{conversationId}\": { endpoint: \"PUT /internal/observability_ai_assistant/conversation/{conversationId}\"; params?: ", "TypeC", "<{ path: ", @@ -1538,6 +1434,58 @@ }, ">; } & ", "ObservabilityAIAssistantRouteCreateOptions", + "; \"POST /internal/observability_ai_assistant/chat/complete\": { endpoint: \"POST /internal/observability_ai_assistant/chat/complete\"; params?: ", + "TypeC", + "<{ body: ", + "IntersectionC", + "<[", + "TypeC", + "<{ messages: ", + "ArrayC", + "<", + "Type", + "<", + { + "pluginId": "observabilityAIAssistant", + "scope": "common", + "docId": "kibObservabilityAIAssistantPluginApi", + "section": "def-common.Message", + "text": "Message" + }, + ", ", + { + "pluginId": "observabilityAIAssistant", + "scope": "common", + "docId": "kibObservabilityAIAssistantPluginApi", + "section": "def-common.Message", + "text": "Message" + }, + ", unknown>>; connectorId: ", + "StringC", + "; persist: ", + "Type", + "; }>, ", + "PartialC", + "<{ conversationId: ", + "StringC", + "; title: ", + "StringC", + "; }>]>; }> | undefined; handler: ({}: ", + "ObservabilityAIAssistantRouteHandlerResources", + " & { params: { body: { messages: ", + { + "pluginId": "observabilityAIAssistant", + "scope": "common", + "docId": "kibObservabilityAIAssistantPluginApi", + "section": "def-common.Message", + "text": "Message" + }, + "[]; connectorId: string; persist: boolean; } & { conversationId?: string | undefined; title?: string | undefined; }; }; }) => Promise<", + "Readable", + " | ", + "CreateChatCompletionResponse", + ">; } & ", + "ObservabilityAIAssistantRouteCreateOptions", "; \"POST /internal/observability_ai_assistant/chat\": { endpoint: \"POST /internal/observability_ai_assistant/chat\"; params?: ", "IntersectionC", "<[", @@ -1723,8 +1671,8 @@ "pluginId": "share", "scope": "public", "docId": "kibSharePluginApi", - "section": "def-public.SharePluginStart", - "text": "SharePluginStart" + "section": "def-public.SharePublicStart", + "text": "SharePublicStart" } ], "path": "x-pack/plugins/observability_ai_assistant/public/types.ts", @@ -1788,7 +1736,7 @@ "description": [], "signature": [ "(fn: ", - "AssistantRegistrationFunction", + "ChatRegistrationRenderFunction", ") => void" ], "path": "x-pack/plugins/observability_ai_assistant/public/types.ts", @@ -1803,7 +1751,7 @@ "label": "fn", "description": [], "signature": [ - "AssistantRegistrationFunction" + "ChatRegistrationRenderFunction" ], "path": "x-pack/plugins/observability_ai_assistant/public/types.ts", "deprecated": false, @@ -1988,54 +1936,16 @@ "ObservabilityAIAssistantRouteCreateOptions", "; \"GET /internal/observability_ai_assistant/kb/status\": { endpoint: \"GET /internal/observability_ai_assistant/kb/status\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", - ") => Promise<{ ready: boolean; error?: any; deployment_state?: string | undefined; allocation_state?: string | undefined; }>; } & ", + ") => Promise<{ ready: boolean; error?: any; deployment_state?: ", + "MlDeploymentState", + " | undefined; allocation_state?: ", + "MlDeploymentAllocationState", + " | undefined; model_name?: string | undefined; }>; } & ", "ObservabilityAIAssistantRouteCreateOptions", "; \"POST /internal/observability_ai_assistant/kb/setup\": { endpoint: \"POST /internal/observability_ai_assistant/kb/setup\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", ") => Promise<{}>; } & ", "ObservabilityAIAssistantRouteCreateOptions", - "; \"POST /internal/observability_ai_assistant/functions/get_dataset_info\": { endpoint: \"POST /internal/observability_ai_assistant/functions/get_dataset_info\"; params?: ", - "TypeC", - "<{ body: ", - "TypeC", - "<{ index: ", - "StringC", - "; }>; }> | undefined; handler: ({}: ", - "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { body: { index: string; }; }; }) => Promise<{ indices: string[]; fields: { name: string; description: string; type: string; }[]; }>; } & ", - "ObservabilityAIAssistantRouteCreateOptions", - "; \"POST /internal/observability_ai_assistant/functions/alerts\": { endpoint: \"POST /internal/observability_ai_assistant/functions/alerts\"; params?: ", - "TypeC", - "<{ body: ", - "IntersectionC", - "<[", - "TypeC", - "<{ featureIds: ", - "ArrayC", - "<", - "StringC", - ">; start: ", - "StringC", - "; end: ", - "StringC", - "; }>, ", - "PartialC", - "<{ filter: ", - "StringC", - "; includeRecovered: ", - "Type", - "; }>]>; }> | undefined; handler: ({}: ", - "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { body: { featureIds: string[]; start: string; end: string; } & { filter?: string | undefined; includeRecovered?: boolean | undefined; }; }; }) => Promise<{ content: { total: number; alerts: OutputOf>[]; }; }>; } & ", - "ObservabilityAIAssistantRouteCreateOptions", "; \"POST /internal/observability_ai_assistant/functions/summarize\": { endpoint: \"POST /internal/observability_ai_assistant/functions/summarize\"; params?: ", "TypeC", "<{ body: ", @@ -2128,33 +2038,15 @@ "RecalledEntry", "[]; }>; } & ", "ObservabilityAIAssistantRouteCreateOptions", - "; \"POST /internal/observability_ai_assistant/functions/elasticsearch\": { endpoint: \"POST /internal/observability_ai_assistant/functions/elasticsearch\"; params?: ", - "TypeC", - "<{ body: ", - "IntersectionC", - "<[", - "TypeC", - "<{ method: ", - "UnionC", - "<[", - "LiteralC", - "<\"GET\">, ", - "LiteralC", - "<\"POST\">, ", - "LiteralC", - "<\"PATCH\">, ", - "LiteralC", - "<\"PUT\">, ", - "LiteralC", - "<\"DELETE\">]>; path: ", - "StringC", - "; }>, ", - "PartialC", - "<{ body: ", - "AnyC", - "; }>]>; }> | undefined; handler: ({}: ", + "; \"GET /internal/observability_ai_assistant/functions\": { endpoint: \"GET /internal/observability_ai_assistant/functions\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { body: { method: \"GET\" | \"DELETE\" | \"POST\" | \"PUT\" | \"PATCH\"; path: string; } & { body?: any; }; }; }) => Promise; } & ", + ") => Promise<{ functionDefinitions: ", + "FunctionDefinition", + "<", + "CompatibleJSONSchema", + ">[]; contextDefinitions: ", + "ContextDefinition", + "[]; }>; } & ", "ObservabilityAIAssistantRouteCreateOptions", "; \"GET /internal/observability_ai_assistant/connectors\": { endpoint: \"GET /internal/observability_ai_assistant/connectors\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", @@ -2200,28 +2092,6 @@ }, ">; } & ", "ObservabilityAIAssistantRouteCreateOptions", - "; \"PUT /internal/observability_ai_assistant/conversation/{conversationId}/auto_title\": { endpoint: \"PUT /internal/observability_ai_assistant/conversation/{conversationId}/auto_title\"; params?: ", - "TypeC", - "<{ path: ", - "TypeC", - "<{ conversationId: ", - "StringC", - "; }>; body: ", - "TypeC", - "<{ connectorId: ", - "StringC", - "; }>; }> | undefined; handler: ({}: ", - "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { path: { conversationId: string; }; body: { connectorId: string; }; }; }) => Promise<", - { - "pluginId": "observabilityAIAssistant", - "scope": "common", - "docId": "kibObservabilityAIAssistantPluginApi", - "section": "def-common.Conversation", - "text": "Conversation" - }, - ">; } & ", - "ObservabilityAIAssistantRouteCreateOptions", "; \"PUT /internal/observability_ai_assistant/conversation/{conversationId}\": { endpoint: \"PUT /internal/observability_ai_assistant/conversation/{conversationId}\"; params?: ", "TypeC", "<{ path: ", @@ -2310,6 +2180,58 @@ }, ">; } & ", "ObservabilityAIAssistantRouteCreateOptions", + "; \"POST /internal/observability_ai_assistant/chat/complete\": { endpoint: \"POST /internal/observability_ai_assistant/chat/complete\"; params?: ", + "TypeC", + "<{ body: ", + "IntersectionC", + "<[", + "TypeC", + "<{ messages: ", + "ArrayC", + "<", + "Type", + "<", + { + "pluginId": "observabilityAIAssistant", + "scope": "common", + "docId": "kibObservabilityAIAssistantPluginApi", + "section": "def-common.Message", + "text": "Message" + }, + ", ", + { + "pluginId": "observabilityAIAssistant", + "scope": "common", + "docId": "kibObservabilityAIAssistantPluginApi", + "section": "def-common.Message", + "text": "Message" + }, + ", unknown>>; connectorId: ", + "StringC", + "; persist: ", + "Type", + "; }>, ", + "PartialC", + "<{ conversationId: ", + "StringC", + "; title: ", + "StringC", + "; }>]>; }> | undefined; handler: ({}: ", + "ObservabilityAIAssistantRouteHandlerResources", + " & { params: { body: { messages: ", + { + "pluginId": "observabilityAIAssistant", + "scope": "common", + "docId": "kibObservabilityAIAssistantPluginApi", + "section": "def-common.Message", + "text": "Message" + }, + "[]; connectorId: string; persist: boolean; } & { conversationId?: string | undefined; title?: string | undefined; }; }; }) => Promise<", + "Readable", + " | ", + "CreateChatCompletionResponse", + ">; } & ", + "ObservabilityAIAssistantRouteCreateOptions", "; \"POST /internal/observability_ai_assistant/chat\": { endpoint: \"POST /internal/observability_ai_assistant/chat\"; params?: ", "IntersectionC", "<[", @@ -2529,54 +2451,16 @@ "ObservabilityAIAssistantRouteCreateOptions", "; \"GET /internal/observability_ai_assistant/kb/status\": { endpoint: \"GET /internal/observability_ai_assistant/kb/status\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", - ") => Promise<{ ready: boolean; error?: any; deployment_state?: string | undefined; allocation_state?: string | undefined; }>; } & ", + ") => Promise<{ ready: boolean; error?: any; deployment_state?: ", + "MlDeploymentState", + " | undefined; allocation_state?: ", + "MlDeploymentAllocationState", + " | undefined; model_name?: string | undefined; }>; } & ", "ObservabilityAIAssistantRouteCreateOptions", "; \"POST /internal/observability_ai_assistant/kb/setup\": { endpoint: \"POST /internal/observability_ai_assistant/kb/setup\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", ") => Promise<{}>; } & ", "ObservabilityAIAssistantRouteCreateOptions", - "; \"POST /internal/observability_ai_assistant/functions/get_dataset_info\": { endpoint: \"POST /internal/observability_ai_assistant/functions/get_dataset_info\"; params?: ", - "TypeC", - "<{ body: ", - "TypeC", - "<{ index: ", - "StringC", - "; }>; }> | undefined; handler: ({}: ", - "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { body: { index: string; }; }; }) => Promise<{ indices: string[]; fields: { name: string; description: string; type: string; }[]; }>; } & ", - "ObservabilityAIAssistantRouteCreateOptions", - "; \"POST /internal/observability_ai_assistant/functions/alerts\": { endpoint: \"POST /internal/observability_ai_assistant/functions/alerts\"; params?: ", - "TypeC", - "<{ body: ", - "IntersectionC", - "<[", - "TypeC", - "<{ featureIds: ", - "ArrayC", - "<", - "StringC", - ">; start: ", - "StringC", - "; end: ", - "StringC", - "; }>, ", - "PartialC", - "<{ filter: ", - "StringC", - "; includeRecovered: ", - "Type", - "; }>]>; }> | undefined; handler: ({}: ", - "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { body: { featureIds: string[]; start: string; end: string; } & { filter?: string | undefined; includeRecovered?: boolean | undefined; }; }; }) => Promise<{ content: { total: number; alerts: OutputOf>[]; }; }>; } & ", - "ObservabilityAIAssistantRouteCreateOptions", "; \"POST /internal/observability_ai_assistant/functions/summarize\": { endpoint: \"POST /internal/observability_ai_assistant/functions/summarize\"; params?: ", "TypeC", "<{ body: ", @@ -2669,33 +2553,15 @@ "RecalledEntry", "[]; }>; } & ", "ObservabilityAIAssistantRouteCreateOptions", - "; \"POST /internal/observability_ai_assistant/functions/elasticsearch\": { endpoint: \"POST /internal/observability_ai_assistant/functions/elasticsearch\"; params?: ", - "TypeC", - "<{ body: ", - "IntersectionC", - "<[", - "TypeC", - "<{ method: ", - "UnionC", - "<[", - "LiteralC", - "<\"GET\">, ", - "LiteralC", - "<\"POST\">, ", - "LiteralC", - "<\"PATCH\">, ", - "LiteralC", - "<\"PUT\">, ", - "LiteralC", - "<\"DELETE\">]>; path: ", - "StringC", - "; }>, ", - "PartialC", - "<{ body: ", - "AnyC", - "; }>]>; }> | undefined; handler: ({}: ", + "; \"GET /internal/observability_ai_assistant/functions\": { endpoint: \"GET /internal/observability_ai_assistant/functions\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { body: { method: \"GET\" | \"DELETE\" | \"POST\" | \"PUT\" | \"PATCH\"; path: string; } & { body?: any; }; }; }) => Promise; } & ", + ") => Promise<{ functionDefinitions: ", + "FunctionDefinition", + "<", + "CompatibleJSONSchema", + ">[]; contextDefinitions: ", + "ContextDefinition", + "[]; }>; } & ", "ObservabilityAIAssistantRouteCreateOptions", "; \"GET /internal/observability_ai_assistant/connectors\": { endpoint: \"GET /internal/observability_ai_assistant/connectors\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", @@ -2741,28 +2607,6 @@ }, ">; } & ", "ObservabilityAIAssistantRouteCreateOptions", - "; \"PUT /internal/observability_ai_assistant/conversation/{conversationId}/auto_title\": { endpoint: \"PUT /internal/observability_ai_assistant/conversation/{conversationId}/auto_title\"; params?: ", - "TypeC", - "<{ path: ", - "TypeC", - "<{ conversationId: ", - "StringC", - "; }>; body: ", - "TypeC", - "<{ connectorId: ", - "StringC", - "; }>; }> | undefined; handler: ({}: ", - "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { path: { conversationId: string; }; body: { connectorId: string; }; }; }) => Promise<", - { - "pluginId": "observabilityAIAssistant", - "scope": "common", - "docId": "kibObservabilityAIAssistantPluginApi", - "section": "def-common.Conversation", - "text": "Conversation" - }, - ">; } & ", - "ObservabilityAIAssistantRouteCreateOptions", "; \"PUT /internal/observability_ai_assistant/conversation/{conversationId}\": { endpoint: \"PUT /internal/observability_ai_assistant/conversation/{conversationId}\"; params?: ", "TypeC", "<{ path: ", @@ -2851,6 +2695,58 @@ }, ">; } & ", "ObservabilityAIAssistantRouteCreateOptions", + "; \"POST /internal/observability_ai_assistant/chat/complete\": { endpoint: \"POST /internal/observability_ai_assistant/chat/complete\"; params?: ", + "TypeC", + "<{ body: ", + "IntersectionC", + "<[", + "TypeC", + "<{ messages: ", + "ArrayC", + "<", + "Type", + "<", + { + "pluginId": "observabilityAIAssistant", + "scope": "common", + "docId": "kibObservabilityAIAssistantPluginApi", + "section": "def-common.Message", + "text": "Message" + }, + ", ", + { + "pluginId": "observabilityAIAssistant", + "scope": "common", + "docId": "kibObservabilityAIAssistantPluginApi", + "section": "def-common.Message", + "text": "Message" + }, + ", unknown>>; connectorId: ", + "StringC", + "; persist: ", + "Type", + "; }>, ", + "PartialC", + "<{ conversationId: ", + "StringC", + "; title: ", + "StringC", + "; }>]>; }> | undefined; handler: ({}: ", + "ObservabilityAIAssistantRouteHandlerResources", + " & { params: { body: { messages: ", + { + "pluginId": "observabilityAIAssistant", + "scope": "common", + "docId": "kibObservabilityAIAssistantPluginApi", + "section": "def-common.Message", + "text": "Message" + }, + "[]; connectorId: string; persist: boolean; } & { conversationId?: string | undefined; title?: string | undefined; }; }; }) => Promise<", + "Readable", + " | ", + "CreateChatCompletionResponse", + ">; } & ", + "ObservabilityAIAssistantRouteCreateOptions", "; \"POST /internal/observability_ai_assistant/chat\": { endpoint: \"POST /internal/observability_ai_assistant/chat\"; params?: ", "IntersectionC", "<[", @@ -2942,7 +2838,7 @@ "label": "ObservabilityAIAssistantAPIEndpoint", "description": [], "signature": [ - "\"POST /internal/observability_ai_assistant/chat\" | \"GET /internal/observability_ai_assistant/conversation/{conversationId}\" | \"POST /internal/observability_ai_assistant/conversations\" | \"POST /internal/observability_ai_assistant/conversation\" | \"PUT /internal/observability_ai_assistant/conversation/{conversationId}\" | \"PUT /internal/observability_ai_assistant/conversation/{conversationId}/auto_title\" | \"PUT /internal/observability_ai_assistant/conversation/{conversationId}/title\" | \"DELETE /internal/observability_ai_assistant/conversation/{conversationId}\" | \"GET /internal/observability_ai_assistant/connectors\" | \"POST /internal/observability_ai_assistant/functions/elasticsearch\" | \"POST /internal/observability_ai_assistant/functions/recall\" | \"POST /internal/observability_ai_assistant/functions/summarize\" | \"POST /internal/observability_ai_assistant/functions/alerts\" | \"POST /internal/observability_ai_assistant/functions/get_dataset_info\" | \"POST /internal/observability_ai_assistant/kb/setup\" | \"GET /internal/observability_ai_assistant/kb/status\" | \"GET /internal/observability_ai_assistant/kb/entries\" | \"POST /internal/observability_ai_assistant/kb/entries/import\" | \"POST /internal/observability_ai_assistant/kb/entries/save\" | \"DELETE /internal/observability_ai_assistant/kb/entries/{entryId}\"" + "\"POST /internal/observability_ai_assistant/chat\" | \"POST /internal/observability_ai_assistant/chat/complete\" | \"GET /internal/observability_ai_assistant/conversation/{conversationId}\" | \"POST /internal/observability_ai_assistant/conversations\" | \"POST /internal/observability_ai_assistant/conversation\" | \"PUT /internal/observability_ai_assistant/conversation/{conversationId}\" | \"PUT /internal/observability_ai_assistant/conversation/{conversationId}/title\" | \"DELETE /internal/observability_ai_assistant/conversation/{conversationId}\" | \"GET /internal/observability_ai_assistant/connectors\" | \"GET /internal/observability_ai_assistant/functions\" | \"POST /internal/observability_ai_assistant/functions/recall\" | \"POST /internal/observability_ai_assistant/functions/summarize\" | \"POST /internal/observability_ai_assistant/kb/setup\" | \"GET /internal/observability_ai_assistant/kb/status\" | \"GET /internal/observability_ai_assistant/kb/entries\" | \"POST /internal/observability_ai_assistant/kb/entries/import\" | \"POST /internal/observability_ai_assistant/kb/entries/save\" | \"DELETE /internal/observability_ai_assistant/kb/entries/{entryId}\"" ], "path": "x-pack/plugins/observability_ai_assistant/public/api/index.ts", "deprecated": false, @@ -3004,7 +2900,7 @@ "label": "callApi", "description": [], "signature": [ - "(endpoint: TEndpoint, ...args: MaybeOptionalArgs<", + "(endpoint: TEndpoint, ...args: MaybeOptionalArgs<", { "pluginId": "@kbn/server-route-repository", "scope": "common", @@ -3148,54 +3044,16 @@ "ObservabilityAIAssistantRouteCreateOptions", "; \"GET /internal/observability_ai_assistant/kb/status\": { endpoint: \"GET /internal/observability_ai_assistant/kb/status\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", - ") => Promise<{ ready: boolean; error?: any; deployment_state?: string | undefined; allocation_state?: string | undefined; }>; } & ", + ") => Promise<{ ready: boolean; error?: any; deployment_state?: ", + "MlDeploymentState", + " | undefined; allocation_state?: ", + "MlDeploymentAllocationState", + " | undefined; model_name?: string | undefined; }>; } & ", "ObservabilityAIAssistantRouteCreateOptions", "; \"POST /internal/observability_ai_assistant/kb/setup\": { endpoint: \"POST /internal/observability_ai_assistant/kb/setup\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", ") => Promise<{}>; } & ", "ObservabilityAIAssistantRouteCreateOptions", - "; \"POST /internal/observability_ai_assistant/functions/get_dataset_info\": { endpoint: \"POST /internal/observability_ai_assistant/functions/get_dataset_info\"; params?: ", - "TypeC", - "<{ body: ", - "TypeC", - "<{ index: ", - "StringC", - "; }>; }> | undefined; handler: ({}: ", - "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { body: { index: string; }; }; }) => Promise<{ indices: string[]; fields: { name: string; description: string; type: string; }[]; }>; } & ", - "ObservabilityAIAssistantRouteCreateOptions", - "; \"POST /internal/observability_ai_assistant/functions/alerts\": { endpoint: \"POST /internal/observability_ai_assistant/functions/alerts\"; params?: ", - "TypeC", - "<{ body: ", - "IntersectionC", - "<[", - "TypeC", - "<{ featureIds: ", - "ArrayC", - "<", - "StringC", - ">; start: ", - "StringC", - "; end: ", - "StringC", - "; }>, ", - "PartialC", - "<{ filter: ", - "StringC", - "; includeRecovered: ", - "Type", - "; }>]>; }> | undefined; handler: ({}: ", - "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { body: { featureIds: string[]; start: string; end: string; } & { filter?: string | undefined; includeRecovered?: boolean | undefined; }; }; }) => Promise<{ content: { total: number; alerts: OutputOf>[]; }; }>; } & ", - "ObservabilityAIAssistantRouteCreateOptions", "; \"POST /internal/observability_ai_assistant/functions/summarize\": { endpoint: \"POST /internal/observability_ai_assistant/functions/summarize\"; params?: ", "TypeC", "<{ body: ", @@ -3288,33 +3146,15 @@ "RecalledEntry", "[]; }>; } & ", "ObservabilityAIAssistantRouteCreateOptions", - "; \"POST /internal/observability_ai_assistant/functions/elasticsearch\": { endpoint: \"POST /internal/observability_ai_assistant/functions/elasticsearch\"; params?: ", - "TypeC", - "<{ body: ", - "IntersectionC", - "<[", - "TypeC", - "<{ method: ", - "UnionC", - "<[", - "LiteralC", - "<\"GET\">, ", - "LiteralC", - "<\"POST\">, ", - "LiteralC", - "<\"PATCH\">, ", - "LiteralC", - "<\"PUT\">, ", - "LiteralC", - "<\"DELETE\">]>; path: ", - "StringC", - "; }>, ", - "PartialC", - "<{ body: ", - "AnyC", - "; }>]>; }> | undefined; handler: ({}: ", + "; \"GET /internal/observability_ai_assistant/functions\": { endpoint: \"GET /internal/observability_ai_assistant/functions\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { body: { method: \"GET\" | \"DELETE\" | \"POST\" | \"PUT\" | \"PATCH\"; path: string; } & { body?: any; }; }; }) => Promise; } & ", + ") => Promise<{ functionDefinitions: ", + "FunctionDefinition", + "<", + "CompatibleJSONSchema", + ">[]; contextDefinitions: ", + "ContextDefinition", + "[]; }>; } & ", "ObservabilityAIAssistantRouteCreateOptions", "; \"GET /internal/observability_ai_assistant/connectors\": { endpoint: \"GET /internal/observability_ai_assistant/connectors\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", @@ -3360,28 +3200,6 @@ }, ">; } & ", "ObservabilityAIAssistantRouteCreateOptions", - "; \"PUT /internal/observability_ai_assistant/conversation/{conversationId}/auto_title\": { endpoint: \"PUT /internal/observability_ai_assistant/conversation/{conversationId}/auto_title\"; params?: ", - "TypeC", - "<{ path: ", - "TypeC", - "<{ conversationId: ", - "StringC", - "; }>; body: ", - "TypeC", - "<{ connectorId: ", - "StringC", - "; }>; }> | undefined; handler: ({}: ", - "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { path: { conversationId: string; }; body: { connectorId: string; }; }; }) => Promise<", - { - "pluginId": "observabilityAIAssistant", - "scope": "common", - "docId": "kibObservabilityAIAssistantPluginApi", - "section": "def-common.Conversation", - "text": "Conversation" - }, - ">; } & ", - "ObservabilityAIAssistantRouteCreateOptions", "; \"PUT /internal/observability_ai_assistant/conversation/{conversationId}\": { endpoint: \"PUT /internal/observability_ai_assistant/conversation/{conversationId}\"; params?: ", "TypeC", "<{ path: ", @@ -3470,9 +3288,7 @@ }, ">; } & ", "ObservabilityAIAssistantRouteCreateOptions", - "; \"POST /internal/observability_ai_assistant/chat\": { endpoint: \"POST /internal/observability_ai_assistant/chat\"; params?: ", - "IntersectionC", - "<[", + "; \"POST /internal/observability_ai_assistant/chat/complete\": { endpoint: \"POST /internal/observability_ai_assistant/chat/complete\"; params?: ", "TypeC", "<{ body: ", "IntersectionC", @@ -3500,12 +3316,66 @@ }, ", unknown>>; connectorId: ", "StringC", - "; functions: ", - "ArrayC", - "<", - "TypeC", - "<{ name: ", - "StringC", + "; persist: ", + "Type", + "; }>, ", + "PartialC", + "<{ conversationId: ", + "StringC", + "; title: ", + "StringC", + "; }>]>; }> | undefined; handler: ({}: ", + "ObservabilityAIAssistantRouteHandlerResources", + " & { params: { body: { messages: ", + { + "pluginId": "observabilityAIAssistant", + "scope": "common", + "docId": "kibObservabilityAIAssistantPluginApi", + "section": "def-common.Message", + "text": "Message" + }, + "[]; connectorId: string; persist: boolean; } & { conversationId?: string | undefined; title?: string | undefined; }; }; }) => Promise<", + "Readable", + " | ", + "CreateChatCompletionResponse", + ">; } & ", + "ObservabilityAIAssistantRouteCreateOptions", + "; \"POST /internal/observability_ai_assistant/chat\": { endpoint: \"POST /internal/observability_ai_assistant/chat\"; params?: ", + "IntersectionC", + "<[", + "TypeC", + "<{ body: ", + "IntersectionC", + "<[", + "TypeC", + "<{ messages: ", + "ArrayC", + "<", + "Type", + "<", + { + "pluginId": "observabilityAIAssistant", + "scope": "common", + "docId": "kibObservabilityAIAssistantPluginApi", + "section": "def-common.Message", + "text": "Message" + }, + ", ", + { + "pluginId": "observabilityAIAssistant", + "scope": "common", + "docId": "kibObservabilityAIAssistantPluginApi", + "section": "def-common.Message", + "text": "Message" + }, + ", unknown>>; connectorId: ", + "StringC", + "; functions: ", + "ArrayC", + "<", + "TypeC", + "<{ name: ", + "StringC", "; description: ", "StringC", "; parameters: ", @@ -3680,54 +3550,16 @@ "ObservabilityAIAssistantRouteCreateOptions", "; \"GET /internal/observability_ai_assistant/kb/status\": { endpoint: \"GET /internal/observability_ai_assistant/kb/status\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", - ") => Promise<{ ready: boolean; error?: any; deployment_state?: string | undefined; allocation_state?: string | undefined; }>; } & ", + ") => Promise<{ ready: boolean; error?: any; deployment_state?: ", + "MlDeploymentState", + " | undefined; allocation_state?: ", + "MlDeploymentAllocationState", + " | undefined; model_name?: string | undefined; }>; } & ", "ObservabilityAIAssistantRouteCreateOptions", "; \"POST /internal/observability_ai_assistant/kb/setup\": { endpoint: \"POST /internal/observability_ai_assistant/kb/setup\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", ") => Promise<{}>; } & ", "ObservabilityAIAssistantRouteCreateOptions", - "; \"POST /internal/observability_ai_assistant/functions/get_dataset_info\": { endpoint: \"POST /internal/observability_ai_assistant/functions/get_dataset_info\"; params?: ", - "TypeC", - "<{ body: ", - "TypeC", - "<{ index: ", - "StringC", - "; }>; }> | undefined; handler: ({}: ", - "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { body: { index: string; }; }; }) => Promise<{ indices: string[]; fields: { name: string; description: string; type: string; }[]; }>; } & ", - "ObservabilityAIAssistantRouteCreateOptions", - "; \"POST /internal/observability_ai_assistant/functions/alerts\": { endpoint: \"POST /internal/observability_ai_assistant/functions/alerts\"; params?: ", - "TypeC", - "<{ body: ", - "IntersectionC", - "<[", - "TypeC", - "<{ featureIds: ", - "ArrayC", - "<", - "StringC", - ">; start: ", - "StringC", - "; end: ", - "StringC", - "; }>, ", - "PartialC", - "<{ filter: ", - "StringC", - "; includeRecovered: ", - "Type", - "; }>]>; }> | undefined; handler: ({}: ", - "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { body: { featureIds: string[]; start: string; end: string; } & { filter?: string | undefined; includeRecovered?: boolean | undefined; }; }; }) => Promise<{ content: { total: number; alerts: OutputOf>[]; }; }>; } & ", - "ObservabilityAIAssistantRouteCreateOptions", "; \"POST /internal/observability_ai_assistant/functions/summarize\": { endpoint: \"POST /internal/observability_ai_assistant/functions/summarize\"; params?: ", "TypeC", "<{ body: ", @@ -3820,33 +3652,15 @@ "RecalledEntry", "[]; }>; } & ", "ObservabilityAIAssistantRouteCreateOptions", - "; \"POST /internal/observability_ai_assistant/functions/elasticsearch\": { endpoint: \"POST /internal/observability_ai_assistant/functions/elasticsearch\"; params?: ", - "TypeC", - "<{ body: ", - "IntersectionC", - "<[", - "TypeC", - "<{ method: ", - "UnionC", - "<[", - "LiteralC", - "<\"GET\">, ", - "LiteralC", - "<\"POST\">, ", - "LiteralC", - "<\"PATCH\">, ", - "LiteralC", - "<\"PUT\">, ", - "LiteralC", - "<\"DELETE\">]>; path: ", - "StringC", - "; }>, ", - "PartialC", - "<{ body: ", - "AnyC", - "; }>]>; }> | undefined; handler: ({}: ", + "; \"GET /internal/observability_ai_assistant/functions\": { endpoint: \"GET /internal/observability_ai_assistant/functions\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { body: { method: \"GET\" | \"DELETE\" | \"POST\" | \"PUT\" | \"PATCH\"; path: string; } & { body?: any; }; }; }) => Promise; } & ", + ") => Promise<{ functionDefinitions: ", + "FunctionDefinition", + "<", + "CompatibleJSONSchema", + ">[]; contextDefinitions: ", + "ContextDefinition", + "[]; }>; } & ", "ObservabilityAIAssistantRouteCreateOptions", "; \"GET /internal/observability_ai_assistant/connectors\": { endpoint: \"GET /internal/observability_ai_assistant/connectors\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", @@ -3892,28 +3706,6 @@ }, ">; } & ", "ObservabilityAIAssistantRouteCreateOptions", - "; \"PUT /internal/observability_ai_assistant/conversation/{conversationId}/auto_title\": { endpoint: \"PUT /internal/observability_ai_assistant/conversation/{conversationId}/auto_title\"; params?: ", - "TypeC", - "<{ path: ", - "TypeC", - "<{ conversationId: ", - "StringC", - "; }>; body: ", - "TypeC", - "<{ connectorId: ", - "StringC", - "; }>; }> | undefined; handler: ({}: ", - "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { path: { conversationId: string; }; body: { connectorId: string; }; }; }) => Promise<", - { - "pluginId": "observabilityAIAssistant", - "scope": "common", - "docId": "kibObservabilityAIAssistantPluginApi", - "section": "def-common.Conversation", - "text": "Conversation" - }, - ">; } & ", - "ObservabilityAIAssistantRouteCreateOptions", "; \"PUT /internal/observability_ai_assistant/conversation/{conversationId}\": { endpoint: \"PUT /internal/observability_ai_assistant/conversation/{conversationId}\"; params?: ", "TypeC", "<{ path: ", @@ -4002,6 +3794,58 @@ }, ">; } & ", "ObservabilityAIAssistantRouteCreateOptions", + "; \"POST /internal/observability_ai_assistant/chat/complete\": { endpoint: \"POST /internal/observability_ai_assistant/chat/complete\"; params?: ", + "TypeC", + "<{ body: ", + "IntersectionC", + "<[", + "TypeC", + "<{ messages: ", + "ArrayC", + "<", + "Type", + "<", + { + "pluginId": "observabilityAIAssistant", + "scope": "common", + "docId": "kibObservabilityAIAssistantPluginApi", + "section": "def-common.Message", + "text": "Message" + }, + ", ", + { + "pluginId": "observabilityAIAssistant", + "scope": "common", + "docId": "kibObservabilityAIAssistantPluginApi", + "section": "def-common.Message", + "text": "Message" + }, + ", unknown>>; connectorId: ", + "StringC", + "; persist: ", + "Type", + "; }>, ", + "PartialC", + "<{ conversationId: ", + "StringC", + "; title: ", + "StringC", + "; }>]>; }> | undefined; handler: ({}: ", + "ObservabilityAIAssistantRouteHandlerResources", + " & { params: { body: { messages: ", + { + "pluginId": "observabilityAIAssistant", + "scope": "common", + "docId": "kibObservabilityAIAssistantPluginApi", + "section": "def-common.Message", + "text": "Message" + }, + "[]; connectorId: string; persist: boolean; } & { conversationId?: string | undefined; title?: string | undefined; }; }; }) => Promise<", + "Readable", + " | ", + "CreateChatCompletionResponse", + ">; } & ", + "ObservabilityAIAssistantRouteCreateOptions", "; \"POST /internal/observability_ai_assistant/chat\": { endpoint: \"POST /internal/observability_ai_assistant/chat\"; params?: ", "IntersectionC", "<[", @@ -4187,8 +4031,8 @@ "pluginId": "share", "scope": "public", "docId": "kibSharePluginApi", - "section": "def-public.SharePluginStart", - "text": "SharePluginStart" + "section": "def-public.SharePublicStart", + "text": "SharePublicStart" } ], "path": "x-pack/plugins/observability_ai_assistant/public/utils/storybook_decorator.tsx", @@ -4287,68 +4131,7 @@ "server": { "classes": [], "functions": [], - "interfaces": [ - { - "parentPluginId": "observabilityAIAssistant", - "id": "def-server.ObservabilityAIAssistantPluginSetup", - "type": "Interface", - "tags": [], - "label": "ObservabilityAIAssistantPluginSetup", - "description": [], - "path": "x-pack/plugins/observability_ai_assistant/server/index.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "observabilityAIAssistant", - "id": "def-server.ObservabilityAIAssistantPluginSetup.service", - "type": "Object", - "tags": [], - "label": "service", - "description": [ - "\nReturns a Observability AI Assistant service instance" - ], - "signature": [ - "ObservabilityAIAssistantService" - ], - "path": "x-pack/plugins/observability_ai_assistant/server/index.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - }, - { - "parentPluginId": "observabilityAIAssistant", - "id": "def-server.ObservabilityAIAssistantPluginStart", - "type": "Interface", - "tags": [], - "label": "ObservabilityAIAssistantPluginStart", - "description": [], - "path": "x-pack/plugins/observability_ai_assistant/server/index.ts", - "deprecated": false, - "trackAdoption": false, - "children": [ - { - "parentPluginId": "observabilityAIAssistant", - "id": "def-server.ObservabilityAIAssistantPluginStart.service", - "type": "Object", - "tags": [], - "label": "service", - "description": [ - "\nReturns a Observability AI Assistant service instance" - ], - "signature": [ - "ObservabilityAIAssistantService" - ], - "path": "x-pack/plugins/observability_ai_assistant/server/index.ts", - "deprecated": false, - "trackAdoption": false - } - ], - "initialIsOpen": false - } - ], + "interfaces": [], "enums": [], "misc": [ { @@ -4495,54 +4278,16 @@ "ObservabilityAIAssistantRouteCreateOptions", "; \"GET /internal/observability_ai_assistant/kb/status\": { endpoint: \"GET /internal/observability_ai_assistant/kb/status\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", - ") => Promise<{ ready: boolean; error?: any; deployment_state?: string | undefined; allocation_state?: string | undefined; }>; } & ", + ") => Promise<{ ready: boolean; error?: any; deployment_state?: ", + "MlDeploymentState", + " | undefined; allocation_state?: ", + "MlDeploymentAllocationState", + " | undefined; model_name?: string | undefined; }>; } & ", "ObservabilityAIAssistantRouteCreateOptions", "; \"POST /internal/observability_ai_assistant/kb/setup\": { endpoint: \"POST /internal/observability_ai_assistant/kb/setup\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", ") => Promise<{}>; } & ", "ObservabilityAIAssistantRouteCreateOptions", - "; \"POST /internal/observability_ai_assistant/functions/get_dataset_info\": { endpoint: \"POST /internal/observability_ai_assistant/functions/get_dataset_info\"; params?: ", - "TypeC", - "<{ body: ", - "TypeC", - "<{ index: ", - "StringC", - "; }>; }> | undefined; handler: ({}: ", - "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { body: { index: string; }; }; }) => Promise<{ indices: string[]; fields: { name: string; description: string; type: string; }[]; }>; } & ", - "ObservabilityAIAssistantRouteCreateOptions", - "; \"POST /internal/observability_ai_assistant/functions/alerts\": { endpoint: \"POST /internal/observability_ai_assistant/functions/alerts\"; params?: ", - "TypeC", - "<{ body: ", - "IntersectionC", - "<[", - "TypeC", - "<{ featureIds: ", - "ArrayC", - "<", - "StringC", - ">; start: ", - "StringC", - "; end: ", - "StringC", - "; }>, ", - "PartialC", - "<{ filter: ", - "StringC", - "; includeRecovered: ", - "Type", - "; }>]>; }> | undefined; handler: ({}: ", - "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { body: { featureIds: string[]; start: string; end: string; } & { filter?: string | undefined; includeRecovered?: boolean | undefined; }; }; }) => Promise<{ content: { total: number; alerts: OutputOf>[]; }; }>; } & ", - "ObservabilityAIAssistantRouteCreateOptions", "; \"POST /internal/observability_ai_assistant/functions/summarize\": { endpoint: \"POST /internal/observability_ai_assistant/functions/summarize\"; params?: ", "TypeC", "<{ body: ", @@ -4635,33 +4380,15 @@ "RecalledEntry", "[]; }>; } & ", "ObservabilityAIAssistantRouteCreateOptions", - "; \"POST /internal/observability_ai_assistant/functions/elasticsearch\": { endpoint: \"POST /internal/observability_ai_assistant/functions/elasticsearch\"; params?: ", - "TypeC", - "<{ body: ", - "IntersectionC", - "<[", - "TypeC", - "<{ method: ", - "UnionC", - "<[", - "LiteralC", - "<\"GET\">, ", - "LiteralC", - "<\"POST\">, ", - "LiteralC", - "<\"PATCH\">, ", - "LiteralC", - "<\"PUT\">, ", - "LiteralC", - "<\"DELETE\">]>; path: ", - "StringC", - "; }>, ", - "PartialC", - "<{ body: ", - "AnyC", - "; }>]>; }> | undefined; handler: ({}: ", + "; \"GET /internal/observability_ai_assistant/functions\": { endpoint: \"GET /internal/observability_ai_assistant/functions\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { body: { method: \"GET\" | \"DELETE\" | \"POST\" | \"PUT\" | \"PATCH\"; path: string; } & { body?: any; }; }; }) => Promise; } & ", + ") => Promise<{ functionDefinitions: ", + "FunctionDefinition", + "<", + "CompatibleJSONSchema", + ">[]; contextDefinitions: ", + "ContextDefinition", + "[]; }>; } & ", "ObservabilityAIAssistantRouteCreateOptions", "; \"GET /internal/observability_ai_assistant/connectors\": { endpoint: \"GET /internal/observability_ai_assistant/connectors\"; params?: undefined; handler: ({}: ", "ObservabilityAIAssistantRouteHandlerResources", @@ -4707,28 +4434,6 @@ }, ">; } & ", "ObservabilityAIAssistantRouteCreateOptions", - "; \"PUT /internal/observability_ai_assistant/conversation/{conversationId}/auto_title\": { endpoint: \"PUT /internal/observability_ai_assistant/conversation/{conversationId}/auto_title\"; params?: ", - "TypeC", - "<{ path: ", - "TypeC", - "<{ conversationId: ", - "StringC", - "; }>; body: ", - "TypeC", - "<{ connectorId: ", - "StringC", - "; }>; }> | undefined; handler: ({}: ", - "ObservabilityAIAssistantRouteHandlerResources", - " & { params: { path: { conversationId: string; }; body: { connectorId: string; }; }; }) => Promise<", - { - "pluginId": "observabilityAIAssistant", - "scope": "common", - "docId": "kibObservabilityAIAssistantPluginApi", - "section": "def-common.Conversation", - "text": "Conversation" - }, - ">; } & ", - "ObservabilityAIAssistantRouteCreateOptions", "; \"PUT /internal/observability_ai_assistant/conversation/{conversationId}\": { endpoint: \"PUT /internal/observability_ai_assistant/conversation/{conversationId}\"; params?: ", "TypeC", "<{ path: ", @@ -4817,6 +4522,58 @@ }, ">; } & ", "ObservabilityAIAssistantRouteCreateOptions", + "; \"POST /internal/observability_ai_assistant/chat/complete\": { endpoint: \"POST /internal/observability_ai_assistant/chat/complete\"; params?: ", + "TypeC", + "<{ body: ", + "IntersectionC", + "<[", + "TypeC", + "<{ messages: ", + "ArrayC", + "<", + "Type", + "<", + { + "pluginId": "observabilityAIAssistant", + "scope": "common", + "docId": "kibObservabilityAIAssistantPluginApi", + "section": "def-common.Message", + "text": "Message" + }, + ", ", + { + "pluginId": "observabilityAIAssistant", + "scope": "common", + "docId": "kibObservabilityAIAssistantPluginApi", + "section": "def-common.Message", + "text": "Message" + }, + ", unknown>>; connectorId: ", + "StringC", + "; persist: ", + "Type", + "; }>, ", + "PartialC", + "<{ conversationId: ", + "StringC", + "; title: ", + "StringC", + "; }>]>; }> | undefined; handler: ({}: ", + "ObservabilityAIAssistantRouteHandlerResources", + " & { params: { body: { messages: ", + { + "pluginId": "observabilityAIAssistant", + "scope": "common", + "docId": "kibObservabilityAIAssistantPluginApi", + "section": "def-common.Message", + "text": "Message" + }, + "[]; connectorId: string; persist: boolean; } & { conversationId?: string | undefined; title?: string | undefined; }; }; }) => Promise<", + "Readable", + " | ", + "CreateChatCompletionResponse", + ">; } & ", + "ObservabilityAIAssistantRouteCreateOptions", "; \"POST /internal/observability_ai_assistant/chat\": { endpoint: \"POST /internal/observability_ai_assistant/chat\"; params?: ", "IntersectionC", "<[", @@ -4891,7 +4648,69 @@ "initialIsOpen": false } ], - "objects": [] + "objects": [], + "start": { + "parentPluginId": "observabilityAIAssistant", + "id": "def-server.ObservabilityAIAssistantPluginStart", + "type": "Interface", + "tags": [], + "label": "ObservabilityAIAssistantPluginStart", + "description": [], + "path": "x-pack/plugins/observability_ai_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "observabilityAIAssistant", + "id": "def-server.ObservabilityAIAssistantPluginStart.service", + "type": "Object", + "tags": [], + "label": "service", + "description": [ + "\nReturns a Observability AI Assistant service instance" + ], + "signature": [ + "ObservabilityAIAssistantService" + ], + "path": "x-pack/plugins/observability_ai_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "lifecycle": "start", + "initialIsOpen": true + }, + "setup": { + "parentPluginId": "observabilityAIAssistant", + "id": "def-server.ObservabilityAIAssistantPluginSetup", + "type": "Interface", + "tags": [], + "label": "ObservabilityAIAssistantPluginSetup", + "description": [], + "path": "x-pack/plugins/observability_ai_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "observabilityAIAssistant", + "id": "def-server.ObservabilityAIAssistantPluginSetup.service", + "type": "Object", + "tags": [], + "label": "service", + "description": [ + "\nReturns a Observability AI Assistant service instance" + ], + "signature": [ + "ObservabilityAIAssistantService" + ], + "path": "x-pack/plugins/observability_ai_assistant/server/types.ts", + "deprecated": false, + "trackAdoption": false + } + ], + "lifecycle": "setup", + "initialIsOpen": true + } }, "common": { "classes": [], diff --git a/api_docs/observability_a_i_assistant.mdx b/api_docs/observability_a_i_assistant.mdx index d7b36fc5cb861..90ad0891e3516 100644 --- a/api_docs/observability_a_i_assistant.mdx +++ b/api_docs/observability_a_i_assistant.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityAIAssistant title: "observabilityAIAssistant" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityAIAssistant plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityAIAssistant'] --- import observabilityAIAssistantObj from './observability_a_i_assistant.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs- | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 91 | 0 | 86 | 9 | +| 91 | 0 | 86 | 12 | ## Client @@ -48,8 +48,11 @@ Contact [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs- ## Server -### Interfaces - +### Setup + + +### Start + ### Consts, variables and types diff --git a/api_docs/observability_log_explorer.devdocs.json b/api_docs/observability_log_explorer.devdocs.json index cbf4d68df4200..d53269feabf30 100644 --- a/api_docs/observability_log_explorer.devdocs.json +++ b/api_docs/observability_log_explorer.devdocs.json @@ -283,7 +283,41 @@ "initialIsOpen": false } ], - "functions": [], + "functions": [ + { + "parentPluginId": "observabilityLogExplorer", + "id": "def-common.deepCompactObject", + "type": "Function", + "tags": [], + "label": "deepCompactObject", + "description": [], + "signature": [ + ">(obj: Value) => Value" + ], + "path": "x-pack/plugins/observability_log_explorer/common/utils/deep_compact_object.ts", + "deprecated": false, + "trackAdoption": false, + "children": [ + { + "parentPluginId": "observabilityLogExplorer", + "id": "def-common.deepCompactObject.$1", + "type": "Uncategorized", + "tags": [], + "label": "obj", + "description": [], + "signature": [ + "Value" + ], + "path": "x-pack/plugins/observability_log_explorer/common/utils/deep_compact_object.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], + "returnComment": [], + "initialIsOpen": false + } + ], "interfaces": [ { "parentPluginId": "observabilityLogExplorer", @@ -359,7 +393,23 @@ } ], "enums": [], - "misc": [], + "misc": [ + { + "parentPluginId": "observabilityLogExplorer", + "id": "def-common.OBSERVABILITY_LOG_EXPLORER_URL_STATE_KEY", + "type": "string", + "tags": [], + "label": "OBSERVABILITY_LOG_EXPLORER_URL_STATE_KEY", + "description": [], + "signature": [ + "\"pageState\"" + ], + "path": "x-pack/plugins/observability_log_explorer/common/url_schema/common.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + } + ], "objects": [] } } \ No newline at end of file diff --git a/api_docs/observability_log_explorer.mdx b/api_docs/observability_log_explorer.mdx index 8f9a88079c037..81d42a1361eec 100644 --- a/api_docs/observability_log_explorer.mdx +++ b/api_docs/observability_log_explorer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityLogExplorer title: "observabilityLogExplorer" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityLogExplorer plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityLogExplorer'] --- import observabilityLogExplorerObj from './observability_log_explorer.devdocs.json'; @@ -21,13 +21,19 @@ Contact [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 15 | 0 | 15 | 1 | +| 18 | 0 | 18 | 1 | ## Common +### Functions + + ### Classes ### Interfaces +### Consts, variables and types + + diff --git a/api_docs/observability_onboarding.mdx b/api_docs/observability_onboarding.mdx index c397d006e5e54..f87755100a6d4 100644 --- a/api_docs/observability_onboarding.mdx +++ b/api_docs/observability_onboarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityOnboarding title: "observabilityOnboarding" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityOnboarding plugin -date: 2023-12-06 +date: 2023-12-19 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 938f26fbaf7dd..a347d6029067f 100644 --- a/api_docs/observability_shared.devdocs.json +++ b/api_docs/observability_shared.devdocs.json @@ -65,37 +65,7 @@ }, ", pluginsSetup: ", "ObservabilitySharedSetup", - ") => { locators: { profiling: { flamegraphLocator: ", - { - "pluginId": "share", - "scope": "common", - "docId": "kibSharePluginApi", - "section": "def-common.LocatorPublic", - "text": "LocatorPublic" - }, - "<", - "FlamegraphLocatorParams", - ">; topNFunctionsLocator: ", - { - "pluginId": "share", - "scope": "common", - "docId": "kibSharePluginApi", - "section": "def-common.LocatorPublic", - "text": "LocatorPublic" - }, - "<", - "TopNFunctionsLocatorParams", - ">; stacktracesLocator: ", - { - "pluginId": "share", - "scope": "common", - "docId": "kibSharePluginApi", - "section": "def-common.LocatorPublic", - "text": "LocatorPublic" - }, - "<", - "StacktracesLocatorParams", - ">; }; }; navigation: { registerSections: (sections$: ", + ") => { locators: ObservabilitySharedLocators; navigation: { registerSections: (sections$: ", "Observable", "<", { @@ -169,7 +139,7 @@ }, ", plugins: ", "ObservabilitySharedStart", - ") => { navigation: { PageTemplate: (pageTemplateProps: ", + ") => { locators: ObservabilitySharedLocators; navigation: { PageTemplate: (pageTemplateProps: ", "WrappedPageTemplateProps", ") => JSX.Element; registerSections: (sections$: ", "Observable", @@ -491,8 +461,8 @@ "pluginId": "uiActions", "scope": "public", "docId": "kibUiActionsPluginApi", - "section": "def-public.UiActionsStart", - "text": "UiActionsStart" + "section": "def-public.UiActionsPublicStart", + "text": "UiActionsPublicStart" }, "; triggerId: ", { @@ -1268,17 +1238,19 @@ }, { "parentPluginId": "observabilityShared", - "id": "def-public.useChartTheme", + "id": "def-public.useChartThemes", "type": "Function", "tags": [], - "label": "useChartTheme", + "label": "useChartThemes", "description": [], "signature": [ - "() => ", + "() => { baseTheme: ", + "Theme", + "; theme: ", "RecursivePartial", "<", "Theme", - ">[]" + ">[]; }" ], "path": "x-pack/plugins/observability_shared/public/hooks/use_chart_theme.tsx", "deprecated": false, @@ -2944,37 +2916,7 @@ "label": "ObservabilitySharedPluginSetup", "description": [], "signature": [ - "{ locators: { profiling: { flamegraphLocator: ", - { - "pluginId": "share", - "scope": "common", - "docId": "kibSharePluginApi", - "section": "def-common.LocatorPublic", - "text": "LocatorPublic" - }, - "<", - "FlamegraphLocatorParams", - ">; topNFunctionsLocator: ", - { - "pluginId": "share", - "scope": "common", - "docId": "kibSharePluginApi", - "section": "def-common.LocatorPublic", - "text": "LocatorPublic" - }, - "<", - "TopNFunctionsLocatorParams", - ">; stacktracesLocator: ", - { - "pluginId": "share", - "scope": "common", - "docId": "kibSharePluginApi", - "section": "def-common.LocatorPublic", - "text": "LocatorPublic" - }, - "<", - "StacktracesLocatorParams", - ">; }; }; navigation: { registerSections: (sections$: ", + "{ locators: ObservabilitySharedLocators; navigation: { registerSections: (sections$: ", "Observable", "<", { @@ -2999,7 +2941,7 @@ "label": "ObservabilitySharedPluginStart", "description": [], "signature": [ - "{ navigation: { PageTemplate: (pageTemplateProps: ", + "{ locators: ObservabilitySharedLocators; navigation: { PageTemplate: (pageTemplateProps: ", "WrappedPageTemplateProps", ") => JSX.Element; registerSections: (sections$: ", "Observable", @@ -3060,36 +3002,12 @@ "description": [], "signature": [ "{ flamegraphLocator: ", - { - "pluginId": "share", - "scope": "common", - "docId": "kibSharePluginApi", - "section": "def-common.LocatorPublic", - "text": "LocatorPublic" - }, - "<", - "FlamegraphLocatorParams", - ">; topNFunctionsLocator: ", - { - "pluginId": "share", - "scope": "common", - "docId": "kibSharePluginApi", - "section": "def-common.LocatorPublic", - "text": "LocatorPublic" - }, - "<", - "TopNFunctionsLocatorParams", - ">; stacktracesLocator: ", - { - "pluginId": "share", - "scope": "common", - "docId": "kibSharePluginApi", - "section": "def-common.LocatorPublic", - "text": "LocatorPublic" - }, - "<", - "StacktracesLocatorParams", - ">; }" + "FlamegraphLocator", + "; topNFunctionsLocator: ", + "TopNFunctionsLocator", + "; stacktracesLocator: ", + "StacktracesLocator", + "; }" ], "path": "x-pack/plugins/observability_shared/public/plugin.ts", "deprecated": false, diff --git a/api_docs/observability_shared.mdx b/api_docs/observability_shared.mdx index e2ab312ddb483..5334dab9e511c 100644 --- a/api_docs/observability_shared.mdx +++ b/api_docs/observability_shared.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/observabilityShared title: "observabilityShared" image: https://source.unsplash.com/400x175/?github description: API docs for the observabilityShared plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'observabilityShared'] --- import observabilitySharedObj from './observability_shared.devdocs.json'; diff --git a/api_docs/osquery.mdx b/api_docs/osquery.mdx index a7a8bd53d09d4..49504c2b52e39 100644 --- a/api_docs/osquery.mdx +++ b/api_docs/osquery.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/osquery title: "osquery" image: https://source.unsplash.com/400x175/?github description: API docs for the osquery plugin -date: 2023-12-06 +date: 2023-12-19 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 8faf240d4a9ce..e2c31190e4daf 100644 --- a/api_docs/painless_lab.mdx +++ b/api_docs/painless_lab.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/painlessLab title: "painlessLab" image: https://source.unsplash.com/400x175/?github description: API docs for the painlessLab plugin -date: 2023-12-06 +date: 2023-12-19 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 51fba6473c48c..5b4629b0ff00c 100644 --- a/api_docs/plugin_directory.mdx +++ b/api_docs/plugin_directory.mdx @@ -7,7 +7,7 @@ id: kibDevDocsPluginDirectory slug: /kibana-dev-docs/api-meta/plugin-api-directory title: Directory description: Directory of public APIs available through plugins or packages. -date: 2023-12-06 +date: 2023-12-19 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 | |--------------|----------|------------------------| -| 731 | 621 | 40 | +| 738 | 628 | 41 | ### Public API health stats | API Count | Any Count | Missing comments | Missing exports | |--------------|----------|-----------------|--------| -| 77390 | 235 | 66122 | 1622 | +| 77945 | 234 | 66664 | 1632 | ## Plugin Directory @@ -32,12 +32,12 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@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/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 822 | 1 | 791 | 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 | 127 | +| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 823 | 1 | 792 | 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 | | | [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs-knowledge-team) | Asset manager plugin for entity assets (inventory, topology, etc) | 9 | 0 | 9 | 2 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 9 | 0 | 9 | 0 | -| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Considering using bfetch capabilities when fetching large amounts of data. This services supports batching HTTP requests and streaming responses back. | 91 | 1 | 75 | 2 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Considering using bfetch capabilities when fetching large amounts of data. This services supports batching HTTP requests and streaming responses back. | 83 | 1 | 73 | 2 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds Canvas application to Kibana | 9 | 0 | 8 | 3 | | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | The Case management system in Kibana | 114 | 0 | 94 | 27 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 268 | 2 | 253 | 10 | @@ -58,27 +58,27 @@ 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 | 109 | 0 | 106 | 11 | | | [@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. | 3197 | 32 | 2545 | 22 | +| | [@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. | 3188 | 31 | 2537 | 22 | | | [@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. | 922 | 0 | 257 | 4 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | The Data Visualizer tools help you understand your data, by analyzing the metrics and fields in a log file or an existing Elasticsearch index. | 31 | 3 | 25 | 1 | -| | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | This plugin introduces the concept of dataset quality, where users can easily get an overview on the datasets they have. | 8 | 0 | 8 | 4 | +| | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | This plugin introduces the concept of dataset quality, where users can easily get an overview on the datasets they have. | 8 | 0 | 8 | 2 | | | [@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. | 134 | 0 | 91 | 20 | +| | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | This plugin contains the Discover application and the saved search embeddable. | 136 | 0 | 91 | 21 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 37 | 0 | 35 | 2 | | | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | APIs used to assess the quality of data in Elasticsearch indexes | 2 | 0 | 0 | 0 | -| | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | Server APIs for the Elastic AI Assistant | 4 | 0 | 2 | 0 | +| | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | Server APIs for the Elastic AI Assistant | 36 | 0 | 28 | 0 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds embeddables service to Kibana | 547 | 1 | 446 | 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. | 51 | 0 | 44 | 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 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 99 | 3 | 97 | 3 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | The Event Annotation service contains expressions for event annotations | 201 | 0 | 201 | 6 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | The listing page for event annotations. | 15 | 0 | 15 | 0 | | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 111 | 0 | 111 | 11 | -| | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 131 | 1 | 131 | 14 | +| | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 130 | 0 | 130 | 14 | | | [@elastic/kibana-presentation](https://github.com/orgs/elastic/teams/kibana-presentation) | Adds 'error' renderer to expressions | 17 | 0 | 15 | 2 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Expression Gauge plugin adds a `gauge` renderer and function to the expression plugin. The renderer will display the `gauge` chart. | 59 | 0 | 59 | 2 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Expression Heatmap plugin adds a `heatmap` renderer and function to the expression plugin. The renderer will display the `heatmap` chart. | 112 | 0 | 108 | 2 | @@ -98,7 +98,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-gis](https://github.com/orgs/elastic/teams/kibana-gis) | The file upload plugin contains components and services for uploading a file, analyzing its data, and then importing the data into an Elasticsearch index. Supported file types include CSV, TSV, newline-delimited JSON and GeoJSON. | 59 | 0 | 59 | 2 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | File upload, download, sharing, and serving over HTTP implementation in Kibana. | 240 | 0 | 24 | 9 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Simple UI for managing files in Kibana | 2 | 0 | 2 | 0 | -| | [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) | - | 1213 | 3 | 1095 | 47 | +| | [@elastic/fleet](https://github.com/orgs/elastic/teams/fleet) | - | 1221 | 3 | 1103 | 48 | | ftrApis | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 0 | 0 | 0 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 68 | 0 | 14 | 5 | | globalSearchBar | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 0 | 0 | 0 | 0 | @@ -118,15 +118,15 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@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) | - | 161 | 0 | 127 | 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) | - | 610 | 3 | 417 | 9 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 611 | 3 | 418 | 9 | | | [@elastic/kibana-cloud-security-posture](https://github.com/orgs/elastic/teams/kibana-cloud-security-posture) | - | 5 | 0 | 5 | 1 | -| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Visualization editor allowing to quickly and easily configure compelling visualizations to use on dashboards and canvas workpads. Exposes components to embed visualizations and link into the Lens editor from within other apps in Kibana. | 632 | 0 | 533 | 60 | +| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Visualization editor allowing to quickly and easily configure compelling visualizations to use on dashboards and canvas workpads. Exposes components to embed visualizations and link into the Lens editor from within other apps in Kibana. | 642 | 0 | 543 | 60 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 8 | 0 | 8 | 0 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 4 | 0 | 4 | 1 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 117 | 0 | 42 | 10 | | | [@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/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | This plugin provides a LogExplorer component using the Discover customization framework, offering several affordances specifically designed for log consumption. | 26 | 0 | 26 | 8 | +| | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | This plugin provides a LogExplorer 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. | 289 | 11 | 274 | 27 | | logstash | [@elastic/logstash](https://github.com/orgs/elastic/teams/logstash) | - | 0 | 0 | 0 | 0 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 45 | 0 | 45 | 7 | @@ -137,13 +137,13 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | - | 25 | 0 | 19 | 0 | | | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 15 | 3 | 13 | 1 | | | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 9 | 0 | 9 | 0 | -| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 35 | 0 | 35 | 2 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 42 | 0 | 42 | 3 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 17 | 0 | 17 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 3 | 0 | 3 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 2 | 0 | 2 | 1 | -| | [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) | - | 599 | 2 | 590 | 17 | -| | [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs-knowledge-team) | - | 91 | 0 | 86 | 9 | -| | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | This plugin exposes and registers observability log consumption features. | 15 | 0 | 15 | 1 | +| | [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) | - | 600 | 2 | 591 | 17 | +| | [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs-knowledge-team) | - | 91 | 0 | 86 | 12 | +| | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | This plugin exposes and registers observability log consumption features. | 18 | 0 | 18 | 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) | - | 307 | 1 | 303 | 15 | | | [@elastic/security-defend-workflows](https://github.com/orgs/elastic/teams/security-defend-workflows) | - | 24 | 0 | 24 | 7 | @@ -189,10 +189,10 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | - | 240 | 1 | 196 | 17 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | This plugin provides access to the transforms features provided by Elastic. Transforms enable you to convert existing Elasticsearch indices into summarized indices, which provide opportunities for new insights and analytics. | 4 | 0 | 4 | 1 | | translations | [@elastic/kibana-localization](https://github.com/orgs/elastic/teams/kibana-localization) | - | 0 | 0 | 0 | 0 | -| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 588 | 1 | 562 | 58 | +| | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 592 | 1 | 566 | 58 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Adds UI Actions service to Kibana | 135 | 0 | 93 | 9 | -| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Extends UI Actions plugin with more functionality | 212 | 0 | 145 | 10 | -| | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | This plugin contains services reliant on the plugin lifecycle for the unified doc viewer component (see @kbn/unified-doc-viewer). | 12 | 0 | 9 | 2 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | Extends UI Actions plugin with more functionality | 212 | 0 | 145 | 11 | +| | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | This plugin contains services reliant on the plugin lifecycle for the unified doc viewer component (see @kbn/unified-doc-viewer). | 10 | 0 | 7 | 2 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | The `unifiedHistogram` plugin provides UI components to create a layout including a resizable histogram and a main display. | 55 | 0 | 23 | 2 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | Contains all the key functionality of Kibana's unified search experience.Contains all the key functionality of Kibana's unified search experience. | 148 | 2 | 110 | 23 | | upgradeAssistant | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 0 | 0 | 0 | 0 | @@ -240,9 +240,10 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 18 | 0 | 2 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 17 | 0 | 17 | 0 | | | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 34 | 0 | 34 | 8 | -| | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 188 | 0 | 188 | 27 | +| | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 188 | 0 | 188 | 28 | | | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 11 | 0 | 11 | 0 | | | [@elastic/kibana-qa](https://github.com/orgs/elastic/teams/kibana-qa) | - | 12 | 0 | 12 | 0 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 4 | 0 | 1 | 0 | | | [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) | - | 10 | 0 | 10 | 0 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 7 | 0 | 7 | 1 | | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 19 | 0 | 16 | 0 | @@ -254,8 +255,9 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@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) | - | 26 | 0 | 9 | 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) | - | 75 | 0 | 46 | 10 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 76 | 0 | 47 | 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 | @@ -393,7 +395,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 4 | 0 | 4 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 125 | 0 | 91 | 47 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 12 | 0 | 12 | 0 | -| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 541 | 1 | 117 | 4 | +| | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 555 | 1 | 130 | 4 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 69 | 0 | 69 | 4 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 14 | 0 | 14 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 36 | 0 | 6 | 0 | @@ -434,7 +436,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 5 | 0 | 5 | 0 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 4 | 0 | 4 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 3 | 0 | 3 | 0 | -| | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | - | 26 | 0 | 16 | 0 | +| | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | - | 31 | 0 | 21 | 0 | | | [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | - | 4 | 0 | 4 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 8 | 0 | 8 | 0 | | | [@elastic/platform-deployment-management](https://github.com/orgs/elastic/teams/platform-deployment-management) | - | 8 | 0 | 8 | 0 | @@ -452,11 +454,12 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 35125 | 0 | 34718 | 0 | | | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | - | 13 | 0 | 5 | 0 | | | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | - | 35 | 0 | 34 | 0 | -| | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | - | 97 | 0 | 77 | 6 | +| | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | - | 98 | 0 | 78 | 6 | +| | [@elastic/security-solution](https://github.com/orgs/elastic/teams/security-solution) | - | 30 | 0 | 30 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 48 | 0 | 33 | 7 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 27 | 0 | 14 | 1 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 7 | 0 | 3 | 0 | -| | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 261 | 1 | 201 | 15 | +| | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 266 | 1 | 206 | 15 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 19 | 0 | 19 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 2 | 0 | 1 | 0 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 39 | 0 | 39 | 0 | @@ -466,6 +469,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 37 | 0 | 29 | 1 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 2 | 0 | 0 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 29 | 0 | 29 | 1 | +| | [@elastic/appex-qa](https://github.com/orgs/elastic/teams/appex-qa) | - | 4 | 0 | 4 | 0 | | | [@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) | - | 10 | 0 | 10 | 1 | @@ -525,10 +529,10 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 5 | 0 | 0 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 8 | 0 | 0 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 2 | 0 | 1 | 0 | -| | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 32 | 0 | 30 | 0 | +| | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 33 | 0 | 28 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 18 | 0 | 18 | 0 | | | [@elastic/ml-ui](https://github.com/orgs/elastic/teams/ml-ui) | - | 31 | 1 | 24 | 1 | -| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 78 | 0 | 76 | 3 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 98 | 0 | 96 | 2 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 55 | 1 | 50 | 0 | | | [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) | - | 10 | 0 | 10 | 2 | | | [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) | - | 99 | 1 | 99 | 0 | @@ -542,7 +546,7 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-performance-testing](https://github.com/orgs/elastic/teams/kibana-performance-testing) | - | 3 | 0 | 3 | 1 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 1 | 0 | 1 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 1 | 0 | 1 | 0 | -| | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 169 | 0 | 51 | 0 | +| | [@elastic/obs-ux-infra_services-team](https://github.com/orgs/elastic/teams/obs-ux-infra_services-team) | - | 163 | 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 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 8 | 0 | 2 | 0 | @@ -557,21 +561,23 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 6 | 0 | 6 | 1 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 80 | 0 | 72 | 7 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 50 | 0 | 50 | 3 | -| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 17 | 0 | 16 | 0 | +| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 23 | 0 | 20 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 32 | 0 | 31 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 14 | 0 | 11 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 16 | 0 | 15 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 13 | 0 | 11 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 2 | 0 | 2 | 0 | -| | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 5 | 0 | 5 | 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) | - | 78 | 0 | 77 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | A component for creating resizable layouts containing a fixed width panel and a flexible panel, with support for horizontal and vertical layouts. | 18 | 0 | 5 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 13 | 2 | 8 | 0 | +| | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | - | 2 | 0 | 1 | 0 | | | [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-ops) | - | 16 | 0 | 16 | 1 | | | [@elastic/security-detections-response](https://github.com/orgs/elastic/teams/security-detections-response) | - | 119 | 0 | 116 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 2 | 0 | 2 | 0 | -| | [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | - | 66 | 0 | 66 | 0 | -| | [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | - | 2378 | 0 | 2378 | 0 | +| | [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | - | 75 | 0 | 75 | 0 | +| | [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | - | 2649 | 0 | 2649 | 0 | +| | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 27 | 1 | 26 | 0 | | | [@elastic/enterprise-search-frontend](https://github.com/orgs/elastic/teams/enterprise-search-frontend) | - | 25 | 0 | 25 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 20 | 0 | 18 | 1 | | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | - | 82 | 0 | 35 | 0 | @@ -646,13 +652,13 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 2 | 0 | 0 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 15 | 0 | 4 | 0 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 11 | 0 | 3 | 0 | -| | [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) | - | 131 | 0 | 128 | 0 | +| | [@elastic/obs-ux-management-team](https://github.com/orgs/elastic/teams/obs-ux-management-team) | - | 144 | 0 | 144 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 20 | 0 | 12 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 102 | 2 | 65 | 1 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 4 | 0 | 2 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 41 | 2 | 21 | 0 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 7 | 0 | 5 | 1 | -| | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 291 | 4 | 244 | 12 | +| | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 311 | 4 | 263 | 12 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 137 | 5 | 105 | 2 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 2 | 0 | 1 | 0 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 25 | 0 | 10 | 0 | @@ -661,10 +667,10 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 39 | 0 | 25 | 1 | | | [@elastic/obs-knowledge-team](https://github.com/orgs/elastic/teams/obs-knowledge-team) | - | 86 | 0 | 86 | 1 | | | [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sharedux) | - | 44 | 0 | 30 | 0 | -| | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 53 | 0 | 44 | 0 | +| | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 54 | 0 | 45 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 7 | 0 | 6 | 0 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | Contains functionality for the unified data table which can be integrated into apps | 109 | 0 | 49 | 1 | -| | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 10 | 0 | 7 | 7 | +| | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 14 | 0 | 13 | 6 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | Contains functionality for the field list and field stats which can be integrated into apps | 285 | 0 | 261 | 9 | | | [@elastic/kibana-data-discovery](https://github.com/orgs/elastic/teams/kibana-data-discovery) | - | 13 | 0 | 9 | 0 | | | [@elastic/security-threat-hunting-investigations](https://github.com/orgs/elastic/teams/security-threat-hunting-investigations) | - | 3 | 0 | 0 | 0 | @@ -672,9 +678,10 @@ tags: ['contributor', 'dev', 'apidocs', 'kibana'] | | [@elastic/kibana-security](https://github.com/orgs/elastic/teams/kibana-security) | - | 80 | 1 | 21 | 2 | | | [@elastic/kibana-core](https://github.com/orgs/elastic/teams/kibana-core) | - | 37 | 0 | 16 | 1 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 2 | 0 | 2 | 0 | -| | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 25 | 0 | 15 | 0 | +| | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 24 | 0 | 14 | 0 | | | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 154 | 0 | 151 | 3 | -| | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | - | 12 | 0 | 12 | 0 | +| | [@elastic/kibana-visualizations](https://github.com/orgs/elastic/teams/kibana-visualizations) | - | 2 | 0 | 1 | 0 | +| | [@elastic/obs-ux-logs-team](https://github.com/orgs/elastic/teams/obs-ux-logs-team) | - | 13 | 0 | 13 | 0 | | | [@elastic/kibana-operations](https://github.com/orgs/elastic/teams/kibana-operations) | - | 6 | 0 | 2 | 0 | | | [@elastic/security-detection-rule-management](https://github.com/orgs/elastic/teams/security-detection-rule-management) | - | 18 | 0 | 9 | 0 | diff --git a/api_docs/presentation_util.mdx b/api_docs/presentation_util.mdx index 1115ac3895761..ed057a0b4f5aa 100644 --- a/api_docs/presentation_util.mdx +++ b/api_docs/presentation_util.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/presentationUtil title: "presentationUtil" image: https://source.unsplash.com/400x175/?github description: API docs for the presentationUtil plugin -date: 2023-12-06 +date: 2023-12-19 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 d962a4b894d6b..c836cb04e869c 100644 --- a/api_docs/profiling.mdx +++ b/api_docs/profiling.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/profiling title: "profiling" image: https://source.unsplash.com/400x175/?github description: API docs for the profiling plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'profiling'] --- import profilingObj from './profiling.devdocs.json'; diff --git a/api_docs/profiling_data_access.devdocs.json b/api_docs/profiling_data_access.devdocs.json index 7a8d4a6ae9ebd..471ec9b401a68 100644 --- a/api_docs/profiling_data_access.devdocs.json +++ b/api_docs/profiling_data_access.devdocs.json @@ -39,7 +39,7 @@ "signature": [ "{ services: { fetchFlamechartData: ({ core, esClient, rangeFromMs, rangeToMs, kuery }: ", "FetchFlamechartParams", - ") => Promise<{ TotalSeconds: number; Size: number; Edges: number[][]; FileID: string[]; FrameType: number[]; Inline: boolean[]; ExeFilename: string[]; AddressOrLine: number[]; FunctionName: string[]; FunctionOffset: number[]; SourceFilename: string[]; SourceLine: number[]; CountInclusive: number[]; CountExclusive: number[]; SamplingRate: number; TotalSamples: number; TotalCPU: number; SelfCPU: number; AnnualCO2TonsExclusive: number[]; AnnualCO2TonsInclusive: number[]; AnnualCostsUSDInclusive: number[]; AnnualCostsUSDExclusive: number[]; SelfAnnualCO2Tons: number; TotalAnnualCO2Tons: number; SelfAnnualCostsUSD: number; TotalAnnualCostsUSD: number; }>; getStatus: ({ esClient, soClient, spaceId }: ", + ") => Promise<{ TotalSeconds: number; Size: number; Edges: number[][]; FileID: string[]; FrameType: number[]; Inline: boolean[]; ExeFilename: string[]; AddressOrLine: number[]; FunctionName: string[]; FunctionOffset: number[]; SourceFilename: string[]; SourceLine: number[]; CountInclusive: number[]; CountExclusive: number[]; SamplingRate: number; TotalSamples: number; TotalCPU: number; SelfCPU: number; AnnualCO2TonsExclusive: number[]; AnnualCO2TonsInclusive: number[]; AnnualCostsUSDInclusive: number[]; AnnualCostsUSDExclusive: number[]; }>; getStatus: ({ esClient, soClient, spaceId }: ", "HasSetupParams", ") => Promise<", { diff --git a/api_docs/profiling_data_access.mdx b/api_docs/profiling_data_access.mdx index 40c879cbaf97d..6a589ce2676af 100644 --- a/api_docs/profiling_data_access.mdx +++ b/api_docs/profiling_data_access.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/profilingDataAccess title: "profilingDataAccess" image: https://source.unsplash.com/400x175/?github description: API docs for the profilingDataAccess plugin -date: 2023-12-06 +date: 2023-12-19 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 4f169d9a1554b..30269acaa86c2 100644 --- a/api_docs/remote_clusters.mdx +++ b/api_docs/remote_clusters.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/remoteClusters title: "remoteClusters" image: https://source.unsplash.com/400x175/?github description: API docs for the remoteClusters plugin -date: 2023-12-06 +date: 2023-12-19 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 4ddd2c69e70b3..7b80f41e0e5fe 100644 --- a/api_docs/reporting.mdx +++ b/api_docs/reporting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/reporting title: "reporting" image: https://source.unsplash.com/400x175/?github description: API docs for the reporting plugin -date: 2023-12-06 +date: 2023-12-19 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 569904473e5c0..e7dda68fbad27 100644 --- a/api_docs/rollup.mdx +++ b/api_docs/rollup.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/rollup title: "rollup" image: https://source.unsplash.com/400x175/?github description: API docs for the rollup plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'rollup'] --- import rollupObj from './rollup.devdocs.json'; diff --git a/api_docs/rule_registry.devdocs.json b/api_docs/rule_registry.devdocs.json index 78d079ce0522c..a24e8ed5ea6d7 100644 --- a/api_docs/rule_registry.devdocs.json +++ b/api_docs/rule_registry.devdocs.json @@ -2315,7 +2315,7 @@ "\nID of the Kibana feature associated with the index.\nUsed by alerts-as-data RBAC.\n\nNote from @dhurley14\nThe purpose of the `feature` param is to force the user to update\nthe data structure which contains the mapping of consumers to alerts\nas data indices. The idea is it is typed such that it forces the\nuser to go to the code and modify it. At least until a better system\nis put in place or we move the alerts as data client out of rule registry.\n" ], "signature": [ - "\"ml\" | \"uptime\" | \"siem\" | \"observability\" | \"apm\" | \"logs\" | \"infrastructure\" | \"slo\"" + "\"ml\" | \"uptime\" | \"siem\" | \"observability\" | \"stackAlerts\" | \"apm\" | \"logs\" | \"infrastructure\" | \"slo\"" ], "path": "x-pack/plugins/rule_registry/server/rule_data_plugin_service/index_options.ts", "deprecated": false, diff --git a/api_docs/rule_registry.mdx b/api_docs/rule_registry.mdx index a70b113e3d83f..939baee853ff3 100644 --- a/api_docs/rule_registry.mdx +++ b/api_docs/rule_registry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ruleRegistry title: "ruleRegistry" image: https://source.unsplash.com/400x175/?github description: API docs for the ruleRegistry plugin -date: 2023-12-06 +date: 2023-12-19 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 463e9666574cb..7ef0873198097 100644 --- a/api_docs/runtime_fields.mdx +++ b/api_docs/runtime_fields.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/runtimeFields title: "runtimeFields" image: https://source.unsplash.com/400x175/?github description: API docs for the runtimeFields plugin -date: 2023-12-06 +date: 2023-12-19 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 d2574e55c41cf..be4b9ffc5ce5e 100644 --- a/api_docs/saved_objects.mdx +++ b/api_docs/saved_objects.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjects title: "savedObjects" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjects plugin -date: 2023-12-06 +date: 2023-12-19 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 cff800f6b0c0b..cfed8116573df 100644 --- a/api_docs/saved_objects_finder.mdx +++ b/api_docs/saved_objects_finder.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsFinder title: "savedObjectsFinder" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsFinder plugin -date: 2023-12-06 +date: 2023-12-19 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 9e54a0e8ac8a5..aa70fd180d11a 100644 --- a/api_docs/saved_objects_management.mdx +++ b/api_docs/saved_objects_management.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsManagement title: "savedObjectsManagement" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsManagement plugin -date: 2023-12-06 +date: 2023-12-19 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 42fd98534ddfb..59754589c9032 100644 --- a/api_docs/saved_objects_tagging.mdx +++ b/api_docs/saved_objects_tagging.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTagging title: "savedObjectsTagging" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTagging plugin -date: 2023-12-06 +date: 2023-12-19 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 58baceba78aa8..abfa72674be23 100644 --- a/api_docs/saved_objects_tagging_oss.mdx +++ b/api_docs/saved_objects_tagging_oss.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedObjectsTaggingOss title: "savedObjectsTaggingOss" image: https://source.unsplash.com/400x175/?github description: API docs for the savedObjectsTaggingOss plugin -date: 2023-12-06 +date: 2023-12-19 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 ee31f097c41af..ccb907e95990f 100644 --- a/api_docs/saved_search.mdx +++ b/api_docs/saved_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/savedSearch title: "savedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the savedSearch plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'savedSearch'] --- import savedSearchObj from './saved_search.devdocs.json'; diff --git a/api_docs/screenshot_mode.devdocs.json b/api_docs/screenshot_mode.devdocs.json index 322f5e1d73132..d5c687d0c57e0 100644 --- a/api_docs/screenshot_mode.devdocs.json +++ b/api_docs/screenshot_mode.devdocs.json @@ -55,26 +55,26 @@ } ], "objects": [], - "setup": { + "start": { "parentPluginId": "screenshotMode", - "id": "def-public.ScreenshotModePluginStart", + "id": "def-public.ScreenshotModePublicStart", "type": "Type", "tags": [], - "label": "ScreenshotModePluginStart", + "label": "ScreenshotModePublicStart", "description": [], "signature": [ { "pluginId": "screenshotMode", "scope": "public", "docId": "kibScreenshotModePluginApi", - "section": "def-public.ScreenshotModePluginSetup", - "text": "ScreenshotModePluginSetup" + "section": "def-public.ScreenshotModePublicSetup", + "text": "ScreenshotModePublicSetup" } ], "path": "src/plugins/screenshot_mode/public/types.ts", "deprecated": false, "trackAdoption": false, - "lifecycle": "setup", + "lifecycle": "start", "initialIsOpen": true } }, @@ -157,26 +157,26 @@ "objects": [], "setup": { "parentPluginId": "screenshotMode", - "id": "def-server.ScreenshotModePluginSetup", + "id": "def-server.ScreenshotModeServerSetup", "type": "Interface", "tags": [], - "label": "ScreenshotModePluginSetup", + "label": "ScreenshotModeServerSetup", "description": [], "signature": [ { "pluginId": "screenshotMode", "scope": "server", "docId": "kibScreenshotModePluginApi", - "section": "def-server.ScreenshotModePluginSetup", - "text": "ScreenshotModePluginSetup" + "section": "def-server.ScreenshotModeServerSetup", + "text": "ScreenshotModeServerSetup" }, " extends ", { "pluginId": "screenshotMode", "scope": "server", "docId": "kibScreenshotModePluginApi", - "section": "def-server.ScreenshotModePluginStart", - "text": "ScreenshotModePluginStart" + "section": "def-server.ScreenshotModeServerStart", + "text": "ScreenshotModeServerStart" } ], "path": "src/plugins/screenshot_mode/server/types.ts", @@ -185,7 +185,7 @@ "children": [ { "parentPluginId": "screenshotMode", - "id": "def-server.ScreenshotModePluginSetup.setScreenshotContext", + "id": "def-server.ScreenshotModeServerSetup.setScreenshotContext", "type": "Function", "tags": [], "label": "setScreenshotContext", @@ -201,7 +201,7 @@ "children": [ { "parentPluginId": "screenshotMode", - "id": "def-server.ScreenshotModePluginSetup.setScreenshotContext.$1", + "id": "def-server.ScreenshotModeServerSetup.setScreenshotContext.$1", "type": "string", "tags": [], "label": "key", @@ -218,7 +218,7 @@ }, { "parentPluginId": "screenshotMode", - "id": "def-server.ScreenshotModePluginSetup.setScreenshotContext.$2", + "id": "def-server.ScreenshotModeServerSetup.setScreenshotContext.$2", "type": "Uncategorized", "tags": [], "label": "value", @@ -238,7 +238,7 @@ }, { "parentPluginId": "screenshotMode", - "id": "def-server.ScreenshotModePluginSetup.setScreenshotModeEnabled", + "id": "def-server.ScreenshotModeServerSetup.setScreenshotModeEnabled", "type": "Function", "tags": [], "label": "setScreenshotModeEnabled", @@ -260,10 +260,10 @@ }, "start": { "parentPluginId": "screenshotMode", - "id": "def-server.ScreenshotModePluginStart", + "id": "def-server.ScreenshotModeServerStart", "type": "Interface", "tags": [], - "label": "ScreenshotModePluginStart", + "label": "ScreenshotModeServerStart", "description": [], "path": "src/plugins/screenshot_mode/server/types.ts", "deprecated": false, @@ -271,7 +271,7 @@ "children": [ { "parentPluginId": "screenshotMode", - "id": "def-server.ScreenshotModePluginStart.isScreenshotMode", + "id": "def-server.ScreenshotModeServerStart.isScreenshotMode", "type": "Function", "tags": [], "label": "isScreenshotMode", @@ -295,7 +295,7 @@ "children": [ { "parentPluginId": "screenshotMode", - "id": "def-server.ScreenshotModePluginStart.isScreenshotMode.$1", + "id": "def-server.ScreenshotModeServerStart.isScreenshotMode.$1", "type": "Object", "tags": [], "label": "request", diff --git a/api_docs/screenshot_mode.mdx b/api_docs/screenshot_mode.mdx index 04a4e8ec1997e..a5ac9e386f2af 100644 --- a/api_docs/screenshot_mode.mdx +++ b/api_docs/screenshot_mode.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotMode title: "screenshotMode" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotMode plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'screenshotMode'] --- import screenshotModeObj from './screenshot_mode.devdocs.json'; @@ -25,8 +25,8 @@ Contact [@elastic/appex-sharedux](https://github.com/orgs/elastic/teams/appex-sh ## Client -### Setup - +### Start + ### Functions diff --git a/api_docs/screenshotting.mdx b/api_docs/screenshotting.mdx index 9e18e06647752..4387682d42e00 100644 --- a/api_docs/screenshotting.mdx +++ b/api_docs/screenshotting.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/screenshotting title: "screenshotting" image: https://source.unsplash.com/400x175/?github description: API docs for the screenshotting plugin -date: 2023-12-06 +date: 2023-12-19 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 1cd99545b49f9..382f2f708ec20 100644 --- a/api_docs/security.mdx +++ b/api_docs/security.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/security title: "security" image: https://source.unsplash.com/400x175/?github description: API docs for the security plugin -date: 2023-12-06 +date: 2023-12-19 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 72d6613ec0ffd..945b6393db345 100644 --- a/api_docs/security_solution.devdocs.json +++ b/api_docs/security_solution.devdocs.json @@ -114,7 +114,7 @@ "label": "experimentalFeatures", "description": [], "signature": [ - "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly alertsPreviewChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly assistantStreamingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly expandableFlyoutInCreateRuleEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly newUserDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly alertSuppressionForThresholdRuleEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly entityAnalyticsAssetCriticalityEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; }" + "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly alertsPreviewChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly assistantStreamingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly expandableFlyoutInCreateRuleEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly assistantRagOnAlerts: boolean; readonly newUserDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly alertSuppressionForThresholdRuleEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly entityAnalyticsAssetCriticalityEnabled: boolean; readonly sentinelOneDataInAnalyzerEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; readonly jsonPrebuiltRulesDiffingEnabled: boolean; }" ], "path": "x-pack/plugins/security_solution/public/plugin.tsx", "deprecated": false, @@ -568,7 +568,7 @@ "\nExperimental flag needed to enable the link" ], "signature": [ - "\"tGridEnabled\" | \"tGridEventRenderedViewEnabled\" | \"excludePoliciesInFilterEnabled\" | \"kubernetesEnabled\" | \"chartEmbeddablesEnabled\" | \"donutChartEmbeddablesEnabled\" | \"alertsPreviewChartEmbeddablesEnabled\" | \"previewTelemetryUrlEnabled\" | \"insightsRelatedAlertsByProcessAncestry\" | \"extendedRuleExecutionLoggingEnabled\" | \"assistantStreamingEnabled\" | \"socTrendsEnabled\" | \"responseActionsEnabled\" | \"endpointResponseActionsEnabled\" | \"responseActionUploadEnabled\" | \"alertsPageChartsEnabled\" | \"alertTypeEnabled\" | \"expandableFlyoutInCreateRuleEnabled\" | \"alertsPageFiltersEnabled\" | \"assistantModelEvaluation\" | \"newUserDetailsFlyout\" | \"riskScoringPersistence\" | \"riskScoringRoutesEnabled\" | \"esqlRulesDisabled\" | \"protectionUpdatesEnabled\" | \"alertSuppressionForThresholdRuleEnabled\" | \"disableTimelineSaveTour\" | \"riskEnginePrivilegesRouteEnabled\" | \"entityAnalyticsAssetCriticalityEnabled\" | \"sentinelOneManualHostActionsEnabled\" | undefined" + "\"tGridEnabled\" | \"tGridEventRenderedViewEnabled\" | \"excludePoliciesInFilterEnabled\" | \"kubernetesEnabled\" | \"chartEmbeddablesEnabled\" | \"donutChartEmbeddablesEnabled\" | \"alertsPreviewChartEmbeddablesEnabled\" | \"previewTelemetryUrlEnabled\" | \"insightsRelatedAlertsByProcessAncestry\" | \"extendedRuleExecutionLoggingEnabled\" | \"assistantStreamingEnabled\" | \"socTrendsEnabled\" | \"responseActionsEnabled\" | \"endpointResponseActionsEnabled\" | \"responseActionUploadEnabled\" | \"alertsPageChartsEnabled\" | \"alertTypeEnabled\" | \"expandableFlyoutInCreateRuleEnabled\" | \"alertsPageFiltersEnabled\" | \"assistantModelEvaluation\" | \"assistantRagOnAlerts\" | \"newUserDetailsFlyout\" | \"riskScoringPersistence\" | \"riskScoringRoutesEnabled\" | \"esqlRulesDisabled\" | \"protectionUpdatesEnabled\" | \"alertSuppressionForThresholdRuleEnabled\" | \"disableTimelineSaveTour\" | \"riskEnginePrivilegesRouteEnabled\" | \"entityAnalyticsAssetCriticalityEnabled\" | \"sentinelOneDataInAnalyzerEnabled\" | \"sentinelOneManualHostActionsEnabled\" | \"jsonPrebuiltRulesDiffingEnabled\" | 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": [ - "\"tGridEnabled\" | \"tGridEventRenderedViewEnabled\" | \"excludePoliciesInFilterEnabled\" | \"kubernetesEnabled\" | \"chartEmbeddablesEnabled\" | \"donutChartEmbeddablesEnabled\" | \"alertsPreviewChartEmbeddablesEnabled\" | \"previewTelemetryUrlEnabled\" | \"insightsRelatedAlertsByProcessAncestry\" | \"extendedRuleExecutionLoggingEnabled\" | \"assistantStreamingEnabled\" | \"socTrendsEnabled\" | \"responseActionsEnabled\" | \"endpointResponseActionsEnabled\" | \"responseActionUploadEnabled\" | \"alertsPageChartsEnabled\" | \"alertTypeEnabled\" | \"expandableFlyoutInCreateRuleEnabled\" | \"alertsPageFiltersEnabled\" | \"assistantModelEvaluation\" | \"newUserDetailsFlyout\" | \"riskScoringPersistence\" | \"riskScoringRoutesEnabled\" | \"esqlRulesDisabled\" | \"protectionUpdatesEnabled\" | \"alertSuppressionForThresholdRuleEnabled\" | \"disableTimelineSaveTour\" | \"riskEnginePrivilegesRouteEnabled\" | \"entityAnalyticsAssetCriticalityEnabled\" | \"sentinelOneManualHostActionsEnabled\" | undefined" + "\"tGridEnabled\" | \"tGridEventRenderedViewEnabled\" | \"excludePoliciesInFilterEnabled\" | \"kubernetesEnabled\" | \"chartEmbeddablesEnabled\" | \"donutChartEmbeddablesEnabled\" | \"alertsPreviewChartEmbeddablesEnabled\" | \"previewTelemetryUrlEnabled\" | \"insightsRelatedAlertsByProcessAncestry\" | \"extendedRuleExecutionLoggingEnabled\" | \"assistantStreamingEnabled\" | \"socTrendsEnabled\" | \"responseActionsEnabled\" | \"endpointResponseActionsEnabled\" | \"responseActionUploadEnabled\" | \"alertsPageChartsEnabled\" | \"alertTypeEnabled\" | \"expandableFlyoutInCreateRuleEnabled\" | \"alertsPageFiltersEnabled\" | \"assistantModelEvaluation\" | \"assistantRagOnAlerts\" | \"newUserDetailsFlyout\" | \"riskScoringPersistence\" | \"riskScoringRoutesEnabled\" | \"esqlRulesDisabled\" | \"protectionUpdatesEnabled\" | \"alertSuppressionForThresholdRuleEnabled\" | \"disableTimelineSaveTour\" | \"riskEnginePrivilegesRouteEnabled\" | \"entityAnalyticsAssetCriticalityEnabled\" | \"sentinelOneDataInAnalyzerEnabled\" | \"sentinelOneManualHostActionsEnabled\" | \"jsonPrebuiltRulesDiffingEnabled\" | undefined" ], "path": "x-pack/plugins/security_solution/public/common/links/types.ts", "deprecated": false, @@ -1913,7 +1913,7 @@ "label": "experimentalFeatures", "description": [], "signature": [ - "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly alertsPreviewChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly assistantStreamingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly expandableFlyoutInCreateRuleEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly newUserDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly alertSuppressionForThresholdRuleEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly entityAnalyticsAssetCriticalityEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; }" + "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly alertsPreviewChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly assistantStreamingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly expandableFlyoutInCreateRuleEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly assistantRagOnAlerts: boolean; readonly newUserDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly alertSuppressionForThresholdRuleEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly entityAnalyticsAssetCriticalityEnabled: boolean; readonly sentinelOneDataInAnalyzerEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; readonly jsonPrebuiltRulesDiffingEnabled: boolean; }" ], "path": "x-pack/plugins/security_solution/public/types.ts", "deprecated": false, @@ -3018,7 +3018,7 @@ "\nThe security solution generic experimental features" ], "signature": [ - "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly alertsPreviewChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly assistantStreamingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly expandableFlyoutInCreateRuleEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly newUserDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly alertSuppressionForThresholdRuleEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly entityAnalyticsAssetCriticalityEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; }" + "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly alertsPreviewChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly assistantStreamingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly expandableFlyoutInCreateRuleEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly assistantRagOnAlerts: boolean; readonly newUserDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly alertSuppressionForThresholdRuleEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly entityAnalyticsAssetCriticalityEnabled: boolean; readonly sentinelOneDataInAnalyzerEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; readonly jsonPrebuiltRulesDiffingEnabled: boolean; }" ], "path": "x-pack/plugins/security_solution/server/plugin_contract.ts", "deprecated": false, @@ -3194,7 +3194,7 @@ "label": "ExperimentalFeatures", "description": [], "signature": [ - "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly alertsPreviewChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly assistantStreamingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly expandableFlyoutInCreateRuleEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly newUserDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly alertSuppressionForThresholdRuleEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly entityAnalyticsAssetCriticalityEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; }" + "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly alertsPreviewChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly assistantStreamingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly expandableFlyoutInCreateRuleEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly assistantRagOnAlerts: boolean; readonly newUserDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly alertSuppressionForThresholdRuleEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly entityAnalyticsAssetCriticalityEnabled: boolean; readonly sentinelOneDataInAnalyzerEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; readonly jsonPrebuiltRulesDiffingEnabled: boolean; }" ], "path": "x-pack/plugins/security_solution/common/experimental_features.ts", "deprecated": false, @@ -3243,7 +3243,7 @@ "\nA list of allowed values that can be used in `xpack.securitySolution.enableExperimental`.\nThis object is then used to validate and parse the value entered." ], "signature": [ - "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly alertsPreviewChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly assistantStreamingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly expandableFlyoutInCreateRuleEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly newUserDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly alertSuppressionForThresholdRuleEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly entityAnalyticsAssetCriticalityEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; }" + "{ readonly tGridEnabled: boolean; readonly tGridEventRenderedViewEnabled: boolean; readonly excludePoliciesInFilterEnabled: boolean; readonly kubernetesEnabled: boolean; readonly chartEmbeddablesEnabled: boolean; readonly donutChartEmbeddablesEnabled: boolean; readonly alertsPreviewChartEmbeddablesEnabled: boolean; readonly previewTelemetryUrlEnabled: boolean; readonly insightsRelatedAlertsByProcessAncestry: boolean; readonly extendedRuleExecutionLoggingEnabled: boolean; readonly assistantStreamingEnabled: boolean; readonly socTrendsEnabled: boolean; readonly responseActionsEnabled: boolean; readonly endpointResponseActionsEnabled: boolean; readonly responseActionUploadEnabled: boolean; readonly alertsPageChartsEnabled: boolean; readonly alertTypeEnabled: boolean; readonly expandableFlyoutInCreateRuleEnabled: boolean; readonly alertsPageFiltersEnabled: boolean; readonly assistantModelEvaluation: boolean; readonly assistantRagOnAlerts: boolean; readonly newUserDetailsFlyout: boolean; readonly riskScoringPersistence: boolean; readonly riskScoringRoutesEnabled: boolean; readonly esqlRulesDisabled: boolean; readonly protectionUpdatesEnabled: boolean; readonly alertSuppressionForThresholdRuleEnabled: boolean; readonly disableTimelineSaveTour: boolean; readonly riskEnginePrivilegesRouteEnabled: boolean; readonly entityAnalyticsAssetCriticalityEnabled: boolean; readonly sentinelOneDataInAnalyzerEnabled: boolean; readonly sentinelOneManualHostActionsEnabled: boolean; readonly jsonPrebuiltRulesDiffingEnabled: boolean; }" ], "path": "x-pack/plugins/security_solution/common/experimental_features.ts", "deprecated": false, diff --git a/api_docs/security_solution.mdx b/api_docs/security_solution.mdx index f99cfcd925a6e..11de42d6c6e15 100644 --- a/api_docs/security_solution.mdx +++ b/api_docs/security_solution.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolution title: "securitySolution" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolution plugin -date: 2023-12-06 +date: 2023-12-19 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 fde4f3515ad24..3d3df5185d3e4 100644 --- a/api_docs/security_solution_ess.mdx +++ b/api_docs/security_solution_ess.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolutionEss title: "securitySolutionEss" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolutionEss plugin -date: 2023-12-06 +date: 2023-12-19 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 9239f5ba2909f..c56836a2b3f9e 100644 --- a/api_docs/security_solution_serverless.mdx +++ b/api_docs/security_solution_serverless.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/securitySolutionServerless title: "securitySolutionServerless" image: https://source.unsplash.com/400x175/?github description: API docs for the securitySolutionServerless plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'securitySolutionServerless'] --- import securitySolutionServerlessObj from './security_solution_serverless.devdocs.json'; diff --git a/api_docs/serverless.devdocs.json b/api_docs/serverless.devdocs.json index a3e8cfec129a8..53843f88f7fdf 100644 --- a/api_docs/serverless.devdocs.json +++ b/api_docs/serverless.devdocs.json @@ -290,10 +290,10 @@ "objects": [], "setup": { "parentPluginId": "serverless", - "id": "def-server.ServerlessPluginSetup", + "id": "def-server.ServerlessServerSetup", "type": "Interface", "tags": [], - "label": "ServerlessPluginSetup", + "label": "ServerlessServerSetup", "description": [], "path": "x-pack/plugins/serverless/server/types.ts", "deprecated": false, @@ -301,7 +301,7 @@ "children": [ { "parentPluginId": "serverless", - "id": "def-server.ServerlessPluginSetup.setupProjectSettings", + "id": "def-server.ServerlessServerSetup.setupProjectSettings", "type": "Function", "tags": [], "label": "setupProjectSettings", @@ -315,7 +315,7 @@ "children": [ { "parentPluginId": "serverless", - "id": "def-server.ServerlessPluginSetup.setupProjectSettings.$1", + "id": "def-server.ServerlessServerSetup.setupProjectSettings.$1", "type": "Array", "tags": [], "label": "keys", @@ -337,10 +337,10 @@ }, "start": { "parentPluginId": "serverless", - "id": "def-server.ServerlessPluginStart", + "id": "def-server.ServerlessServerStart", "type": "Interface", "tags": [], - "label": "ServerlessPluginStart", + "label": "ServerlessServerStart", "description": [], "path": "x-pack/plugins/serverless/server/types.ts", "deprecated": false, diff --git a/api_docs/serverless.mdx b/api_docs/serverless.mdx index ed9ce1c772a2f..66d91140c9a82 100644 --- a/api_docs/serverless.mdx +++ b/api_docs/serverless.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverless title: "serverless" image: https://source.unsplash.com/400x175/?github description: API docs for the serverless plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'serverless'] --- import serverlessObj from './serverless.devdocs.json'; diff --git a/api_docs/serverless_observability.devdocs.json b/api_docs/serverless_observability.devdocs.json index caecd1a34bae8..27cfb7e4ed1a4 100644 --- a/api_docs/serverless_observability.devdocs.json +++ b/api_docs/serverless_observability.devdocs.json @@ -9,10 +9,10 @@ "objects": [], "setup": { "parentPluginId": "serverlessObservability", - "id": "def-public.ServerlessObservabilityPluginSetup", + "id": "def-public.ServerlessObservabilityPublicSetup", "type": "Interface", "tags": [], - "label": "ServerlessObservabilityPluginSetup", + "label": "ServerlessObservabilityPublicSetup", "description": [], "path": "x-pack/plugins/serverless_observability/public/types.ts", "deprecated": false, @@ -23,10 +23,10 @@ }, "start": { "parentPluginId": "serverlessObservability", - "id": "def-public.ServerlessObservabilityPluginStart", + "id": "def-public.ServerlessObservabilityPublicStart", "type": "Interface", "tags": [], - "label": "ServerlessObservabilityPluginStart", + "label": "ServerlessObservabilityPublicStart", "description": [], "path": "x-pack/plugins/serverless_observability/public/types.ts", "deprecated": false, diff --git a/api_docs/serverless_observability.mdx b/api_docs/serverless_observability.mdx index f58248d1a6101..bff7ebf553d98 100644 --- a/api_docs/serverless_observability.mdx +++ b/api_docs/serverless_observability.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverlessObservability title: "serverlessObservability" image: https://source.unsplash.com/400x175/?github description: API docs for the serverlessObservability plugin -date: 2023-12-06 +date: 2023-12-19 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 14642016247c2..2eaf6275c8683 100644 --- a/api_docs/serverless_search.mdx +++ b/api_docs/serverless_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/serverlessSearch title: "serverlessSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the serverlessSearch plugin -date: 2023-12-06 +date: 2023-12-19 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 72fca65dcd686..4dd9c30b26cd7 100644 --- a/api_docs/session_view.mdx +++ b/api_docs/session_view.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/sessionView title: "sessionView" image: https://source.unsplash.com/400x175/?github description: API docs for the sessionView plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'sessionView'] --- import sessionViewObj from './session_view.devdocs.json'; diff --git a/api_docs/share.devdocs.json b/api_docs/share.devdocs.json index 53e4bd286a6ac..3446a1bec0580 100644 --- a/api_docs/share.devdocs.json +++ b/api_docs/share.devdocs.json @@ -1379,10 +1379,10 @@ "objects": [], "setup": { "parentPluginId": "share", - "id": "def-public.SharePluginSetup", + "id": "def-public.SharePublicSetup", "type": "Type", "tags": [], - "label": "SharePluginSetup", + "label": "SharePublicSetup", "description": [], "signature": [ "{ register: (shareMenuProvider: ", @@ -1429,10 +1429,10 @@ }, "start": { "parentPluginId": "share", - "id": "def-public.SharePluginStart", + "id": "def-public.SharePublicStart", "type": "Type", "tags": [], - "label": "SharePluginStart", + "label": "SharePublicStart", "description": [], "signature": [ "{ toggleShareContextMenu: (options: ", @@ -1510,10 +1510,10 @@ "objects": [], "setup": { "parentPluginId": "share", - "id": "def-server.SharePluginSetup", + "id": "def-server.SharePublicSetup", "type": "Interface", "tags": [], - "label": "SharePluginSetup", + "label": "SharePublicSetup", "description": [], "path": "src/plugins/share/server/plugin.ts", "deprecated": false, @@ -1521,7 +1521,7 @@ "children": [ { "parentPluginId": "share", - "id": "def-server.SharePluginSetup.url", + "id": "def-server.SharePublicSetup.url", "type": "Object", "tags": [], "label": "url", @@ -1544,10 +1544,10 @@ }, "start": { "parentPluginId": "share", - "id": "def-server.SharePluginStart", + "id": "def-server.SharePublicStart", "type": "Interface", "tags": [], - "label": "SharePluginStart", + "label": "SharePublicStart", "description": [], "path": "src/plugins/share/server/plugin.ts", "deprecated": false, @@ -1555,7 +1555,7 @@ "children": [ { "parentPluginId": "share", - "id": "def-server.SharePluginStart.url", + "id": "def-server.SharePublicStart.url", "type": "Object", "tags": [], "label": "url", diff --git a/api_docs/share.mdx b/api_docs/share.mdx index 7befc797bfa7b..cace61074af40 100644 --- a/api_docs/share.mdx +++ b/api_docs/share.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/share title: "share" image: https://source.unsplash.com/400x175/?github description: API docs for the share plugin -date: 2023-12-06 +date: 2023-12-19 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 b1f449fc7c2f9..2b9c2b1c5ba2e 100644 --- a/api_docs/snapshot_restore.mdx +++ b/api_docs/snapshot_restore.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/snapshotRestore title: "snapshotRestore" image: https://source.unsplash.com/400x175/?github description: API docs for the snapshotRestore plugin -date: 2023-12-06 +date: 2023-12-19 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 be25ce8e8c91a..85cc6c588b490 100644 --- a/api_docs/spaces.mdx +++ b/api_docs/spaces.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/spaces title: "spaces" image: https://source.unsplash.com/400x175/?github description: API docs for the spaces plugin -date: 2023-12-06 +date: 2023-12-19 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 54385ac0bdeac..da709cb8a10f7 100644 --- a/api_docs/stack_alerts.mdx +++ b/api_docs/stack_alerts.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackAlerts title: "stackAlerts" image: https://source.unsplash.com/400x175/?github description: API docs for the stackAlerts plugin -date: 2023-12-06 +date: 2023-12-19 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 920dad1bba1cc..245fd16ac6034 100644 --- a/api_docs/stack_connectors.mdx +++ b/api_docs/stack_connectors.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/stackConnectors title: "stackConnectors" image: https://source.unsplash.com/400x175/?github description: API docs for the stackConnectors plugin -date: 2023-12-06 +date: 2023-12-19 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 5ea85b22b9b25..6c9bb7b1dc5ca 100644 --- a/api_docs/task_manager.mdx +++ b/api_docs/task_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/taskManager title: "taskManager" image: https://source.unsplash.com/400x175/?github description: API docs for the taskManager plugin -date: 2023-12-06 +date: 2023-12-19 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 9149a76e8e593..8846f5ba1a25f 100644 --- a/api_docs/telemetry.mdx +++ b/api_docs/telemetry.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetry title: "telemetry" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetry plugin -date: 2023-12-06 +date: 2023-12-19 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 08a4096670996..aa3e1742b08f1 100644 --- a/api_docs/telemetry_collection_manager.mdx +++ b/api_docs/telemetry_collection_manager.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionManager title: "telemetryCollectionManager" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionManager plugin -date: 2023-12-06 +date: 2023-12-19 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 0b22b664d3f4b..cd71b1a1ca109 100644 --- a/api_docs/telemetry_collection_xpack.mdx +++ b/api_docs/telemetry_collection_xpack.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryCollectionXpack title: "telemetryCollectionXpack" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryCollectionXpack plugin -date: 2023-12-06 +date: 2023-12-19 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 6b92a4013a2ca..8b3d7d8194850 100644 --- a/api_docs/telemetry_management_section.mdx +++ b/api_docs/telemetry_management_section.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/telemetryManagementSection title: "telemetryManagementSection" image: https://source.unsplash.com/400x175/?github description: API docs for the telemetryManagementSection plugin -date: 2023-12-06 +date: 2023-12-19 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 41394052543ad..92f7b53ebb2c7 100644 --- a/api_docs/text_based_languages.mdx +++ b/api_docs/text_based_languages.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/textBasedLanguages title: "textBasedLanguages" image: https://source.unsplash.com/400x175/?github description: API docs for the textBasedLanguages plugin -date: 2023-12-06 +date: 2023-12-19 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 f54033be09c04..5942395bbc829 100644 --- a/api_docs/threat_intelligence.mdx +++ b/api_docs/threat_intelligence.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/threatIntelligence title: "threatIntelligence" image: https://source.unsplash.com/400x175/?github description: API docs for the threatIntelligence plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'threatIntelligence'] --- import threatIntelligenceObj from './threat_intelligence.devdocs.json'; diff --git a/api_docs/timelines.devdocs.json b/api_docs/timelines.devdocs.json index 58e0918547bdf..2e0f4f47ba19a 100644 --- a/api_docs/timelines.devdocs.json +++ b/api_docs/timelines.devdocs.json @@ -4508,7 +4508,7 @@ "section": "def-common.DeprecatedRowRenderer", "text": "DeprecatedRowRenderer" }, - "[] | undefined; setFlyoutAlert?: ((data: any) => void) | undefined; scopeId: string; truncate?: boolean | undefined; key?: string | undefined; closeCellPopover?: (() => void) | undefined; enableActions?: boolean | undefined; }" + "[] | undefined; setFlyoutAlert?: ((alertId: string) => void) | undefined; scopeId: string; truncate?: boolean | undefined; key?: string | undefined; closeCellPopover?: (() => void) | undefined; enableActions?: boolean | undefined; }" ], "path": "x-pack/plugins/timelines/common/types/timeline/cells/index.ts", "deprecated": true, diff --git a/api_docs/timelines.mdx b/api_docs/timelines.mdx index a05a72bf63f72..8d1f14dc7e667 100644 --- a/api_docs/timelines.mdx +++ b/api_docs/timelines.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/timelines title: "timelines" image: https://source.unsplash.com/400x175/?github description: API docs for the timelines plugin -date: 2023-12-06 +date: 2023-12-19 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 55970aaafd7df..337d19283987e 100644 --- a/api_docs/transform.mdx +++ b/api_docs/transform.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/transform title: "transform" image: https://source.unsplash.com/400x175/?github description: API docs for the transform plugin -date: 2023-12-06 +date: 2023-12-19 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 48c76c727aff0..e33cbc84596e9 100644 --- a/api_docs/triggers_actions_ui.devdocs.json +++ b/api_docs/triggers_actions_ui.devdocs.json @@ -177,7 +177,15 @@ "label": "start", "description": [], "signature": [ - "() => ", + "(_: ", + { + "pluginId": "@kbn/core-lifecycle-browser", + "scope": "common", + "docId": "kibKbnCoreLifecycleBrowserPluginApi", + "section": "def-common.CoreStart", + "text": "CoreStart" + }, + ", plugins: PluginsStart) => ", { "pluginId": "triggersActionsUi", "scope": "public", @@ -189,7 +197,44 @@ "path": "x-pack/plugins/triggers_actions_ui/public/plugin.ts", "deprecated": false, "trackAdoption": false, - "children": [], + "children": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.Plugin.start.$1", + "type": "Object", + "tags": [], + "label": "_", + "description": [], + "signature": [ + { + "pluginId": "@kbn/core-lifecycle-browser", + "scope": "common", + "docId": "kibKbnCoreLifecycleBrowserPluginApi", + "section": "def-common.CoreStart", + "text": "CoreStart" + } + ], + "path": "x-pack/plugins/triggers_actions_ui/public/plugin.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.Plugin.start.$2", + "type": "Object", + "tags": [], + "label": "plugins", + "description": [], + "signature": [ + "PluginsStart" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/plugin.ts", + "deprecated": false, + "trackAdoption": false, + "isRequired": true + } + ], "returnComment": [] }, { @@ -5870,6 +5915,36 @@ "path": "x-pack/plugins/triggers_actions_ui/public/application/app.tsx", "deprecated": false, "trackAdoption": false + }, + { + "parentPluginId": "triggersActionsUi", + "id": "def-public.TriggersAndActionsUiServices.fieldFormats", + "type": "CompoundType", + "tags": [], + "label": "fieldFormats", + "description": [], + "signature": [ + "Omit<", + { + "pluginId": "fieldFormats", + "scope": "common", + "docId": "kibFieldFormatsPluginApi", + "section": "def-common.FieldFormatsRegistry", + "text": "FieldFormatsRegistry" + }, + ", \"init\" | \"register\"> & { deserialize: ", + { + "pluginId": "fieldFormats", + "scope": "common", + "docId": "kibFieldFormatsPluginApi", + "section": "def-common.FormatFactory", + "text": "FormatFactory" + }, + "; }" + ], + "path": "x-pack/plugins/triggers_actions_ui/public/application/app.tsx", + "deprecated": false, + "trackAdoption": false } ], "initialIsOpen": false @@ -9896,6 +9971,18 @@ ], "enums": [], "misc": [ + { + "parentPluginId": "triggersActionsUi", + "id": "def-common.ALERT_TABLE_GENERIC_CONFIG_ID", + "type": "string", + "tags": [], + "label": "ALERT_TABLE_GENERIC_CONFIG_ID", + "description": [], + "path": "x-pack/plugins/triggers_actions_ui/common/alert_config.ts", + "deprecated": false, + "trackAdoption": false, + "initialIsOpen": false + }, { "parentPluginId": "triggersActionsUi", "id": "def-common.BASE_TRIGGERS_ACTIONS_UI_API_PATH", diff --git a/api_docs/triggers_actions_ui.mdx b/api_docs/triggers_actions_ui.mdx index faad24c161a57..4ebd2dbdbb494 100644 --- a/api_docs/triggers_actions_ui.mdx +++ b/api_docs/triggers_actions_ui.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/triggersActionsUi title: "triggersActionsUi" image: https://source.unsplash.com/400x175/?github description: API docs for the triggersActionsUi plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'triggersActionsUi'] --- import triggersActionsUiObj from './triggers_actions_ui.devdocs.json'; @@ -21,7 +21,7 @@ Contact [@elastic/response-ops](https://github.com/orgs/elastic/teams/response-o | Public API count | Any count | Items lacking comments | Missing exports | |-------------------|-----------|------------------------|-----------------| -| 588 | 1 | 562 | 58 | +| 592 | 1 | 566 | 58 | ## Client diff --git a/api_docs/ui_actions.devdocs.json b/api_docs/ui_actions.devdocs.json index bf583998fc74e..95d88333a2d23 100644 --- a/api_docs/ui_actions.devdocs.json +++ b/api_docs/ui_actions.devdocs.json @@ -2517,10 +2517,10 @@ ], "setup": { "parentPluginId": "uiActions", - "id": "def-public.UiActionsSetup", + "id": "def-public.UiActionsPublicSetup", "type": "Type", "tags": [], - "label": "UiActionsSetup", + "label": "UiActionsPublicSetup", "description": [], "signature": [ "{ readonly registerTrigger: (trigger: ", @@ -2565,10 +2565,10 @@ }, "start": { "parentPluginId": "uiActions", - "id": "def-public.UiActionsStart", + "id": "def-public.UiActionsPublicStart", "type": "Type", "tags": [], - "label": "UiActionsStart", + "label": "UiActionsPublicStart", "description": [], "signature": [ "{ readonly registerTrigger: (trigger: ", diff --git a/api_docs/ui_actions.mdx b/api_docs/ui_actions.mdx index b95d756d2c1cc..4be2c7d874ecc 100644 --- a/api_docs/ui_actions.mdx +++ b/api_docs/ui_actions.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActions title: "uiActions" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActions plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActions'] --- import uiActionsObj from './ui_actions.devdocs.json'; diff --git a/api_docs/ui_actions_enhanced.devdocs.json b/api_docs/ui_actions_enhanced.devdocs.json index 83c71b1e3e655..7eeb5a7446abf 100644 --- a/api_docs/ui_actions_enhanced.devdocs.json +++ b/api_docs/ui_actions_enhanced.devdocs.json @@ -3698,8 +3698,8 @@ "pluginId": "uiActions", "scope": "public", "docId": "kibUiActionsPluginApi", - "section": "def-public.UiActionsSetup", - "text": "UiActionsSetup" + "section": "def-public.UiActionsPublicSetup", + "text": "UiActionsPublicSetup" }, ",Pick<", "UiActionsServiceEnhancements", @@ -3732,8 +3732,8 @@ "pluginId": "uiActions", "scope": "public", "docId": "kibUiActionsPluginApi", - "section": "def-public.UiActionsStart", - "text": "UiActionsStart" + "section": "def-public.UiActionsPublicStart", + "text": "UiActionsPublicStart" }, ",Pick<", "UiActionsServiceEnhancements", @@ -3825,10 +3825,12 @@ "pluginId": "uiActionsEnhanced", "scope": "server", "docId": "kibUiActionsEnhancedPluginApi", - "section": "def-server.SetupContract", - "text": "SetupContract" + "section": "def-server.UiActionsEnhancedServerSetup", + "text": "UiActionsEnhancedServerSetup" }, - ", void, SetupDependencies, object>" + ", void, UiActionsEnhancedServerSetupDependencies, ", + "UiActionsEnhancedServerStartDependencies", + ">" ], "path": "src/plugins/ui_actions_enhanced/server/plugin.ts", "deprecated": false, @@ -3888,7 +3890,7 @@ "label": "setup", "description": [], "signature": [ - "(core: ", + "(_core: ", { "pluginId": "@kbn/core-lifecycle-server", "scope": "common", @@ -3896,7 +3898,7 @@ "section": "def-common.CoreSetup", "text": "CoreSetup" }, - ", { embeddable }: SetupDependencies) => { registerActionFactory: (definition: ", + ", { embeddable }: UiActionsEnhancedServerSetupDependencies) => { registerActionFactory: (definition: ", { "pluginId": "uiActionsEnhanced", "scope": "server", @@ -3923,7 +3925,7 @@ "id": "def-server.AdvancedUiActionsServerPlugin.setup.$1", "type": "Object", "tags": [], - "label": "core", + "label": "_core", "description": [], "signature": [ { @@ -3948,7 +3950,7 @@ "label": "{ embeddable }", "description": [], "signature": [ - "SetupDependencies" + "UiActionsEnhancedServerSetupDependencies" ], "path": "src/plugins/ui_actions_enhanced/server/plugin.ts", "deprecated": false, @@ -4246,10 +4248,10 @@ "objects": [], "setup": { "parentPluginId": "uiActionsEnhanced", - "id": "def-server.SetupContract", + "id": "def-server.UiActionsEnhancedServerSetup", "type": "Interface", "tags": [], - "label": "SetupContract", + "label": "UiActionsEnhancedServerSetup", "description": [], "path": "src/plugins/ui_actions_enhanced/server/plugin.ts", "deprecated": false, @@ -4257,7 +4259,7 @@ "children": [ { "parentPluginId": "uiActionsEnhanced", - "id": "def-server.SetupContract.registerActionFactory", + "id": "def-server.UiActionsEnhancedServerSetup.registerActionFactory", "type": "Function", "tags": [], "label": "registerActionFactory", @@ -4287,7 +4289,7 @@ "children": [ { "parentPluginId": "uiActionsEnhanced", - "id": "def-server.SetupContract.registerActionFactory.$1", + "id": "def-server.UiActionsEnhancedServerSetup.registerActionFactory.$1", "type": "Object", "tags": [], "label": "definition", @@ -4324,10 +4326,10 @@ }, "start": { "parentPluginId": "uiActionsEnhanced", - "id": "def-server.StartContract", + "id": "def-server.UiActionsEnhancedServerStart", "type": "Type", "tags": [], - "label": "StartContract", + "label": "UiActionsEnhancedServerStart", "description": [], "signature": [ "void" diff --git a/api_docs/ui_actions_enhanced.mdx b/api_docs/ui_actions_enhanced.mdx index 890a90f593b33..5120a5a6eb086 100644 --- a/api_docs/ui_actions_enhanced.mdx +++ b/api_docs/ui_actions_enhanced.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uiActionsEnhanced title: "uiActionsEnhanced" image: https://source.unsplash.com/400x175/?github description: API docs for the uiActionsEnhanced plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'uiActionsEnhanced'] --- import uiActionsEnhancedObj from './ui_actions_enhanced.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 | |-------------------|-----------|------------------------|-----------------| -| 212 | 0 | 145 | 10 | +| 212 | 0 | 145 | 11 | ## Client diff --git a/api_docs/unified_doc_viewer.devdocs.json b/api_docs/unified_doc_viewer.devdocs.json index 28e674ad58441..a72cb5053802f 100644 --- a/api_docs/unified_doc_viewer.devdocs.json +++ b/api_docs/unified_doc_viewer.devdocs.json @@ -152,40 +152,23 @@ "children": [ { "parentPluginId": "unifiedDocViewer", - "id": "def-public.UnifiedDocViewerSetup.addDocView", - "type": "Function", + "id": "def-public.UnifiedDocViewerSetup.registry", + "type": "Object", "tags": [], - "label": "addDocView", + "label": "registry", "description": [], "signature": [ - "(docViewRaw: ", - "DocViewInput", - " | ", - "DocViewInputFn", - ") => void" + { + "pluginId": "@kbn/unified-doc-viewer", + "scope": "common", + "docId": "kibKbnUnifiedDocViewerPluginApi", + "section": "def-common.DocViewsRegistry", + "text": "DocViewsRegistry" + } ], "path": "src/plugins/unified_doc_viewer/public/plugin.tsx", "deprecated": false, - "trackAdoption": false, - "returnComment": [], - "children": [ - { - "parentPluginId": "unifiedDocViewer", - "id": "def-public.UnifiedDocViewerSetup.addDocView.$1", - "type": "CompoundType", - "tags": [], - "label": "docViewRaw", - "description": [], - "signature": [ - "DocViewInput", - " | ", - "DocViewInputFn" - ], - "path": "packages/kbn-unified-doc-viewer/src/services/doc_views_registry.ts", - "deprecated": false, - "trackAdoption": false - } - ] + "trackAdoption": false } ], "lifecycle": "setup", @@ -204,38 +187,23 @@ "children": [ { "parentPluginId": "unifiedDocViewer", - "id": "def-public.UnifiedDocViewerStart.getDocViews", - "type": "Function", + "id": "def-public.UnifiedDocViewerStart.registry", + "type": "Object", "tags": [], - "label": "getDocViews", + "label": "registry", "description": [], "signature": [ - "(hit: ", - "DataTableRecord", - ") => ", - "DocView", - "[]" + { + "pluginId": "@kbn/unified-doc-viewer", + "scope": "common", + "docId": "kibKbnUnifiedDocViewerPluginApi", + "section": "def-common.DocViewsRegistry", + "text": "DocViewsRegistry" + } ], "path": "src/plugins/unified_doc_viewer/public/plugin.tsx", "deprecated": false, - "trackAdoption": false, - "returnComment": [], - "children": [ - { - "parentPluginId": "unifiedDocViewer", - "id": "def-public.UnifiedDocViewerStart.getDocViews.$1", - "type": "Object", - "tags": [], - "label": "hit", - "description": [], - "signature": [ - "DataTableRecord" - ], - "path": "packages/kbn-unified-doc-viewer/src/services/doc_views_registry.ts", - "deprecated": false, - "trackAdoption": false - } - ] + "trackAdoption": false } ], "lifecycle": "start", diff --git a/api_docs/unified_doc_viewer.mdx b/api_docs/unified_doc_viewer.mdx index d1fe9b2f53711..28f070fe36038 100644 --- a/api_docs/unified_doc_viewer.mdx +++ b/api_docs/unified_doc_viewer.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedDocViewer title: "unifiedDocViewer" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedDocViewer plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'unifiedDocViewer'] --- import unifiedDocViewerObj from './unified_doc_viewer.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 | |-------------------|-----------|------------------------|-----------------| -| 12 | 0 | 9 | 2 | +| 10 | 0 | 7 | 2 | ## Client diff --git a/api_docs/unified_histogram.mdx b/api_docs/unified_histogram.mdx index 1e39061586555..697fbffe5c3b7 100644 --- a/api_docs/unified_histogram.mdx +++ b/api_docs/unified_histogram.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedHistogram title: "unifiedHistogram" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedHistogram plugin -date: 2023-12-06 +date: 2023-12-19 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 ad26f33477a00..b898a070d1924 100644 --- a/api_docs/unified_search.mdx +++ b/api_docs/unified_search.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch title: "unifiedSearch" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch plugin -date: 2023-12-06 +date: 2023-12-19 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 a2deaf6ef70ae..9b68141f8522f 100644 --- a/api_docs/unified_search_autocomplete.mdx +++ b/api_docs/unified_search_autocomplete.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/unifiedSearch-autocomplete title: "unifiedSearch.autocomplete" image: https://source.unsplash.com/400x175/?github description: API docs for the unifiedSearch.autocomplete plugin -date: 2023-12-06 +date: 2023-12-19 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 206afd7728756..82eec26d156ba 100644 --- a/api_docs/uptime.mdx +++ b/api_docs/uptime.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/uptime title: "uptime" image: https://source.unsplash.com/400x175/?github description: API docs for the uptime plugin -date: 2023-12-06 +date: 2023-12-19 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 2fd2061cacafe..bff9ba3c43a1b 100644 --- a/api_docs/url_forwarding.mdx +++ b/api_docs/url_forwarding.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/urlForwarding title: "urlForwarding" image: https://source.unsplash.com/400x175/?github description: API docs for the urlForwarding plugin -date: 2023-12-06 +date: 2023-12-19 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 3e53563cf9028..7aea5969874bc 100644 --- a/api_docs/usage_collection.mdx +++ b/api_docs/usage_collection.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/usageCollection title: "usageCollection" image: https://source.unsplash.com/400x175/?github description: API docs for the usageCollection plugin -date: 2023-12-06 +date: 2023-12-19 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 1a08a4d89f21c..f8873435bea7a 100644 --- a/api_docs/ux.mdx +++ b/api_docs/ux.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/ux title: "ux" image: https://source.unsplash.com/400x175/?github description: API docs for the ux plugin -date: 2023-12-06 +date: 2023-12-19 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 6ee35f4a09012..2b8600f50197c 100644 --- a/api_docs/vis_default_editor.mdx +++ b/api_docs/vis_default_editor.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visDefaultEditor title: "visDefaultEditor" image: https://source.unsplash.com/400x175/?github description: API docs for the visDefaultEditor plugin -date: 2023-12-06 +date: 2023-12-19 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 8c801bdf15fdd..f79890da6271f 100644 --- a/api_docs/vis_type_gauge.mdx +++ b/api_docs/vis_type_gauge.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeGauge title: "visTypeGauge" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeGauge plugin -date: 2023-12-06 +date: 2023-12-19 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 e8bfc54c56527..c03a6f74fe8cc 100644 --- a/api_docs/vis_type_heatmap.mdx +++ b/api_docs/vis_type_heatmap.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeHeatmap title: "visTypeHeatmap" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeHeatmap plugin -date: 2023-12-06 +date: 2023-12-19 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 374c8e0181eab..442d2d5f72c74 100644 --- a/api_docs/vis_type_pie.mdx +++ b/api_docs/vis_type_pie.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypePie title: "visTypePie" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypePie plugin -date: 2023-12-06 +date: 2023-12-19 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 5ac9b78ee2695..ed6a569057afe 100644 --- a/api_docs/vis_type_table.mdx +++ b/api_docs/vis_type_table.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTable title: "visTypeTable" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTable plugin -date: 2023-12-06 +date: 2023-12-19 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 251fc4d204892..87bfde401b952 100644 --- a/api_docs/vis_type_timelion.mdx +++ b/api_docs/vis_type_timelion.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimelion title: "visTypeTimelion" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimelion plugin -date: 2023-12-06 +date: 2023-12-19 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 273a29683e905..b7a9e411e74d6 100644 --- a/api_docs/vis_type_timeseries.mdx +++ b/api_docs/vis_type_timeseries.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeTimeseries title: "visTypeTimeseries" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeTimeseries plugin -date: 2023-12-06 +date: 2023-12-19 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 244745816d5f8..4108de2dd9ff4 100644 --- a/api_docs/vis_type_vega.mdx +++ b/api_docs/vis_type_vega.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVega title: "visTypeVega" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVega plugin -date: 2023-12-06 +date: 2023-12-19 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 e74e70bd54899..753c3bfe5e715 100644 --- a/api_docs/vis_type_vislib.mdx +++ b/api_docs/vis_type_vislib.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeVislib title: "visTypeVislib" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeVislib plugin -date: 2023-12-06 +date: 2023-12-19 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 231ba1f7c2e8c..76b4076f2d5ab 100644 --- a/api_docs/vis_type_xy.mdx +++ b/api_docs/vis_type_xy.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visTypeXy title: "visTypeXy" image: https://source.unsplash.com/400x175/?github description: API docs for the visTypeXy plugin -date: 2023-12-06 +date: 2023-12-19 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 afb31e9580446..e94e1d35c22d9 100644 --- a/api_docs/visualizations.devdocs.json +++ b/api_docs/visualizations.devdocs.json @@ -6710,15 +6710,7 @@ "section": "def-common.AggregateQuery", "text": "AggregateQuery" }, - " | undefined>; openInspector: () => ", - { - "pluginId": "@kbn/core-mount-utils-browser", - "scope": "common", - "docId": "kibKbnCoreMountUtilsBrowserPluginApi", - "section": "def-common.OverlayRef", - "text": "OverlayRef" - }, - " | undefined; render: (domNode: HTMLElement) => Promise; getFilters: () => Promise<", + " | undefined>; getFilters: () => Promise<", { "pluginId": "@kbn/es-query", "scope": "common", @@ -6726,41 +6718,15 @@ "section": "def-common.Filter", "text": "Filter" }, - "[]>; reportsEmbeddableLoad: () => boolean; getVis: () => ", - { - "pluginId": "visualizations", - "scope": "public", - "docId": "kibVisualizationsPluginApi", - "section": "def-public.Vis", - "text": "Vis" - }, - "<", + "[]>; openInspector: () => ", { - "pluginId": "visualizations", - "scope": "common", - "docId": "kibVisualizationsPluginApi", - "section": "def-common.VisParams", - "text": "VisParams" - }, - ">; getInspectorAdapters: () => ", - { - "pluginId": "inspector", + "pluginId": "@kbn/core-mount-utils-browser", "scope": "common", - "docId": "kibInspectorPluginApi", - "section": "def-common.Adapters", - "text": "Adapters" - }, - " | undefined; transferCustomizationsToUiState: () => void; hasInspector: () => boolean; onContainerLoading: () => void; onContainerData: () => void; onContainerRender: () => void; onContainerError: (error: ", - { - "pluginId": "expressions", - "scope": "public", - "docId": "kibExpressionsPluginApi", - "section": "def-public.ExpressionRenderError", - "text": "ExpressionRenderError" + "docId": "kibKbnCoreMountUtilsBrowserPluginApi", + "section": "def-common.OverlayRef", + "text": "OverlayRef" }, - ") => void; reload: () => Promise; supportedTriggers: () => string[]; getExpressionVariables$: () => ", - "Observable", - " | undefined>; getExpressionVariables: () => Record | undefined; inputIsRefType: (input: ", + " | undefined; render: (domNode: HTMLElement) => Promise; readonly isContainer: boolean; reload: () => Promise; getExplicitInputIsEqual: (lastExplicitInput: Partial<", { "pluginId": "visualizations", "scope": "public", @@ -6768,13 +6734,7 @@ "section": "def-public.VisualizeInput", "text": "VisualizeInput" }, - ") => input is ", - "VisualizeByReferenceInput", - "; getInputAsValueType: () => Promise<", - "VisualizeByValueInput", - ">; getInputAsRefType: () => Promise<", - "VisualizeByReferenceInput", - ">; readonly runtimeId: number; readonly isContainer: boolean; readonly deferEmbeddableLoad: boolean; catchError?: ((error: ", + ">) => Promise; readonly runtimeId: number; readonly deferEmbeddableLoad: boolean; catchError?: ((error: ", { "pluginId": "expressions", "scope": "common", @@ -6790,7 +6750,7 @@ "section": "def-public.EmbeddableAppContext", "text": "EmbeddableAppContext" }, - " | undefined; refreshInputFromParent: () => void; getIsContainer: () => this is ", + " | undefined; reportsEmbeddableLoad: () => boolean; refreshInputFromParent: () => void; getIsContainer: () => this is ", { "pluginId": "embeddable", "scope": "public", @@ -6842,15 +6802,7 @@ "VisualizeOutput", ">>; getOutput: () => Readonly<", "VisualizeOutput", - ">; getExplicitInputIsEqual: (lastExplicitInput: Partial<", - { - "pluginId": "visualizations", - "scope": "public", - "docId": "kibVisualizationsPluginApi", - "section": "def-public.VisualizeInput", - "text": "VisualizeInput" - }, - ">) => Promise; getPersistableInput: () => ", + ">; getPersistableInput: () => ", { "pluginId": "visualizations", "scope": "public", @@ -6922,9 +6874,57 @@ "section": "def-public.VisualizeInput", "text": "VisualizeInput" }, - ">) => void; updateOutput: (outputChanges: Partial<", + ">) => void; getInspectorAdapters: () => ", + { + "pluginId": "inspector", + "scope": "common", + "docId": "kibInspectorPluginApi", + "section": "def-common.Adapters", + "text": "Adapters" + }, + " | undefined; updateOutput: (outputChanges: Partial<", "VisualizeOutput", - ">) => void; }" + ">) => void; supportedTriggers: () => string[]; getVis: () => ", + { + "pluginId": "visualizations", + "scope": "public", + "docId": "kibVisualizationsPluginApi", + "section": "def-public.Vis", + "text": "Vis" + }, + "<", + { + "pluginId": "visualizations", + "scope": "common", + "docId": "kibVisualizationsPluginApi", + "section": "def-common.VisParams", + "text": "VisParams" + }, + ">; transferCustomizationsToUiState: () => void; hasInspector: () => boolean; onContainerLoading: () => void; onContainerData: () => void; onContainerRender: () => void; onContainerError: (error: ", + { + "pluginId": "expressions", + "scope": "public", + "docId": "kibExpressionsPluginApi", + "section": "def-public.ExpressionRenderError", + "text": "ExpressionRenderError" + }, + ") => void; getExpressionVariables$: () => ", + "Observable", + " | undefined>; getExpressionVariables: () => Record | undefined; inputIsRefType: (input: ", + { + "pluginId": "visualizations", + "scope": "public", + "docId": "kibVisualizationsPluginApi", + "section": "def-public.VisualizeInput", + "text": "VisualizeInput" + }, + ") => input is ", + "VisualizeByReferenceInput", + "; getInputAsValueType: () => Promise<", + "VisualizeByValueInput", + ">; getInputAsRefType: () => Promise<", + "VisualizeByReferenceInput", + ">; }" ], "path": "src/plugins/visualizations/public/index.ts", "deprecated": false, @@ -8585,14 +8585,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/visualizations/common/expression_functions/range.ts", @@ -14644,14 +14636,6 @@ "section": "def-common.Adapters", "text": "Adapters" }, - ", ", - { - "pluginId": "@kbn/utility-types", - "scope": "common", - "docId": "kibKbnUtilityTypesPluginApi", - "section": "def-common.SerializableRecord", - "text": "SerializableRecord" - }, ">>" ], "path": "src/plugins/visualizations/common/expression_functions/vis_dimension.ts", diff --git a/api_docs/visualizations.mdx b/api_docs/visualizations.mdx index 42a1e0c2f4726..d3a0ca23afaab 100644 --- a/api_docs/visualizations.mdx +++ b/api_docs/visualizations.mdx @@ -8,7 +8,7 @@ slug: /kibana-dev-docs/api/visualizations title: "visualizations" image: https://source.unsplash.com/400x175/?github description: API docs for the visualizations plugin -date: 2023-12-06 +date: 2023-12-19 tags: ['contributor', 'dev', 'apidocs', 'kibana', 'visualizations'] --- import visualizationsObj from './visualizations.devdocs.json'; diff --git a/dev_docs/shared_ux/shared_ux_landing.mdx b/dev_docs/shared_ux/shared_ux_landing.mdx new file mode 100644 index 0000000000000..2b6bc7661dedd --- /dev/null +++ b/dev_docs/shared_ux/shared_ux_landing.mdx @@ -0,0 +1,70 @@ +--- +id: kibDevDocsSharedUxOverview +slug: /kibana-dev-docs/shared-ux +title: Shared UX Team +description: Links to all the documentation maintained by the Shared UX team. +tags: ['kibana', 'dev', 'shared-ux'] +layout: landing +--- + + + + + + diff --git a/docs/CHANGELOG.asciidoc b/docs/CHANGELOG.asciidoc index 1b7d6e6b88321..63b13250a1591 100644 --- a/docs/CHANGELOG.asciidoc +++ b/docs/CHANGELOG.asciidoc @@ -10,6 +10,8 @@ Review important information about the {kib} 8.x releases. +* <> +* <> * <> * <> * <> @@ -54,7 +56,82 @@ Review important information about the {kib} 8.x releases. * <> -- +[[release-notes-8.11.3]] +== {kib} 8.11.3 +The 8.11.3 release includes the following bug fixes. + +[float] +[[fixes-v8.11.3]] +=== Bug Fixes +Elastic Security:: +For the Elastic Security 8.11.3 release information, refer to {security-guide}/release-notes.html[_Elastic Security Solution Release Notes_]. +Fleet:: +* Fixes a 500 error in the Fleet API when a request for the product versions endpoint throws `ECONNREFUSED` ({kibana-pull}172850[#172850]). +* Fixes agent policy timeout to accept only integers ({kibana-pull}172222[#172222]). +Machine Learning:: +* Fixes data drift numeric fields not displaying correctly ({kibana-pull}172504[#172504]). +* Fixes Data visualizer, ML field stats, and Data Frame Analytics so the `_tier` field can be excluded ({kibana-pull}172223[#172223]). +Operations:: +* Fixes an issue where running `kibana-keystore` commands required `kibana.yml` to exist ({kibana-pull}172943[#172943]). + +[[release-notes-8.11.2]] +== {kib} 8.11.2 + +The 8.11.2 release includes the following bug fixes. + +[float] +[[security-update-v8.11.2]] +=== Security updates + +* The 8.11.2 patch release contains a fix for a potential security vulnerability. https://discuss.elastic.co/c/announcements/security-announcements/31[Please see our security advisory for more details]. + +[float] +[[enhancement-v8.11.2]] +=== Enhancements +APM:: +* Added `context_propagation_only` APM agent setting ({kibana-pull}170405[#170405]). +Elastic Security:: +For the Elastic Security 8.11.2 release information, refer to {security-guide}/release-notes.html[_Elastic Security Solution Release Notes_]. +Fleet:: +* Improve UX for policy secrets ({kibana-pull}171405[#171405]). +Observability:: +* Adds `date_formats` to SLI ingest pipeline template ({kibana-pull}172377[#172377]). +Platform:: +* It is now possible to hot reload Kibana's TLS (`server.ssl`) configuration by updating it and then sending a `SIGHUP` signal to the Kibana process ({kibana-pull}171823[#171823]). + +[float] +[[fixes-v8.11.2]] +=== Bug Fixes +Dashboard:: +* Fixes reference extract method ({kibana-pull}171360[#171360]). +* Adds Dashboard title to browser tab title ({kibana-pull}171255[#171255]). +Elastic Security:: +For the Elastic Security 8.11.2 release information, refer to {security-guide}/release-notes.html[_Elastic Security Solution Release Notes_]. +Fleet:: +* Support integration secrets in a local package registry with variables `secret: true` and `required: false` ({kibana-pull}172078[#172078]). +* Fixes agents metrics retrieval on the agent list page, previously displaying N/A for metrics for users with more than 10 agents. ({kibana-pull}172016[#172016]). +* Only add `time_series_metric` if TSDB is enabled ({kibana-pull}171712[#171712]). +* Fixes inability to upgrade agents from version 8.10.4 to version 8.11 ({kibana-pull}170974[#170974]). +Lens & Visualizations:: +* Handle invalid values gracefully for static value operation in *Lens* ({kibana-pull}172198[#172198]). +* Make the dashboard SO lighter ({kibana-pull}172130[#172130]). +Machine Learning:: +* Fixes blocked jobs polling interval ({kibana-pull}171878[#171878]). +Management:: +* Fixes autocomplete to show suggestions even if user types in every letter ({kibana-pull}171952[#171952]). +* Fixes wrong autocomplete suggestions when using the slash symbol ({kibana-pull}171948[#171948]). +* Fixes clusters and shards table expanded row not updating when request is changed ({kibana-pull}171232[#171232]). +Maps:: +* Fixes using `max_result_window` to set up a mapbox vector tile (MVT) size request leading to all results not showing ({kibana-pull}171344[#171344]). +Observability:: +* Fixes the custom threshold document link ({kibana-pull}171125[#171125]). +SharedUX:: +* Fixes custom branding for users without "Saved Object Management" privilege ({kibana-pull}171308[#171308]). +* Fixes the custom threshold document link ({kibana-pull}171125[#171125]). +Uptime:: +* Fixes advanced fields broken for ICMP monitors ({kibana-pull}171161[#171161]). +Ê [[release-notes-8.11.1]] == {kib} 8.11.1 diff --git a/docs/canvas/canvas-tutorial.asciidoc b/docs/canvas/canvas-tutorial.asciidoc index f8d2297df18b9..5016dbec88aac 100644 --- a/docs/canvas/canvas-tutorial.asciidoc +++ b/docs/canvas/canvas-tutorial.asciidoc @@ -121,7 +121,7 @@ To focus your data on a specific time range, add the time filter. . To use the date time field from the sample data, enter `order_date` in the *Column* field, then click *Set*. [role="screenshot"] -image::images/canvas_tutorialCustomTimeFilter_7.17.0.png[Custom time filter added to the workpad] +image::../setup/images/canvas_tutorialCustomTimeFilter_7.17.0.png[Custom time filter added to the workpad] To see how the data changes, set the time filter to *Last 7 days*. As you change the time filter options, the elements automatically update. diff --git a/docs/canvas/images/canvas_tutorialCustomTimeFilter_7.17.0.png b/docs/canvas/images/canvas_tutorialCustomTimeFilter_7.17.0.png deleted file mode 100644 index 7eb78efa5f591..0000000000000 Binary files a/docs/canvas/images/canvas_tutorialCustomTimeFilter_7.17.0.png and /dev/null differ diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc index 03a85d319cf55..c0307253a7208 100644 --- a/docs/management/advanced-options.asciidoc +++ b/docs/management/advanced-options.asciidoc @@ -451,6 +451,9 @@ preview:[] When enabled, allows users to create Service Groups from the APM Serv [[observability-apm-trace-explorer-tab]]`observability:apmTraceExplorerTab`:: preview:[] Enable the APM Trace Explorer feature, that allows you to search and inspect traces with KQL or EQL. +[[observability-infrastructure-profiling-integration]]`observability:enableInfrastructureProfilingIntegration`:: +preview:[] Enables the Profiling view in Host details within Infrastructure. + [float] [[kibana-reporting-settings]] ==== Reporting diff --git a/docs/management/connectors/action-types/servicenow-itom.asciidoc b/docs/management/connectors/action-types/servicenow-itom.asciidoc index 70ee48ccccd7b..ea21cb72248f7 100644 --- a/docs/management/connectors/action-types/servicenow-itom.asciidoc +++ b/docs/management/connectors/action-types/servicenow-itom.asciidoc @@ -31,16 +31,14 @@ image::management/connectors/images/servicenow-itom-connector-oauth.png[{sn-itom [[servicenow-itom-connector-configuration]] ==== Connector configuration -{sn-itom} connectors have the following configuration properties: +{sn-itom} connectors have a name and the following configuration properties: Client ID:: The client identifier assigned to your OAuth application. Client secret:: The client secret assigned to your OAuth application. -JWT key ID:: +JWT verifier key ID:: The key identifier assigned to the JWT verifier map of your OAuth application. -Connector name:: -The name is used to identify a connector in the management UI connector listing or in the connector list when configuring an action. Password:: The password for HTTP basic authentication. Private key:: diff --git a/docs/management/connectors/action-types/servicenow.asciidoc b/docs/management/connectors/action-types/servicenow.asciidoc index f3c636df29b6b..db0b9b3183a6f 100644 --- a/docs/management/connectors/action-types/servicenow.asciidoc +++ b/docs/management/connectors/action-types/servicenow.asciidoc @@ -12,9 +12,108 @@ The {sn-itsm} connector uses the https://developer.servicenow.com/dev.do#!/reference/api/sandiego/rest/c_ImportSetAPI[import set API] to create {sn} incidents. You can use the connector for rule actions and cases. +[float] +[[define-servicenow-ui]] +=== Create connectors in {kib} + +You can create connectors in *{stack-manage-app} > {connectors-ui}* +or as needed when you're creating a rule. You must choose whether to use OAuth for authentication. + +[role="screenshot"] +image::management/connectors/images/servicenow-connector-basic.png[ServiceNow connector using basic auth] +// NOTE: This is an autogenerated screenshot. Do not edit it directly. + + +[role="screenshot"] +image::management/connectors/images/servicenow-connector-oauth.png[ServiceNow connector using OAuth] +// NOTE: This is an autogenerated screenshot. Do not edit it directly. + +[float] +[[servicenow-connector-configuration]] +==== Connector configuration + +{sn-itsm} connectors have a name and the following configuration properties: + +Client ID:: +The client identifier assigned to your OAuth application. +Client secret:: +The client secret assigned to your OAuth application. +JWT verifier key ID:: +The key identifier assigned to the JWT verifier map of your OAuth application. +Password:: +The password for HTTP basic authentication. +Private key:: +The RSA private key that you created for use in {sn}. +Private key password:: +The password for the RSA private key. +This values is required if you set a password for your private key. +{sn} instance URL:: +The full URL for the {sn} instance. +Use OAuth authentication:: +By default, basic authentication is used instead of open authorization (OAuth). +User identifier:: +The identifier to use for OAuth type authentication. +This identifier should be the user field you selected during setup. For example, if the selected user field is `Email`, the user identifier should be the user's email address. +Username:: +The username for HTTP basic authentication. + +[float] +[[servicenow-action-configuration]] +=== Test connectors + +You can test connectors with the <> or +as you're creating or editing the connector in {kib}. For example: + +[role="screenshot"] +image::management/connectors/images/servicenow-params-test.png[ServiceNow params test] + +{sn-itsm} actions have the following configuration properties. + +Additional comments:: +Additional information for the client, such as how to troubleshoot the issue. +Category:: +The category of the incident. +Correlation ID:: +Connectors using the same correlation ID will be associated with the same {sn} incident. +This value determines whether a new {sn} incident will be created or an existing one is updated. +Modifying this value is optional; if not modified, the rule ID and alert ID are combined as `{{ruleID}}:{{alert ID}}` to form the correlation ID value in {sn}. +The maximum character length for this value is 100 characters. ++ +-- +NOTE: Using the default configuration of `{{ruleID}}:{{alert ID}}` ensures that {sn} will create a separate incident record for every generated alert that uses a unique alert ID. If the rule generates multiple alerts that use the same alert IDs, {sn} creates and continually updates a single incident record for the alert. +-- + +Correlation display:: +A descriptive label of the alert for correlation purposes in {sn}. +Description:: +The details about the incident. +Impact:: +The effect an incident has on business. +It can be measured by the number of affected users or by how critical it is to the business in question. +Severity:: +The severity of the incident. +Short description:: +A short description for the incident, used for searching the contents of the knowledge base. +Subcategory:: +The category of the incident. +Urgency:: +The extent to which the incident resolution can delay. + +[float] +[[servicenow-connector-networking-configuration]] +=== Connector networking configuration + +Use the <> to customize connector networking configurations, such as proxies, certificates, or TLS settings. You can set configurations that apply to all your connectors or use `xpack.actions.customHostSettings` to set per-host configurations. + +[float] +[[configuring-servicenow]] +=== Configure {sn} + +{sn} offers free https://developer.servicenow.com/dev.do#!/guides/madrid/now-platform/pdi-guide/obtaining-a-pdi[Personal Developer Instances], which you can use to test incidents. + [float] [[servicenow-itsm-connector-prerequisites]] -=== Prerequisites +==== Prerequisites After upgrading from {stack} version 7.15.0 or earlier to version 7.16.0 or later, you must complete the following steps within your {sn} instance before @@ -23,13 +122,56 @@ creating a new {sn-itsm} connector or . Install https://store.servicenow.com/sn_appstore_store.do#!/store/application/7148dbc91bf1f450ced060a7234bcb88[Elastic for ITSM] -from the {sn} Store. +from the {sn} store. +. <>. . <>. . <>. . If you use open authorization (OAuth), you must also: .. <>. .. <>. +[float] +[[servicenow-itsm-connector-privileges]] +==== Assign cross-scope privileges + +The Elastic for ITSM app requires specific cross-scope privilege records to run successfully. +In particular, you must have a privilege record for the `Elastic for ITSM` application and source scope with a `global` target scope for each of the following targets: + +|=== +|Target name, type|Operation|Status + +|GlideRecord.insert, Scriptable +|Execute API +|Allowed + +|GlideRecord.setValue, Scriptable +|Execute API +|Allowed + +|GlideRecordSecure.getValue, Scriptable +|Execute API +|Allowed + +|Incident, Table +|Read +|Allowed + +|ScriptableServiceResultBuilder.setBody, Scriptable +|Execute API +|Allowed + +|ScopedGlideElement, Scriptable +|Execute API +|Allowed +|=== + +To access the cross scope privileges table: + +1. Log into {sn} and set your application scope to Elastic for ITSM. +2. Click *All* and search for `sys_scope_privilege`. + +For more details, refer to the https://docs.servicenow.com/[{sn} product documentation]. + [float] [[servicenow-itsm-connector-prerequisites-integration-user]] ==== Create a {sn} integration user @@ -174,77 +316,7 @@ To update a deprecated connector: . Select the deprecated connector to open the *Edit connector* flyout. . In the warning message, click *Update this connector*. . Complete the guided steps in the *Edit connector* flyout. -.. Install https://store.servicenow.com/sn_appstore_store.do#!/store/application/7148dbc91bf1f450ced060a7234bcb88[Elastic for ITSM] and complete the <>. +.. Install https://store.servicenow.com/sn_appstore_store.do#!/store/application/7148dbc91bf1f450ced060a7234bcb88[Elastic for ITSM] and complete the <>. .. Enter the URL of your {sn} instance. .. Enter the username and password of your {sn} instance. . Click *Update*. - -[float] -[[define-servicenow-ui]] -=== Create connectors in {kib} - -You can create connectors in *{stack-manage-app} > {connectors-ui}* -or as needed when you're creating a rule. You must choose whether to use OAuth for authentication. - -[role="screenshot"] -image::management/connectors/images/servicenow-connector-basic.png[ServiceNow connector using basic auth] - -[role="screenshot"] -image::management/connectors/images/servicenow-connector-oauth.png[ServiceNow connector using OAuth] - -[float] -[[servicenow-connector-configuration]] -==== Connector configuration - -{sn-itsm} connectors have the following configuration properties: - -Name:: The name of the connector. -Is OAuth:: The type of authentication to use. -URL:: {sn} instance URL. -Username:: Username for HTTP Basic authentication. -Password:: Password for HTTP Basic authentication. -User Identifier:: Identifier to use for OAuth type authentication. This identifier should be the *User field* you selected during setup. For example, if the selected *User field* is *Email*, the user identifier should be the user's email address. -Client ID:: The client ID assigned to your OAuth application. -Client Secret:: The client secret assigned to your OAuth application. -JWT Key ID:: The key ID assigned to the JWT Verifier Map of your OAuth application. -Private Key:: The RSA private key generated during setup. -Private Key Password:: The password for the RSA private key generated during setup, if set. - -[float] -[[servicenow-action-configuration]] -=== Test connectors - -You can test connectors with the <> or -as you're creating or editing the connector in {kib}. For example: - -[role="screenshot"] -image::management/connectors/images/servicenow-params-test.png[ServiceNow params test] - - -{sn-itsm} actions have the following configuration properties. - -Urgency:: The extent to which the incident resolution can delay. -Severity:: The severity of the incident. -Impact:: The effect an incident has on business. Can be measured by the number of affected users or by how critical it is to the business in question. -Category:: The category of the incident. -Subcategory:: The category of the incident. -Correlation ID:: Connectors using the same Correlation ID will be associated with the same {sn} incident. This value determines whether a new {sn} incident will be created or an existing one is updated. Modifying this value is optional; if not modified, the rule ID and alert ID are combined as `{{ruleID}}:{{alert ID}}` to form the Correlation ID value in {sn}. The maximum character length for this value is 100 characters. - -NOTE: Using the default configuration of `{{ruleID}}:{{alert ID}}` ensures that {sn} will create a separate incident record for every generated alert that uses a unique alert ID. If the rule generates multiple alerts that use the same alert IDs, {sn} creates and continually updates a single incident record for the alert. - -Correlation Display:: A descriptive label of the alert for correlation purposes in {sn}. -Short description:: A short description for the incident, used for searching the contents of the knowledge base. -Description:: The details about the incident. -Additional comments:: Additional information for the client, such as how to troubleshoot the issue. - -[float] -[[servicenow-connector-networking-configuration]] -=== Connector networking configuration - -Use the <> to customize connector networking configurations, such as proxies, certificates, or TLS settings. You can set configurations that apply to all your connectors or use `xpack.actions.customHostSettings` to set per-host configurations. - -[float] -[[configuring-servicenow]] -=== Configure {sn} - -{sn} offers free https://developer.servicenow.com/dev.do#!/guides/madrid/now-platform/pdi-guide/obtaining-a-pdi[Personal Developer Instances], which you can use to test incidents. diff --git a/docs/management/connectors/images/servicenow-connector-basic.png b/docs/management/connectors/images/servicenow-connector-basic.png index e2bf73ad14594..4cf1f19fe8173 100644 Binary files a/docs/management/connectors/images/servicenow-connector-basic.png and b/docs/management/connectors/images/servicenow-connector-basic.png differ diff --git a/docs/management/connectors/images/servicenow-connector-oauth.png b/docs/management/connectors/images/servicenow-connector-oauth.png index f0124d39cfde9..8f818185f686b 100644 Binary files a/docs/management/connectors/images/servicenow-connector-oauth.png and b/docs/management/connectors/images/servicenow-connector-oauth.png differ diff --git a/docs/settings/fleet-settings.asciidoc b/docs/settings/fleet-settings.asciidoc index d3cad6b4dfdbc..a85c4b67063e6 100644 --- a/docs/settings/fleet-settings.asciidoc +++ b/docs/settings/fleet-settings.asciidoc @@ -13,7 +13,10 @@ In {ecloud}, {fleet} flags are already configured. You can configure `xpack.fleet` settings in your `kibana.yml`. By default, {fleet} is enabled. To use {fleet}, you also need to configure {kib} and {es} hosts. -See the {fleet-guide}/index.html[{fleet}] docs for more information. +Many {fleet} settings can also be configured directly through the {fleet} UI. +See {fleet-guide}/fleet-settings.html[Fleet UI settings] for details. + +See the {fleet-guide}/index.html[{fleet}] docs for more information about {fleet}. [[general-fleet-settings-kb]] ==== General {fleet} settings @@ -174,6 +177,8 @@ xpack.fleet.agentPolicies: `xpack.fleet.outputs`:: List of outputs that are configured when the {fleet} app starts. + +Certain types of outputs have additional required and optional settings. Refer to {fleet-guide}/fleet-settings.html#output-settings[Output settings] in the {fleet} and {agent} Guide for the full list of settings for each output type. ++ If configured in your `kibana.yml`, output settings are grayed out and unavailable in the {fleet} UI. To make these settings editable in the UI, do not configure them in the configuration file. @@ -188,13 +193,9 @@ NOTE: The `xpack.fleet.outputs` settings are intended for advanced configuration `name`::: Output name. `type`::: - Type of Output. Currently we support "elasticsearch", "logstash", "kafka". + Type of Output. Currently we support "elasticsearch", "logstash", "kafka", and "remote_elasticsearch". `hosts`::: Array that contains the list of host for that output. - `config`::: - Extra config for that output. - `proxy_id`::: - Unique ID of a proxy to access the output. ===== + .Optional properties of `xpack.fleet.outputs` @@ -204,7 +205,46 @@ NOTE: The `xpack.fleet.outputs` settings are intended for advanced configuration If `true`, the output specified in `xpack.fleet.outputs` will be the one used to send agent data unless there is another one configured specifically for the agent policy. `is_default_monitoring`::: If `true`, the output specified in `xpack.fleet.outputs` will be the one used to send agent monitoring data unless there is another one configured specifically for the agent policy. + `config`::: + Extra config for that output. + `proxy_id`::: + Unique ID of a proxy to access the output. + `ssl`::: + Set to enable authentication using the Secure Sockets Layer (SSL) protocol. ++ +.Properties of `ssl` +[%collapsible%open] +======= + `certificate`:::: + The SSL certificate that {agents} use to authenticate with the output. Include the full contents of the certificate here. +======= + + `secrets`::: + Include here any values for preconfigured outputs that should be stored as secrets. A secret value is replaced in the `kibana.yml` settings file with a reference, with the original value stored externally as a secure hash. Note that this type of secret storage requires all configured {fleet-server}s to be on version 8.12.0 or later. ++ +.Properties of `secrets` +[%collapsible%open] +======= + `key`::::: + The private certificate key that {agents} use to authenticate with the output. +======= ===== ++ +Example `xpack.fleet.outputs` configuration: ++ +[source,yaml] +---- +xpack.fleet.outputs: + - id: my-logstash-output-with-a-secret + name: preconfigured logstash output with a secret + type: logstash + hosts: ["localhost:9999"] + ssl: + certificate: xxxxxxxxxx + secrets: + ssl: + key: securekey +---- `xpack.fleet.fleetServerHosts`:: List of {fleet-server} hosts that are configured when the {fleet} app starts. diff --git a/docs/setup/settings.asciidoc b/docs/setup/settings.asciidoc index e43b70dd16941..3e8e05db7511f 100644 --- a/docs/setup/settings.asciidoc +++ b/docs/setup/settings.asciidoc @@ -326,9 +326,8 @@ or `*` to select all roles. *Default: `*`* Choose the default email connector for user notifications. As of `8.6.0`, {kib} is shipping with a new notification mechanism that will send email notifications for various user actions, e.g. assigning a _Case_ to a user. To enable notifications, an email connector must be <> in the system via `kibana.yml`, and the notifications plugin must be configured to point to the ID of that connector. [[path-data]] `path.data`:: -The path where {kib} stores persistent data not saved in {es}. -Can be a relative or absolute path. -Relative paths are resolved starting from the installation directory. *Default: `data`* +The path where {kib} stores persistent data +not saved in {es}. *Default: `data`* `pid.file`:: Specifies the path where {kib} creates the process ID file. diff --git a/docs/user/ml/index.asciidoc b/docs/user/ml/index.asciidoc index 1d3bf33479d1b..4f25e0aa45c78 100644 --- a/docs/user/ml/index.asciidoc +++ b/docs/user/ml/index.asciidoc @@ -148,8 +148,6 @@ advanced statistical methods to help you interpret your data and its behavior. [[log-rate-analysis]] === Log rate analysis -preview::[] - Log rate analysis is a feature that uses advanced statistical methods to identify reasons for increases or decreases in log rates. It makes it easy to find and investigate causes of unusual spikes or drops by using the analysis diff --git a/examples/README.asciidoc b/examples/README.asciidoc index d33c5e825ce12..3f2c58a173304 100644 --- a/examples/README.asciidoc +++ b/examples/README.asciidoc @@ -1,7 +1,7 @@ [[example-plugins]] == Example plugins -This folder contains example plugins. To run the plugins in this folder, use the `--run-examples` flag, via +This folder contains example plugins. To run the plugins in this folder, use the `--run-examples` flag (without a basepath), via [source,bash] ---- diff --git a/examples/discover_customization_examples/public/plugin.tsx b/examples/discover_customization_examples/public/plugin.tsx index 5eefb4932cc43..9368c943532a4 100644 --- a/examples/discover_customization_examples/public/plugin.tsx +++ b/examples/discover_customization_examples/public/plugin.tsx @@ -213,7 +213,6 @@ export class DiscoverCustomizationExamplesPlugin implements Plugin { {currentSavedSearch.title ?? 'None selected'} } - anchorClassName="eui-fullWidth" isOpen={isPopoverOpen} panelPaddingSize="none" closePopover={closePopover} @@ -280,7 +279,6 @@ export class DiscoverCustomizationExamplesPlugin implements Plugin { {currentSavedSearch.title ?? 'None selected'} } - anchorClassName="eui-fullWidth" isOpen={isPopoverOpen} panelPaddingSize="none" closePopover={closePopover} diff --git a/examples/eso_model_version_example/README.md b/examples/eso_model_version_example/README.md new file mode 100755 index 0000000000000..f655fbf13c9e1 --- /dev/null +++ b/examples/eso_model_version_example/README.md @@ -0,0 +1,16 @@ +## Encrypted Saved Object Model Version Example + +This plugin provides a simple use case demonstration of: + - How to organize versioned saved object and encryption registration definitions + - How to use the createModelVersion wrapper function of the Encrypted Saved Objects plugin + - How/when encrypted model versions are migrated and what to expect when they are queried + +This is an example plugin to demonstrate implementation of an encrypted saved object with model versions using the new encryptedSavedObjectsPlugin.createModelVersion API. + +A good place to start is by reviewing the definitions in `examples/eso_model_version_example/server/types`. This is where the interfaces and constants that for the example saved object are defined. + +In `examples/eso_model_version_example/server/plugin.ts` the model versions are defined, which include typical changes you might see in a saved object over time only in this case the model version definitions are wrapped by the new createModelVersion API. + +Lastly, use the plugin UI to get a sense for how the objects are migrated - you can query the raw documents and then decrypted the migrated objects. + +To run this example, use the command `yarn start --run-examples`. diff --git a/src/plugins/data/public/search/errors/index.ts b/examples/eso_model_version_example/common/index.ts similarity index 64% rename from src/plugins/data/public/search/errors/index.ts rename to examples/eso_model_version_example/common/index.ts index 27acf0baecdb4..b430282790f62 100644 --- a/src/plugins/data/public/search/errors/index.ts +++ b/examples/eso_model_version_example/common/index.ts @@ -6,9 +6,4 @@ * Side Public License, v 1. */ -export * from './es_error'; -export * from './painless_error'; -export * from './timeout_error'; -export * from './utils'; -export * from './types'; -export * from './search_session_incomplete_warning'; +export const PLUGIN_ID = 'esoModelVersionExample'; diff --git a/examples/eso_model_version_example/kibana.jsonc b/examples/eso_model_version_example/kibana.jsonc new file mode 100644 index 0000000000000..a09c85e9757dc --- /dev/null +++ b/examples/eso_model_version_example/kibana.jsonc @@ -0,0 +1,13 @@ +{ + "type": "plugin", + "id": "@kbn/eso-model-version-example", + "owner": "@elastic/kibana-security", + "description": "ESO Model Version Example", + "plugin": { + "id": "esoModelVersionExample", + "server": true, + "browser": true, + "requiredBundles": ["kibanaReact"], + "requiredPlugins": ["developerExamples", "security", "spaces", "encryptedSavedObjects"] + } +} diff --git a/examples/eso_model_version_example/public/app.tsx b/examples/eso_model_version_example/public/app.tsx new file mode 100644 index 0000000000000..9fa3c43521e08 --- /dev/null +++ b/examples/eso_model_version_example/public/app.tsx @@ -0,0 +1,149 @@ +/* + * Copyright 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 { + EuiAccordion, + EuiButton, + EuiCodeBlock, + EuiPageTemplate, + EuiSpacer, + EuiText, + EuiTextColor, + EuiTitle, +} from '@elastic/eui'; +import React, { useState } from 'react'; + +export const MyPluginComponent: React.FC = () => { + const [generated, setGenerated] = useState(''); + const [rawDocs, setRawDocs] = useState(''); + const [objects, setObjects] = useState(''); + const [decrypted, setDecrypted] = useState(''); + + const handler = async ( + endpoint: string, + setter: (value: React.SetStateAction) => void + ) => { + const response = await fetch(endpoint); + const data = await response.json(); + setter(JSON.stringify(data, null, 2)); + }; + + return ( + + + +

Encrypted Saved Object Model Version Example

+
+ + This is a demonstration to show the results of the implementation found in  + examples/eso_model_version_example + +
+ + + 1. This will create three objects - one for each model version definition (see  + + examples/eso_model_version_example/server/types + + ). + + { + handler('/internal/eso_mv_example/generate', setGenerated); + }} + > + Create Objects + + + + + {generated} + + + + + + 2. This will read the raw documents of the objects with an Elasticsearch client. Note that + the  + typeMigrationVersion  + (10.n.0) will correspond to the model version (n). + + { + handler('/internal/eso_mv_example/read_raw', setRawDocs); + }} + > + Read Raw Documents + + + + + {rawDocs} + + + + + + 3. This will read the saved objects with a Kibana saved object client. Note that the + objects have been migrated on read to the latest model version, and the encrypted fields + have been stripped. + + { + handler('/internal/eso_mv_example/get_objects', setObjects); + }} + > + Read Saved Objects + + + + + {objects} + + + + + 4. This will decrypt the saved objects. + { + handler('/internal/eso_mv_example/get_decrypted', setDecrypted); + }} + > + Decrypt Secrets + + + + + {decrypted} + + + +
+ ); +}; diff --git a/examples/eso_model_version_example/public/index.tsx b/examples/eso_model_version_example/public/index.tsx new file mode 100644 index 0000000000000..d8bc5fd3884f4 --- /dev/null +++ b/examples/eso_model_version_example/public/index.tsx @@ -0,0 +1,59 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import React from 'react'; +import ReactDOM from 'react-dom'; +import { AppMountParameters, CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; +import { DeveloperExamplesSetup } from '@kbn/developer-examples-plugin/public'; +import { SecurityPluginSetup, SecurityPluginStart } from '@kbn/security-plugin/public'; +import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; +import type { FeaturesPluginSetup } from '@kbn/features-plugin/public'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { MyPluginComponent } from './app'; + +interface SetupDeps { + developerExamples: DeveloperExamplesSetup; + security: SecurityPluginSetup; + features: FeaturesPluginSetup; +} + +interface StartDeps { + security: SecurityPluginStart; +} + +export class EsoModelVersionExample implements Plugin { + public setup(coreSetup: CoreSetup, deps: SetupDeps) { + coreSetup.application.register({ + id: 'esoModelVersionExample', + title: 'ESO Model Version Example', + async mount({ element }: AppMountParameters) { + const [coreStart] = await coreSetup.getStartServices(); + ReactDOM.render( + + + + + , + element + ); + return () => ReactDOM.unmountComponentAtNode(element); + }, + }); + deps.developerExamples.register({ + appId: 'esoModelVersionExample', + title: 'ESO Model Version Example', + description: 'Example of encrypted saved object with model version implementation', + }); + } + + public start(core: CoreStart, deps: StartDeps) { + return {}; + } + + public stop() {} +} +export const plugin = () => new EsoModelVersionExample(); diff --git a/examples/eso_model_version_example/server/index.ts b/examples/eso_model_version_example/server/index.ts new file mode 100644 index 0000000000000..fc9eacee87e24 --- /dev/null +++ b/examples/eso_model_version_example/server/index.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { PluginInitializer } from '@kbn/core/server'; +import { EsoModelVersionExample } from './plugin'; + +export const plugin: PluginInitializer = async () => new EsoModelVersionExample(); diff --git a/examples/eso_model_version_example/server/plugin.ts b/examples/eso_model_version_example/server/plugin.ts new file mode 100644 index 0000000000000..5f5014e090c02 --- /dev/null +++ b/examples/eso_model_version_example/server/plugin.ts @@ -0,0 +1,423 @@ +/* + * Copyright 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. + */ + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// This is an example plugin to demonstrate implementation of an encrypted saved object with model versions using // +// the new encryptedSavedObjectsPlugin.createModelVersion API. // +// // +// A good place to start is by reviewing the definitions in examples/eso_model_version_example/server/types. This // +// is where the interfaces and constants for the example saved object are defined. // +// // +// In this file (plugin.ts) the model versions are defined, which include typical changes you might see in a saved // +// object over time, only in this case the model version definitions are wrapped by the new createModelVersion API. // +// // +// Lastly, use the plugin UI to get a sense for how the objects are migrated - you can query the raw documents and // +// the decrypted migrated objects. // +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +import { + CoreSetup, + IRouter, + Plugin, + RequestHandlerContext, + SavedObjectsBulkResponse, +} from '@kbn/core/server'; + +import { + EncryptedSavedObjectsPluginSetup, + EncryptedSavedObjectsPluginStart, +} from '@kbn/encrypted-saved-objects-plugin/server'; +import { schema } from '@kbn/config-schema'; + +import { SpacesPluginSetup } from '@kbn/spaces-plugin/server'; +import { WriteResponseBase } from '@elastic/elasticsearch/lib/api/types'; + +import { + esoModelVersionExampleV1, + esoModelVersionExampleV2, + esoModelVersionExampleV3, +} from './types'; + +import { EsoModelVersionExampleTypeRegistration, EXAMPLE_SAVED_OBJECT_TYPE } from './types/latest'; + +const documentVersionConstants = [ + esoModelVersionExampleV1.ESO_MV_RAW_DOC, + esoModelVersionExampleV2.ESO_MV_RAW_DOC, + esoModelVersionExampleV3.ESO_MV_RAW_DOC, +]; + +export interface EsoModelVersionExamplePluginSetup { + encryptedSavedObjects: EncryptedSavedObjectsPluginSetup; + spaces: SpacesPluginSetup; +} + +export interface EsoModelVersionExamplePluginsStart { + encryptedSavedObjects: EncryptedSavedObjectsPluginStart; +} + +export class EsoModelVersionExample implements Plugin { + public setup( + core: CoreSetup, + plugins: EsoModelVersionExamplePluginSetup + ) { + // Register some endpoints for the example plugin + const router = core.http.createRouter(); + this.registerGenerateEndpoint(router); // This will create three objects - one for each model version definition. + this.registerReadRawEndpoint(router); // This will read the objects' raw documents with an Elasticsearch client. + this.registerGetObjectsEndpoint(router); // This will read the migrated objects with an SO client. + this.registerGetDecryptedEndpoint(router, core, plugins); // This will decrypt the objects' secrets. + + // register type as ESO using the latest definition + plugins.encryptedSavedObjects.registerType(EsoModelVersionExampleTypeRegistration); + + // Register the SO with model versions + core.savedObjects.registerType({ + name: EXAMPLE_SAVED_OBJECT_TYPE, + hidden: false, + namespaceType: 'multiple-isolated', + mappings: { + dynamic: false, + properties: { + name: { + type: 'text', + fields: { + keyword: { + type: 'keyword', + }, + }, + }, + }, + }, + modelVersions: { + 1: plugins.encryptedSavedObjects.createModelVersion({ + modelVersion: { + // It is required to define at least one change to use the 'createModelVersion' wrapper, so we will define a no-op transform + changes: [ + { + type: 'unsafe_transform', + transformFn: (document) => { + return { document }; + }, + }, + ], + schemas: { + forwardCompatibility: schema.object( + { + name: schema.string(), + toBeRemoved: schema.string(), + aadField1: schema.maybe(schema.object({ flag1: schema.maybe(schema.boolean()) })), + secrets: schema.any(), + }, + // 'ignore' will strip any new unknown fields coming from new versions (a zero-downtime upgrade consideration) + // We want to do this unless we have a compelling reason not to, like if we know we want to add a new AAD field + // in the next version (see model version 2) + { unknowns: 'ignore' } + ), + create: schema.object({ + name: schema.string(), + toBeRemoved: schema.string(), + aadField1: schema.maybe(schema.object({ flag1: schema.maybe(schema.boolean()) })), + secrets: schema.any(), + }), + }, + }, + inputType: esoModelVersionExampleV1.EsoModelVersionExampleTypeRegistration, // Pass in the type registration for the specific version + outputType: esoModelVersionExampleV1.EsoModelVersionExampleTypeRegistration, // In this case both input an output are V1 + shouldTransformIfDecryptionFails: true, + }), + 2: plugins.encryptedSavedObjects.createModelVersion({ + modelVersion: { + changes: [ + // Version 2 adds additional optional properties (or "sub-fields") to aadField1 and secrets, we're going to back fill them. + // Version 2 also adds an optional field aadExcludedField, which is excluded from AAD. This will be stripped out for + // older versions during zero-downtime upgrades due to the forwardCompatibility schema in model version 1. + { + type: 'data_backfill', + backfillFn: (doc) => { + const aadField1 = doc.attributes.aadField1; + const secrets = doc.attributes.secrets; + return { + attributes: { + aadField1: { ...aadField1, flag2: false }, + secrets: { + ...secrets, + b: "model version 2 adds property 'b' to the secrets attribute", + }, + }, + }; + }, + }, + ], + schemas: { + forwardCompatibility: schema.object( + { + name: schema.string(), + toBeRemoved: schema.string(), + aadField1: schema.maybe( + schema.object({ + flag1: schema.maybe(schema.boolean()), + flag2: schema.maybe(schema.boolean()), + }) + ), + aadExcludedField: schema.maybe(schema.string()), + secrets: schema.any(), + }, + // If we know that we will be adding a new AAD field in the next version, we will NOT strip new fields + // in the forward compatibility schema. This is a Zero-downtime upgrade consideration and ensures that + // old versions will use those fields when constructing AAD. The caveat is that we need to know ahead + // of time, and make sure the consuming code can handle having the additional attribute, even if it + // is not used yet. + { unknowns: 'allow' } + ), + create: schema.object({ + name: schema.string(), + toBeRemoved: schema.string(), + aadField1: schema.maybe( + schema.object({ + flag1: schema.maybe(schema.boolean()), + flag2: schema.maybe(schema.boolean()), + }) + ), + aadExcludedField: schema.maybe(schema.string()), + secrets: schema.any(), + }), + }, + }, + inputType: esoModelVersionExampleV1.EsoModelVersionExampleTypeRegistration, // In this case we are expecting to transform from a V1 object + outputType: esoModelVersionExampleV2.EsoModelVersionExampleTypeRegistration, // to a V2 object + shouldTransformIfDecryptionFails: true, + }), + 3: plugins.encryptedSavedObjects.createModelVersion({ + modelVersion: { + // Version 3 adds a new attribute aadField2 which is included in AAD, we're not going to back fill it. + // For zero-downtime this new attribute is ok, because the previous model version allows unknown fields and will not strip it. + // The previous version will include it by default when constructing AAD. + changes: [ + { + type: 'data_removal', + removedAttributePaths: ['toBeRemoved'], + }, + ], + schemas: { + forwardCompatibility: schema.object( + { + name: schema.string(), + aadField1: schema.maybe( + schema.object({ + flag1: schema.maybe(schema.boolean()), + flag2: schema.maybe(schema.boolean()), + }) + ), + aadField2: schema.maybe( + schema.object({ + foo: schema.maybe(schema.string()), + bar: schema.maybe(schema.string()), + }) + ), + aadExcludedField: schema.maybe(schema.string()), + secrets: schema.any(), + }, + // We will ignore any new fields of future versions again + { unknowns: 'ignore' } + ), + create: schema.object({ + name: schema.string(), + aadField1: schema.maybe( + schema.object({ + flag1: schema.maybe(schema.boolean()), + flag2: schema.maybe(schema.boolean()), + }) + ), + aadField2: schema.maybe( + schema.object({ + foo: schema.maybe(schema.string()), + bar: schema.maybe(schema.string()), + }) + ), + aadExcludedField: schema.maybe(schema.string()), + secrets: schema.any(), + }), + }, + }, + inputType: esoModelVersionExampleV2.EsoModelVersionExampleTypeRegistration, // In this case we are expecting to transform from V2 to V3. This happens to be the latest + outputType: esoModelVersionExampleV3.EsoModelVersionExampleTypeRegistration, // version, but being explicit means we don't need to change this when we implement V4 + shouldTransformIfDecryptionFails: true, + }), + }, + }); + } + + start() { + return {}; + } + + // This will create three objects - one for each model version definition. + private registerGenerateEndpoint(router: IRouter) { + router.get( + { + path: '/internal/eso_mv_example/generate', + validate: false, + }, + async (context, request, response) => { + const { elasticsearch } = await context.core; + + // Try to delete the documents in case they already exist + try { + await Promise.all( + documentVersionConstants.map(async (obj) => { + await elasticsearch.client.asInternalUser.delete({ + id: obj.id, + index: obj.index, + }); + }) + ); + } catch (error) { + // ignore errors - objects may not exist + } + + // Save raw docs for all three versions, so we can decrypt them and inspect + try { + const objectsCreated = await Promise.all( + documentVersionConstants.map(async (obj) => { + const createdDoc: WriteResponseBase = + await elasticsearch.client.asInternalUser.create(obj); + const parts = createdDoc._id.split(':', 2); + return { type: parts[0], id: parts[1] }; + }) + ); + + return response.ok({ + body: { + objectsCreated, + }, + }); + } catch (error) { + return response.ok({ + body: { + error, + }, + }); + } + } + ); + } + + // This will read the objects' raw documents with an Elasticsearch client. + private registerReadRawEndpoint(router: IRouter) { + router.get( + { + path: '/internal/eso_mv_example/read_raw', + validate: false, + }, + async (context, request, response) => { + // Read the raw documents so we can display the model versions prior to migration transformations + const { elasticsearch } = await context.core; + try { + const rawDocuments = await Promise.all( + documentVersionConstants.map(async (obj) => { + return await elasticsearch.client.asInternalUser.get({ + id: obj.id, + index: obj.index, + }); + }) + ); + + return response.ok({ + body: { + rawDocuments, + }, + }); + } catch (error) { + return response.ok({ + body: { + error, + }, + }); + } + } + ); + } + + // This will read the migrated objects with an SO client. + private registerGetObjectsEndpoint(router: IRouter) { + router.get( + { + path: '/internal/eso_mv_example/get_objects', + validate: false, + }, + async (context, request, response) => { + // Get the objects via the SO client so we can display how the objects are migrated via the MV definitions + const { savedObjects } = await context.core; + try { + const bulkGetObjects = documentVersionConstants.map((obj) => { + const parts = obj.id.split(':', 2); + return { type: parts[0], id: parts[1] }; + }); + + const result: SavedObjectsBulkResponse = await savedObjects.client.bulkGet( + bulkGetObjects + ); + return response.ok({ + body: result, + }); + } catch (error) { + return response.ok({ + body: { + error, + }, + }); + } + } + ); + } + + // This will decrypt the objects' secrets. + private registerGetDecryptedEndpoint( + router: IRouter, + core: CoreSetup, + plugins: EsoModelVersionExamplePluginSetup + ) { + router.get( + { + path: '/internal/eso_mv_example/get_decrypted', + validate: false, + }, + async (context, request, response) => { + // Decrypt the objects as the internal user so we can display the secrets + const [, { encryptedSavedObjects }] = await core.getStartServices(); + const spaceId = plugins.spaces.spacesService.getSpaceId(request); + const namespace = plugins.spaces.spacesService.spaceIdToNamespace(spaceId); + try { + const esoClient = encryptedSavedObjects.getClient({ + includedHiddenTypes: [EXAMPLE_SAVED_OBJECT_TYPE], + }); + + const decrypted = await Promise.all( + documentVersionConstants.map(async (obj) => { + const parts = obj.id.split(':', 2); + const dooder = await esoClient.getDecryptedAsInternalUser(parts[0], parts[1], { + namespace, + }); + return dooder; + }) + ); + + return response.ok({ + body: decrypted, + }); + } catch (error) { + return response.ok({ + body: { + error, + }, + }); + } + } + ); + } +} diff --git a/examples/eso_model_version_example/server/types/example_type/v1.ts b/examples/eso_model_version_example/server/types/example_type/v1.ts new file mode 100644 index 0000000000000..06c81c6f1ac99 --- /dev/null +++ b/examples/eso_model_version_example/server/types/example_type/v1.ts @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { EncryptedSavedObjectTypeRegistration } from '@kbn/encrypted-saved-objects-plugin/server'; + +export const EXAMPLE_SAVED_OBJECT_TYPE = 'eso_model_version_example'; + +export interface EsoModelVersionExampleOptions1 { + flag1?: boolean; +} + +export interface EsoModelVersionExampleSecretData { + a: string; +} + +// These are the attributes of V1 of our saved object. +export interface EsoModelVersionExample { + name: string; // Display name attribute. Not part of AAD + toBeRemoved: string; // An attribute that will be removed in a later model version. + aadField1?: EsoModelVersionExampleOptions1; // Optional attribute that is part of AAD. + secrets: EsoModelVersionExampleSecretData; // An encrypted attribute. +} + +// This is the encryption definition for V1 of our saved object. +// It is important to keep this definition so it can be used with the new +// createModelVersion wrapper when newer model versions are defined. +export const EsoModelVersionExampleTypeRegistration: EncryptedSavedObjectTypeRegistration = { + type: EXAMPLE_SAVED_OBJECT_TYPE, + attributesToEncrypt: new Set(['secrets']), + attributesToExcludeFromAAD: new Set(['name', 'toBeRemoved']), // aadField1 is included in AAD, but not name or toBeRemoved +}; + +// This is just some static information used to generate a document +// for this specific model version. Otherwise, creating a saved object +// will always create the latest model version. +export const ESO_MV_RAW_DOC = { + index: '.kibana', + id: 'eso_model_version_example:9e2c00d0-7dc4-11ee-b7ba-ede5fa1a84d7', + document: { + eso_model_version_example: { + name: 'MV1 Test', + toBeRemoved: 'nope', + aadField1: { + flag1: false, + }, + secrets: + 'SItM+8gR71K5LSmy2dX7EmwZUcDiZWAaI667qFZ22Cn6PtncjMuCMI9586IVt0X69ROV/q81J1XBNp71JpC+hVBZQjis1M17iYerot53srZbG2uw5j8onBiTdr30EgoWx2YFca0+Plm23ukiSdpZH0FSSQJ3npjN5HFumzG9eseNzET3', + }, + type: 'eso_model_version_example', + references: [], + managed: false, + namespaces: ['default'], + coreMigrationVersion: '8.8.0', + typeMigrationVersion: '10.1.0', + updated_at: '2023-11-07T23:23:11.581Z', + created_at: '2023-11-07T23:23:11.581Z', + }, +}; diff --git a/examples/eso_model_version_example/server/types/example_type/v2.ts b/examples/eso_model_version_example/server/types/example_type/v2.ts new file mode 100644 index 0000000000000..ec37f749a3e33 --- /dev/null +++ b/examples/eso_model_version_example/server/types/example_type/v2.ts @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { EncryptedSavedObjectTypeRegistration } from '@kbn/encrypted-saved-objects-plugin/server'; + +export const EXAMPLE_SAVED_OBJECT_TYPE = 'eso_model_version_example'; + +// V2 adds a new sub-field "flag2" +export interface EsoModelVersionExampleOptions1 { + flag1?: boolean; + flag2?: boolean; +} + +// V2 adds a new encrypted sub-field "b" +export interface EsoModelVersionExampleSecretData { + a: string; + b?: string; +} + +// These are the attributes of V2 of our saved object. +export interface EsoModelVersionExample { + name: string; // Display name attribute. Not part of AAD + toBeRemoved: string; // An attribute that will be removed in a later model version. + aadField1?: EsoModelVersionExampleOptions1; // Optional attribute that is part of AAD. + aadExcludedField?: string; // Optional attribute that is NOT part of AAD. + secrets: EsoModelVersionExampleSecretData; // An encrypted attribute. +} + +// This is the encryption definition for V2 of our saved object. +// It is important to keep this definition so it can be used with the new +// createModelVersion wrapper when newer model versions are defined. +export const EsoModelVersionExampleTypeRegistration: EncryptedSavedObjectTypeRegistration = { + type: EXAMPLE_SAVED_OBJECT_TYPE, + attributesToEncrypt: new Set(['secrets']), + attributesToExcludeFromAAD: new Set(['name', 'toBeRemoved', 'aadExcludedField']), // aadField1 is included in AAD, but not name, toBeRemoved, or aadExcludedField +}; + +// This is just some static information used to generate a document +// for this specific model version. Otherwise, creating a saved object +// will always create the latest model version. +export const ESO_MV_RAW_DOC = { + index: '.kibana', + id: 'eso_model_version_example:52868e00-7dd5-11ee-bc21-35484912189c', + document: { + eso_model_version_example: { + name: 'MV2 Test', + toBeRemoved: 'nothing to see here', + aadField1: { + flag1: true, + flag2: true, + }, + aadExcludedField: 'this will not be used in AAD', + secrets: + 'uXBOQpvkI9+lAcfJ52yQAroKIIj+YBT9Ym3IpH1nmPBj2u51tZ07tnPQ3EtO379zHzGOMu+9Da3+bVmDbtsL0z/YrDad3f0o0XSnuEDvmPIVWqC0EwKguik+t63s5LrFvp4r+X3OmsG+jIISx/PXXgLl/8NiWa/urjp649lTGo/k4QvSHyQ4egeM1LjRihFSBFEZkQljF6SJLFocuDlQb8GHkVtgp0pKKfrZu0mI8Q==', + }, + type: 'eso_model_version_example', + references: [], + managed: false, + namespaces: ['default'], + coreMigrationVersion: '8.8.0', + typeMigrationVersion: '10.2.0', + updated_at: '2023-11-08T01:22:46.112Z', + created_at: '2023-11-08T01:22:46.112Z', + }, +}; diff --git a/examples/eso_model_version_example/server/types/example_type/v3.ts b/examples/eso_model_version_example/server/types/example_type/v3.ts new file mode 100644 index 0000000000000..5d41ec8c79aab --- /dev/null +++ b/examples/eso_model_version_example/server/types/example_type/v3.ts @@ -0,0 +1,78 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { EncryptedSavedObjectTypeRegistration } from '@kbn/encrypted-saved-objects-plugin/server'; + +export const EXAMPLE_SAVED_OBJECT_TYPE = 'eso_model_version_example'; + +export interface EsoModelVersionExampleOptions1 { + flag1?: boolean; + flag2?: boolean; +} + +// This is a new attribute added in V3 +export interface EsoModelVersionExampleOptions2 { + foo?: string; + bar?: string; +} + +export interface EsoModelVersionExampleSecretData { + a: string; + b?: string; +} + +// These are the attributes of V3 of our saved object. +export interface EsoModelVersionExample { + name: string; // Display name attribute. Not part of AAD + // We have removed 'toBeRemoved' + aadField1?: EsoModelVersionExampleOptions1; // Optional attribute that is part of AAD. + aadField2?: EsoModelVersionExampleOptions2; // Optional attribute that is part of AAD. + aadExcludedField?: string; // Optional attribute that is NOT part of AAD. + secrets: EsoModelVersionExampleSecretData; // An encrypted attribute. +} + +// This is the encryption definition for V3 of our saved object. +// It is important to keep this definition so it can be used with the new +// createModelVersion wrapper when newer model versions are defined. +export const EsoModelVersionExampleTypeRegistration: EncryptedSavedObjectTypeRegistration = { + type: EXAMPLE_SAVED_OBJECT_TYPE, + attributesToEncrypt: new Set(['secrets']), + attributesToExcludeFromAAD: new Set(['name', 'aadExcludedField']), // aadField1 and aadField2 are included in AAD, but not name, or aadExcludedField +}; + +// This is just some static information used to generate a document +// for this specific model version. Otherwise, creating a saved object +// will always create the latest model version. +export const ESO_MV_RAW_DOC = { + index: '.kibana', + id: 'eso_model_version_example:4b43a8b0-7dd7-11ee-8355-7d13444c2fd7', + document: { + eso_model_version_example: { + name: 'MV3 Test', + aadField1: { + flag1: false, + flag2: true, + }, + aadField2: { + foo: 'bar', + bar: 'foo', + }, + aadExcludedField: 'this is a field excluded from AAD', + secrets: + 'YYtHdisdq44Mvd9VdUui62hM8OowEgkuWSfidWq11lG4aXYR61tf+G+BlbwO6rqKPbFWK238Vn1tP+zceeiCofDqEZkViinT1nGDGjArEEsmIUlDtj5IdaY6boMGRzUJ+37viUrISFXMVV9n2qVMp7IYb2BGkAb3hyh4+ZO9SPTbrKhkcpKgpLs3CEvmfsgeW/Tkxh+F65uK2RShkgLoPy62JI35XUz1paop+zSQ90yPL9ysoQ==', + }, + type: 'eso_model_version_example', + references: [], + managed: false, + namespaces: ['default'], + coreMigrationVersion: '8.8.0', + typeMigrationVersion: '10.3.0', + updated_at: '2023-11-08T01:36:52.923Z', + created_at: '2023-11-08T01:36:52.923Z', + }, +}; diff --git a/examples/eso_model_version_example/server/types/index.ts b/examples/eso_model_version_example/server/types/index.ts new file mode 100644 index 0000000000000..e44621ff7c444 --- /dev/null +++ b/examples/eso_model_version_example/server/types/index.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 * as esoModelVersionExampleV1 from './example_type/v1'; +export * as esoModelVersionExampleV2 from './example_type/v2'; +export * as esoModelVersionExampleV3 from './example_type/v3'; diff --git a/src/plugins/vis_types/timeseries/public/application/lib/index.ts b/examples/eso_model_version_example/server/types/latest.ts similarity index 88% rename from src/plugins/vis_types/timeseries/public/application/lib/index.ts rename to examples/eso_model_version_example/server/types/latest.ts index 4fd845df4d58c..188f9d8c41679 100644 --- a/src/plugins/vis_types/timeseries/public/application/lib/index.ts +++ b/examples/eso_model_version_example/server/types/latest.ts @@ -6,4 +6,4 @@ * Side Public License, v 1. */ -export { getTimezone } from './get_timezone'; +export * from './example_type/v3'; diff --git a/examples/eso_model_version_example/tsconfig.json b/examples/eso_model_version_example/tsconfig.json new file mode 100644 index 0000000000000..40c8532c22a94 --- /dev/null +++ b/examples/eso_model_version_example/tsconfig.json @@ -0,0 +1,26 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types" + }, + "include": [ + "index.ts", + "common/**/*.ts", + "public/**/*.ts", + "public/**/*.tsx", + "server/**/*.ts", + "../../typings/**/*" + ], + "exclude": ["target/**/*"], + "kbn_references": [ + "@kbn/core", + "@kbn/kibana-react-plugin", + "@kbn/developer-examples-plugin", + "@kbn/security-plugin", + "@kbn/shared-ux-page-kibana-template", + "@kbn/features-plugin", + "@kbn/encrypted-saved-objects-plugin", + "@kbn/config-schema", + "@kbn/spaces-plugin" + ] +} diff --git a/examples/response_stream/public/containers/app/pages/page_reducer_stream/index.tsx b/examples/response_stream/public/containers/app/pages/page_reducer_stream/index.tsx index a55f25292cf5d..cfa672748f7ff 100644 --- a/examples/response_stream/public/containers/app/pages/page_reducer_stream/index.tsx +++ b/examples/response_stream/public/containers/app/pages/page_reducer_stream/index.tsx @@ -8,7 +8,15 @@ import React, { useEffect, useState, FC } from 'react'; -import { Chart, Settings, Axis, BarSeries, Position, ScaleType } from '@elastic/charts'; +import { + Chart, + Settings, + Axis, + BarSeries, + Position, + ScaleType, + LEGACY_LIGHT_THEME, +} from '@elastic/charts'; import { EuiBadge, @@ -113,7 +121,11 @@ export const PageReducerStream: FC = () => {
- + diff --git a/nav-kibana-dev.docnav.json b/nav-kibana-dev.docnav.json index 56517c4136f7f..d4eb3aaba6dd9 100644 --- a/nav-kibana-dev.docnav.json +++ b/nav-kibana-dev.docnav.json @@ -25,15 +25,6 @@ } ] }, - { - "label": "Operations", - "items": [ - { - "id": "kibDevDocsOpsOverview", - "label": "Overview" - } - ] - }, { "label": "Contributing", "items": [ @@ -149,9 +140,6 @@ { "id": "kibDevTutorialExpressions" }, - { - "id": "kibDevDocsKPTTutorial" - }, { "id": "kibDevTutorialDataSearchAndSessions", "label": "data.search" @@ -178,15 +166,21 @@ }, { "id": "kibDevTutorialAdvancedSettings" - }, + } + ] + }, + { + "label": "Serverless", + "pageId": "ktServerlessReleaseOverview", + "items": [ { - "id": "kibDevSharePluginReadme" + "id": "ktServerlessReleaseOverview" }, { - "id": "kibDevTutorialScreenshotting" + "id": "ktServerlessEmergencyReleases" }, { - "id": "kibDevTutorialsContentManagementOnboarding" + "id": "ktCustomServerlessImage" }, { "id": "kibDevTutorialsServerlessProjectNavigation" @@ -194,13 +188,54 @@ ] }, { - "label": "Serverless", + "label": "Operations", "items": [ { - "id": "ktServerlessReleaseOverview" + "id": "kibDevDocsOpsOverview", + "label": "Overview" + } + ] + }, + { + "label": "Shared UX", + "pageId": "kibDevDocsSharedUxOverview", + "items": [ + { + "label": "Serverless Project Navigation", + "id": "kibDevTutorialsServerlessProjectNavigation" }, { - "id": "ktCustomServerlessImage" + "id": "kibDevTutorialFileService", + "label": "File service" + }, + { + "id": "kibDevSharePluginReadme", + "label": "Sharing" + }, + { + "label": "Content Management", + "id": "kibContentManagement", + "items": [ + { + "id": "kibDevTutorialsContentManagementOnboarding" + } + ] + }, + { + "id": "kibDevTutorialScreenshotting", + "label": "Screenshotting" + }, + { + "id": "kibDevTutorialAdvancedSettings", + "label": "Advanced Settings" + }, + { + "id": "kibDevDocsKPTTutorial", + "label": "Kibana Page Template" + }, + { + "id": "kibDevReactKibanaContext", + "label": "Kibana React Contexts" } ] }, diff --git a/package.json b/package.json index f91f9061ec9e2..f2446fc5c27a0 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "dashboarding" ], "private": true, - "version": "8.12.0", + "version": "8.13.0", "branch": "main", "types": "./kibana.d.ts", "tsdocMetadata": "./build/tsdoc-metadata.json", @@ -98,11 +98,11 @@ "@dnd-kit/utilities": "^2.0.0", "@elastic/apm-rum": "^5.15.0", "@elastic/apm-rum-react": "^2.0.1", - "@elastic/charts": "60.0.0", + "@elastic/charts": "61.0.3", "@elastic/datemath": "5.0.3", "@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@8.9.1-canary.1", "@elastic/ems-client": "8.5.1", - "@elastic/eui": "90.0.1-backport.0", + "@elastic/eui": "91.0.0-backport.0", "@elastic/filesaver": "1.1.2", "@elastic/node-crypto": "1.2.1", "@elastic/numeral": "^2.5.1", @@ -168,6 +168,7 @@ "@kbn/assetManager-plugin": "link:x-pack/plugins/asset_manager", "@kbn/audit-log-plugin": "link:x-pack/test/security_api_integration/plugins/audit_log", "@kbn/banners-plugin": "link:x-pack/plugins/banners", + "@kbn/bfetch-error": "link:packages/kbn-bfetch-error", "@kbn/bfetch-explorer-plugin": "link:examples/bfetch_explorer", "@kbn/bfetch-plugin": "link:src/plugins/bfetch", "@kbn/calculate-auto": "link:packages/kbn-calculate-auto", @@ -415,6 +416,7 @@ "@kbn/es-query": "link:packages/kbn-es-query", "@kbn/es-types": "link:packages/kbn-es-types", "@kbn/es-ui-shared-plugin": "link:src/plugins/es_ui_shared", + "@kbn/eso-model-version-example": "link:examples/eso_model_version_example", "@kbn/eso-plugin": "link:x-pack/test/encrypted_saved_objects_api_integration/plugins/api_consumer_plugin", "@kbn/event-annotation-common": "link:packages/kbn-event-annotation-common", "@kbn/event-annotation-components": "link:packages/kbn-event-annotation-components", @@ -627,6 +629,7 @@ "@kbn/response-stream-plugin": "link:examples/response_stream", "@kbn/rison": "link:packages/kbn-rison", "@kbn/rollup-plugin": "link:x-pack/plugins/rollup", + "@kbn/router-utils": "link:packages/kbn-router-utils", "@kbn/routing-example-plugin": "link:examples/routing_example", "@kbn/rrule": "link:packages/kbn-rrule", "@kbn/rule-data-utils": "link:packages/kbn-rule-data-utils", @@ -653,6 +656,7 @@ "@kbn/screenshotting-plugin": "link:x-pack/plugins/screenshotting", "@kbn/search-api-panels": "link:packages/kbn-search-api-panels", "@kbn/search-connectors": "link:packages/kbn-search-connectors", + "@kbn/search-errors": "link:packages/kbn-search-errors", "@kbn/search-examples-plugin": "link:examples/search_examples", "@kbn/search-index-documents": "link:packages/kbn-search-index-documents", "@kbn/search-response-warnings": "link:packages/kbn-search-response-warnings", @@ -839,6 +843,7 @@ "@kbn/vis-type-vislib-plugin": "link:src/plugins/vis_types/vislib", "@kbn/vis-type-xy-plugin": "link:src/plugins/vis_types/xy", "@kbn/visualization-ui-components": "link:packages/kbn-visualization-ui-components", + "@kbn/visualization-utils": "link:packages/kbn-visualization-utils", "@kbn/visualizations-plugin": "link:src/plugins/visualizations", "@kbn/watcher-plugin": "link:x-pack/plugins/watcher", "@kbn/xstate-utils": "link:packages/kbn-xstate-utils", @@ -878,7 +883,7 @@ "JSONStream": "1.3.5", "adm-zip": "^0.5.9", "ajv": "^8.12.0", - "ansi-regex": "^5.0.1", + "ansi-regex": "^6.0.1", "antlr4ts": "^0.5.0-alpha.3", "archiver": "^5.3.1", "async": "^3.2.3", @@ -893,7 +898,7 @@ "canvg": "^3.0.9", "cbor-x": "^1.3.3", "chalk": "^4.1.0", - "cheerio": "^1.0.0-rc.10", + "cheerio": "^1.0.0-rc.12", "chroma-js": "^2.1.0", "classnames": "2.2.6", "color": "^4.2.3", @@ -901,7 +906,7 @@ "compare-versions": "3.5.1", "constate": "^3.3.2", "copy-to-clipboard": "^3.0.8", - "core-js": "^3.31.0", + "core-js": "^3.34.0", "cronstrue": "^1.51.0", "css-box-model": "^1.2.1", "css.escape": "^1.5.1", @@ -922,13 +927,15 @@ "deep-freeze-strict": "^1.1.1", "deepmerge": "^4.2.2", "del": "^6.1.0", - "elastic-apm-node": "^4.2.0", + "diff": "^5.1.0", + "elastic-apm-node": "^4.3.0", "email-addresses": "^5.0.0", "execa": "^5.1.1", "expiry-js": "0.1.7", "exponential-backoff": "^3.1.1", "extract-zip": "^2.0.1", "fast-deep-equal": "^3.1.1", + "fast-glob": "^3.3.2", "fflate": "^0.6.9", "file-saver": "^1.3.8", "fnv-plus": "^1.3.1", @@ -969,7 +976,7 @@ "json-stable-stringify": "^1.0.1", "json-stringify-pretty-compact": "1.2.0", "json-stringify-safe": "5.0.1", - "jsonwebtoken": "^9.0.0", + "jsonwebtoken": "^9.0.2", "jsts": "^1.6.2", "kea": "^2.6.0", "langchain": "^0.0.186", @@ -1029,6 +1036,7 @@ "react": "^17.0.2", "react-ace": "^7.0.5", "react-color": "^2.13.8", + "react-diff-view": "^3.2.0", "react-dom": "^17.0.2", "react-dropzone": "^4.2.9", "react-fast-compare": "^2.0.4", @@ -1089,6 +1097,7 @@ "type-detect": "^4.0.8", "typescript-fsa": "^3.0.0", "typescript-fsa-reducers": "^1.2.2", + "unidiff": "^1.0.4", "unified": "9.2.2", "use-resize-observer": "^9.1.0", "usng.js": "^0.4.5", @@ -1168,6 +1177,7 @@ "@kbn/ci-stats-reporter": "link:packages/kbn-ci-stats-reporter", "@kbn/ci-stats-shipper-cli": "link:packages/kbn-ci-stats-shipper-cli", "@kbn/cli-dev-mode": "link:packages/kbn-cli-dev-mode", + "@kbn/code-owners": "link:packages/kbn-code-owners", "@kbn/core-analytics-browser-mocks": "link:packages/core/analytics/core-analytics-browser-mocks", "@kbn/core-analytics-server-mocks": "link:packages/core/analytics/core-analytics-server-mocks", "@kbn/core-application-browser-mocks": "link:packages/core/application/core-application-browser-mocks", @@ -1241,6 +1251,7 @@ "@kbn/failed-test-reporter-cli": "link:packages/kbn-failed-test-reporter-cli", "@kbn/find-used-node-modules": "link:packages/kbn-find-used-node-modules", "@kbn/ftr-common-functional-services": "link:packages/kbn-ftr-common-functional-services", + "@kbn/ftr-common-functional-ui-services": "link:packages/kbn-ftr-common-functional-ui-services", "@kbn/ftr-screenshot-filename": "link:packages/kbn-ftr-screenshot-filename", "@kbn/generate": "link:packages/kbn-generate", "@kbn/get-repo-files": "link:packages/kbn-get-repo-files", @@ -1330,6 +1341,7 @@ "@types/chroma-js": "^2.1.0", "@types/chromedriver": "^81.0.5", "@types/classnames": "^2.2.9", + "@types/cli-progress": "^3.11.5", "@types/color": "^3.0.3", "@types/cytoscape": "^3.14.0", "@types/d3": "^3.5.43", @@ -1345,6 +1357,7 @@ "@types/dedent": "^0.7.0", "@types/deep-freeze-strict": "^1.1.0", "@types/delete-empty": "^2.0.0", + "@types/diff": "^5.0.8", "@types/ejs": "^3.0.6", "@types/enzyme": "^3.10.12", "@types/eslint": "^8.44.2", @@ -1435,7 +1448,7 @@ "@types/redux-logger": "^3.0.8", "@types/resolve": "^1.20.1", "@types/seedrandom": ">=2.0.0 <4.0.0", - "@types/selenium-webdriver": "^4.1.20", + "@types/selenium-webdriver": "^4.1.21", "@types/semver": "^7", "@types/set-value": "^2.0.0", "@types/sharp": "^0.30.4", @@ -1489,8 +1502,9 @@ "blob-polyfill": "^7.0.20220408", "callsites": "^3.1.0", "chance": "1.0.18", - "chromedriver": "^119.0.1", + "chromedriver": "^120.0.0", "clean-webpack-plugin": "^3.0.0", + "cli-progress": "^3.12.0", "cli-table3": "^0.6.1", "copy-webpack-plugin": "^6.0.2", "cpy": "^8.1.1", @@ -1508,7 +1522,6 @@ "debug": "^2.6.9", "delete-empty": "^2.0.0", "dependency-check": "^4.1.0", - "diff": "^4.0.1", "dpdm": "3.9.0", "ejs": "^3.1.8", "enzyme": "^3.11.0", @@ -1538,7 +1551,7 @@ "file-loader": "^4.2.0", "find-cypress-specs": "^1.35.1", "form-data": "^4.0.0", - "geckodriver": "^4.2.1", + "geckodriver": "^4.3.0", "gulp-brotli": "^3.0.0", "gulp-postcss": "^9.0.1", "gulp-sourcemaps": "2.6.5", @@ -1548,6 +1561,7 @@ "html": "1.0.0", "html-loader": "^1.3.2", "http-proxy": "^1.18.1", + "ignore": "^5.3.0", "is-path-inside": "^3.0.2", "jest": "^29.6.1", "jest-axe": "^5.0.0", @@ -1613,7 +1627,7 @@ "resolve": "^1.22.0", "rxjs-marbles": "^7.0.1", "sass-loader": "^10.4.1", - "selenium-webdriver": "^4.15.0", + "selenium-webdriver": "^4.16.0", "simple-git": "^3.16.0", "sinon": "^7.4.2", "sort-package-json": "^1.53.1", @@ -1627,9 +1641,10 @@ "supertest": "^6.3.3", "supports-color": "^7.0.0", "svgo": "^2.8.0", + "table": "^6.8.1", "tape": "^5.0.1", "tempy": "^0.3.0", - "terser": "^5.16.5", + "terser": "^5.26.0", "terser-webpack-plugin": "^4.2.3", "tough-cookie": "^4.1.2", "tree-kill": "^1.2.2", diff --git a/packages/core/analytics/core-analytics-browser-internal/src/analytics_service.ts b/packages/core/analytics/core-analytics-browser-internal/src/analytics_service.ts index 60656e9dfd1cb..fc394702eadfa 100644 --- a/packages/core/analytics/core-analytics-browser-internal/src/analytics_service.ts +++ b/packages/core/analytics/core-analytics-browser-internal/src/analytics_service.ts @@ -184,6 +184,10 @@ export class AnalyticsService { type: 'keyword', _meta: { description: 'The Cluster version', optional: true }, }, + cluster_build_flavor: { + type: 'keyword', + _meta: { description: 'The Cluster build flavor', optional: true }, + }, }, }); } diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/header/__snapshots__/collapsible_nav.test.tsx.snap b/packages/core/chrome/core-chrome-browser-internal/src/ui/header/__snapshots__/collapsible_nav.test.tsx.snap index 416ce39bbf9ba..f505901f37abb 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/header/__snapshots__/collapsible_nav.test.tsx.snap +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/header/__snapshots__/collapsible_nav.test.tsx.snap @@ -157,7 +157,7 @@ Array [ aria-labelledby="generated-id" class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isOpen" id="generated-id" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > @@ -281,7 +281,7 @@ Array [ aria-labelledby="generated-id" class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isOpen" id="generated-id" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > @@ -412,7 +412,7 @@ Array [ aria-labelledby="generated-id" class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isOpen" id="generated-id" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > @@ -526,7 +526,7 @@ Array [ aria-labelledby="generated-id" class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isOpen" id="generated-id" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > @@ -623,7 +623,7 @@ Array [ aria-labelledby="generated-id" class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isOpen" id="generated-id" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > diff --git a/packages/core/chrome/core-chrome-browser-internal/src/ui/header/__snapshots__/header.test.tsx.snap b/packages/core/chrome/core-chrome-browser-internal/src/ui/header/__snapshots__/header.test.tsx.snap index 689888d4d82a5..209e2f1d0c113 100644 --- a/packages/core/chrome/core-chrome-browser-internal/src/ui/header/__snapshots__/header.test.tsx.snap +++ b/packages/core/chrome/core-chrome-browser-internal/src/ui/header/__snapshots__/header.test.tsx.snap @@ -108,37 +108,33 @@ Array [ class="euiHeaderSectionItem emotion-euiHeaderSectionItem" >
- + +
{ let injectedMetadata: ReturnType; + let coreContext: ReturnType; let service: DocLinksService; beforeEach(() => { @@ -26,7 +28,8 @@ describe('DocLinksService', () => { settings: 'http://settings.test.url', }); - service = new DocLinksService(); + coreContext = coreContextMock.create(); + service = new DocLinksService(coreContext); }); afterEach(() => { @@ -43,6 +46,7 @@ describe('DocLinksService', () => { expect(getDocLinksMetaMock).toHaveBeenCalledTimes(1); expect(getDocLinksMetaMock).toHaveBeenCalledWith({ kibanaBranch: 'test-branch', + buildFlavor: coreContext.env.packageInfo.buildFlavor, }); }); @@ -64,6 +68,7 @@ describe('DocLinksService', () => { expect(getDocLinksMock).toHaveBeenCalledTimes(1); expect(getDocLinksMock).toHaveBeenCalledWith({ kibanaBranch: 'test-branch', + buildFlavor: coreContext.env.packageInfo.buildFlavor, }); }); diff --git a/packages/core/doc-links/core-doc-links-browser-internal/src/doc_links_service.ts b/packages/core/doc-links/core-doc-links-browser-internal/src/doc_links_service.ts index 115d41e1ee930..a5d6fe17ddf63 100644 --- a/packages/core/doc-links/core-doc-links-browser-internal/src/doc_links_service.ts +++ b/packages/core/doc-links/core-doc-links-browser-internal/src/doc_links_service.ts @@ -7,6 +7,7 @@ */ import { getDocLinks, getDocLinksMeta } from '@kbn/doc-links'; +import type { CoreContext } from '@kbn/core-base-browser-internal'; import type { InternalInjectedMetadataSetup } from '@kbn/core-injected-metadata-browser-internal'; import type { DocLinksStart } from '@kbn/core-doc-links-browser'; @@ -17,12 +18,15 @@ export interface DocLinksServiceStartDeps { /** @internal */ export class DocLinksService { + constructor(private readonly coreContext: CoreContext) {} + public setup() {} public start({ injectedMetadata }: DocLinksServiceStartDeps): DocLinksStart { const kibanaBranch = injectedMetadata.getKibanaBranch(); - const docMeta = getDocLinksMeta({ kibanaBranch }); - const docLinks = getDocLinks({ kibanaBranch }); + const buildFlavor = this.coreContext.env.packageInfo.buildFlavor; + const docMeta = getDocLinksMeta({ kibanaBranch, buildFlavor }); + const docLinks = getDocLinks({ kibanaBranch, buildFlavor }); return { DOC_LINK_VERSION: docMeta.version, diff --git a/packages/core/doc-links/core-doc-links-browser-internal/tsconfig.json b/packages/core/doc-links/core-doc-links-browser-internal/tsconfig.json index dda4c975120d7..45980da56c3da 100644 --- a/packages/core/doc-links/core-doc-links-browser-internal/tsconfig.json +++ b/packages/core/doc-links/core-doc-links-browser-internal/tsconfig.json @@ -15,6 +15,8 @@ "@kbn/core-injected-metadata-browser-internal", "@kbn/core-doc-links-browser", "@kbn/core-injected-metadata-browser-mocks", + "@kbn/core-base-browser-mocks", + "@kbn/core-base-browser-internal", ], "exclude": [ "target/**/*", diff --git a/packages/core/doc-links/core-doc-links-browser-mocks/src/doc_links_service.mock.ts b/packages/core/doc-links/core-doc-links-browser-mocks/src/doc_links_service.mock.ts index 03c394aa926e8..ef62bd97eca19 100644 --- a/packages/core/doc-links/core-doc-links-browser-mocks/src/doc_links_service.mock.ts +++ b/packages/core/doc-links/core-doc-links-browser-mocks/src/doc_links_service.mock.ts @@ -7,6 +7,7 @@ */ import type { PublicMethodsOf } from '@kbn/utility-types'; +import { coreContextMock } from '@kbn/core-base-browser-mocks'; import { injectedMetadataServiceMock } from '@kbn/core-injected-metadata-browser-mocks'; import type { DocLinksStart } from '@kbn/core-doc-links-browser'; import { DocLinksService } from '@kbn/core-doc-links-browser-internal'; @@ -15,7 +16,7 @@ const createStartContractMock = (): DocLinksStart => { // This service is so simple that we actually use the real implementation const injectedMetadata = injectedMetadataServiceMock.createStartContract(); injectedMetadata.getKibanaBranch.mockReturnValue('mocked-test-branch'); - return new DocLinksService().start({ injectedMetadata }); + return new DocLinksService(coreContextMock.create()).start({ injectedMetadata }); }; type DocLinksServiceContract = PublicMethodsOf; diff --git a/packages/core/doc-links/core-doc-links-browser-mocks/tsconfig.json b/packages/core/doc-links/core-doc-links-browser-mocks/tsconfig.json index 473ae750e67ee..e60609b94a11b 100644 --- a/packages/core/doc-links/core-doc-links-browser-mocks/tsconfig.json +++ b/packages/core/doc-links/core-doc-links-browser-mocks/tsconfig.json @@ -14,7 +14,8 @@ "@kbn/utility-types", "@kbn/core-injected-metadata-browser-mocks", "@kbn/core-doc-links-browser", - "@kbn/core-doc-links-browser-internal" + "@kbn/core-doc-links-browser-internal", + "@kbn/core-base-browser-mocks" ], "exclude": [ "target/**/*", diff --git a/packages/core/doc-links/core-doc-links-server-internal/src/doc_links_service.test.ts b/packages/core/doc-links/core-doc-links-server-internal/src/doc_links_service.test.ts index 6b7c7d3b33f3a..13c75fa0f6d25 100644 --- a/packages/core/doc-links/core-doc-links-server-internal/src/doc_links_service.test.ts +++ b/packages/core/doc-links/core-doc-links-server-internal/src/doc_links_service.test.ts @@ -41,6 +41,7 @@ describe('DocLinksService', () => { expect(getDocLinksMetaMock).toHaveBeenCalledTimes(1); expect(getDocLinksMetaMock).toHaveBeenCalledWith({ kibanaBranch: coreContext.env.packageInfo.branch, + buildFlavor: coreContext.env.packageInfo.buildFlavor, }); }); @@ -62,6 +63,7 @@ describe('DocLinksService', () => { expect(getDocLinksMock).toHaveBeenCalledTimes(1); expect(getDocLinksMock).toHaveBeenCalledWith({ kibanaBranch: coreContext.env.packageInfo.branch, + buildFlavor: coreContext.env.packageInfo.buildFlavor, }); }); diff --git a/packages/core/doc-links/core-doc-links-server-internal/src/doc_links_service.ts b/packages/core/doc-links/core-doc-links-server-internal/src/doc_links_service.ts index 3201bcd0256a4..ae11657666864 100644 --- a/packages/core/doc-links/core-doc-links-server-internal/src/doc_links_service.ts +++ b/packages/core/doc-links/core-doc-links-server-internal/src/doc_links_service.ts @@ -12,16 +12,16 @@ import type { DocLinksServiceSetup, DocLinksServiceStart } from '@kbn/core-doc-l /** @internal */ export class DocLinksService { - private readonly kibanaBranch: string; private docLinks?: DocLinksServiceSetup; - constructor(core: CoreContext) { - this.kibanaBranch = core.env.packageInfo.branch; - } + constructor(private readonly coreContext: CoreContext) {} public setup(): DocLinksServiceSetup { - const docMeta = getDocLinksMeta({ kibanaBranch: this.kibanaBranch }); - const docLinks = getDocLinks({ kibanaBranch: this.kibanaBranch }); + const kibanaBranch = this.coreContext.env.packageInfo.branch; + const buildFlavor = this.coreContext.env.packageInfo.buildFlavor; + + const docMeta = getDocLinksMeta({ kibanaBranch, buildFlavor }); + const docLinks = getDocLinks({ kibanaBranch, buildFlavor }); this.docLinks = { ...docMeta, links: docLinks, diff --git a/packages/core/doc-links/core-doc-links-server-mocks/src/doc_links_service.mock.ts b/packages/core/doc-links/core-doc-links-server-mocks/src/doc_links_service.mock.ts index d43c14c7fe8c3..b0ed04298dd1c 100644 --- a/packages/core/doc-links/core-doc-links-server-mocks/src/doc_links_service.mock.ts +++ b/packages/core/doc-links/core-doc-links-server-mocks/src/doc_links_service.mock.ts @@ -15,9 +15,10 @@ type DocLinksServiceContract = PublicMethodsOf; const createSetupMock = (): DocLinksServiceSetup => { const branch = 'test-branch'; + const buildFlavor = 'traditional'; return { - ...getDocLinksMeta({ kibanaBranch: branch }), - links: getDocLinks({ kibanaBranch: branch }), + ...getDocLinksMeta({ kibanaBranch: branch, buildFlavor }), + links: getDocLinks({ kibanaBranch: branch, buildFlavor }), }; }; diff --git a/packages/core/fatal-errors/core-fatal-errors-browser-internal/src/__snapshots__/fatal_errors_service.test.ts.snap b/packages/core/fatal-errors/core-fatal-errors-browser-internal/src/__snapshots__/fatal_errors_service.test.ts.snap index c88e342110c13..755095d444dce 100644 --- a/packages/core/fatal-errors/core-fatal-errors-browser-internal/src/__snapshots__/fatal_errors_service.test.ts.snap +++ b/packages/core/fatal-errors/core-fatal-errors-browser-internal/src/__snapshots__/fatal_errors_service.test.ts.snap @@ -19,6 +19,7 @@ Array [ } theme={ Object { + "getTheme": [MockFunction], "theme$": Rx.Observable, } } diff --git a/packages/core/i18n/core-i18n-browser-internal/src/__snapshots__/i18n_service.test.tsx.snap b/packages/core/i18n/core-i18n-browser-internal/src/__snapshots__/i18n_service.test.tsx.snap index 39aacd87efc9e..610656fa96f46 100644 --- a/packages/core/i18n/core-i18n-browser-internal/src/__snapshots__/i18n_service.test.tsx.snap +++ b/packages/core/i18n/core-i18n-browser-internal/src/__snapshots__/i18n_service.test.tsx.snap @@ -7,6 +7,7 @@ exports[`#start() returns \`Context\` component 1`] = ` Object { "mapping": Object { "euiAbsoluteTab.dateFormatError": [Function], + "euiAbsoluteTab.dateFormatHint": "Press the Enter key to parse as a date.", "euiAccordionChildrenLoading.message": "Loading", "euiAutoRefresh.autoRefreshLabel": "Auto refresh", "euiAutoRefresh.buttonLabelOff": "Auto refresh is off", diff --git a/packages/core/i18n/core-i18n-browser-internal/src/i18n_eui_mapping.tsx b/packages/core/i18n/core-i18n-browser-internal/src/i18n_eui_mapping.tsx index e76a38f74f6d5..a669a86bfda1c 100644 --- a/packages/core/i18n/core-i18n-browser-internal/src/i18n_eui_mapping.tsx +++ b/packages/core/i18n/core-i18n-browser-internal/src/i18n_eui_mapping.tsx @@ -1434,7 +1434,7 @@ export const getEuiContextMapping = (): EuiTokensObject => { ), 'euiAbsoluteTab.dateFormatError': ({ dateFormat }: EuiValues) => i18n.translate('core.euiAbsoluteTab.dateFormatError', { - defaultMessage: 'Expected format: {dateFormat}', + defaultMessage: 'Allowed formats: {dateFormat}, ISO 8601, RFC 2822, or Unix timestamp.', values: { dateFormat }, }), 'euiRelativeTab.fullDescription': ({ unit }: EuiValues) => @@ -1454,6 +1454,9 @@ export const getEuiContextMapping = (): EuiTokensObject => { 'euiRelativeTab.dateInputError': i18n.translate('core.euiRelativeTab.dateInputError', { defaultMessage: 'Must be a valid range', }), + 'euiAbsoluteTab.dateFormatHint': i18n.translate('core.euiAbsoluteTab.dateFormatHint', { + defaultMessage: 'Press the Enter key to parse as a date.', + }), 'euiResizableButton.horizontalResizerAriaLabel': i18n.translate( 'core.euiResizableButton.horizontalResizerAriaLabel', { diff --git a/packages/core/injected-metadata/core-injected-metadata-common-internal/src/types.ts b/packages/core/injected-metadata/core-injected-metadata-common-internal/src/types.ts index dd9d6bced5c98..01b46679c7452 100644 --- a/packages/core/injected-metadata/core-injected-metadata-common-internal/src/types.ts +++ b/packages/core/injected-metadata/core-injected-metadata-common-internal/src/types.ts @@ -16,6 +16,7 @@ export interface InjectedMetadataClusterInfo { cluster_uuid?: string; cluster_name?: string; cluster_version?: string; + cluster_build_flavor?: string; } /** @internal */ diff --git a/packages/core/notifications/core-notifications-browser-internal/src/toasts/__snapshots__/toasts_service.test.tsx.snap b/packages/core/notifications/core-notifications-browser-internal/src/toasts/__snapshots__/toasts_service.test.tsx.snap index a86cd8f38df52..f21c83f026521 100644 --- a/packages/core/notifications/core-notifications-browser-internal/src/toasts/__snapshots__/toasts_service.test.tsx.snap +++ b/packages/core/notifications/core-notifications-browser-internal/src/toasts/__snapshots__/toasts_service.test.tsx.snap @@ -25,6 +25,7 @@ Array [ } theme={ Object { + "getTheme": [MockFunction], "theme$": Observable { "_subscribe": [Function], }, diff --git a/packages/core/overlays/core-overlays-browser-internal/src/flyout/__snapshots__/flyout_service.test.tsx.snap b/packages/core/overlays/core-overlays-browser-internal/src/flyout/__snapshots__/flyout_service.test.tsx.snap index 6908a64f66441..bb50e7397a729 100644 --- a/packages/core/overlays/core-overlays-browser-internal/src/flyout/__snapshots__/flyout_service.test.tsx.snap +++ b/packages/core/overlays/core-overlays-browser-internal/src/flyout/__snapshots__/flyout_service.test.tsx.snap @@ -33,6 +33,7 @@ Array [ } theme={ Object { + "getTheme": [MockFunction], "theme$": Observable { "_subscribe": [Function], }, @@ -147,6 +148,7 @@ Array [ } theme={ Object { + "getTheme": [MockFunction], "theme$": Observable { "_subscribe": [Function], }, @@ -254,6 +256,7 @@ Array [ } theme={ Object { + "getTheme": [MockFunction], "theme$": Observable { "_subscribe": [Function], }, diff --git a/packages/core/overlays/core-overlays-browser-internal/src/modal/__snapshots__/modal_service.test.tsx.snap b/packages/core/overlays/core-overlays-browser-internal/src/modal/__snapshots__/modal_service.test.tsx.snap index a3b098adbbd8e..c93fae64cf361 100644 --- a/packages/core/overlays/core-overlays-browser-internal/src/modal/__snapshots__/modal_service.test.tsx.snap +++ b/packages/core/overlays/core-overlays-browser-internal/src/modal/__snapshots__/modal_service.test.tsx.snap @@ -100,6 +100,7 @@ Array [ } theme={ Object { + "getTheme": [MockFunction], "theme$": Observable { "_subscribe": [Function], }, @@ -361,6 +362,7 @@ Array [ } theme={ Object { + "getTheme": [MockFunction], "theme$": Observable { "_subscribe": [Function], }, @@ -677,6 +679,7 @@ Array [ } theme={ Object { + "getTheme": [MockFunction], "theme$": Observable { "_subscribe": [Function], }, @@ -914,6 +917,7 @@ Array [ } theme={ Object { + "getTheme": [MockFunction], "theme$": Observable { "_subscribe": [Function], }, @@ -1156,6 +1160,7 @@ Array [ } theme={ Object { + "getTheme": [MockFunction], "theme$": Observable { "_subscribe": [Function], }, @@ -1393,6 +1398,7 @@ Array [ } theme={ Object { + "getTheme": [MockFunction], "theme$": Observable { "_subscribe": [Function], }, @@ -1438,6 +1444,7 @@ Array [ } theme={ Object { + "getTheme": [MockFunction], "theme$": Observable { "_subscribe": [Function], }, @@ -1581,6 +1588,7 @@ Array [ } theme={ Object { + "getTheme": [MockFunction], "theme$": Observable { "_subscribe": [Function], }, @@ -1688,6 +1696,7 @@ Array [ } theme={ Object { + "getTheme": [MockFunction], "theme$": Observable { "_subscribe": [Function], }, @@ -1800,6 +1809,7 @@ Array [ } theme={ Object { + "getTheme": [MockFunction], "theme$": Observable { "_subscribe": [Function], }, @@ -1907,6 +1917,7 @@ Array [ } theme={ Object { + "getTheme": [MockFunction], "theme$": Observable { "_subscribe": [Function], }, diff --git a/packages/core/plugins/core-plugins-server-internal/index.ts b/packages/core/plugins/core-plugins-server-internal/index.ts index f8971b0b2b0fc..aa918dea16afa 100644 --- a/packages/core/plugins/core-plugins-server-internal/index.ts +++ b/packages/core/plugins/core-plugins-server-internal/index.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -export { PluginsService, PluginWrapper, config, isNewPlatformPlugin } from './src'; +export { PluginsService, PluginWrapper, config } from './src'; export type { InternalPluginsServiceSetup, InternalPluginsServiceStart, diff --git a/packages/core/plugins/core-plugins-server-internal/src/discovery/index.ts b/packages/core/plugins/core-plugins-server-internal/src/discovery/index.ts index 73e6835f49bf8..5349df1927f72 100644 --- a/packages/core/plugins/core-plugins-server-internal/src/discovery/index.ts +++ b/packages/core/plugins/core-plugins-server-internal/src/discovery/index.ts @@ -9,6 +9,4 @@ /** @internal */ export { PluginDiscoveryError, PluginDiscoveryErrorType } from './plugin_discovery_error'; /** @internal */ -export { isNewPlatformPlugin } from './plugin_manifest_parser'; -/** @internal */ export { discover } from './plugins_discovery'; diff --git a/packages/core/plugins/core-plugins-server-internal/src/discovery/plugin_manifest_parser.ts b/packages/core/plugins/core-plugins-server-internal/src/discovery/plugin_manifest_parser.ts index 6aef278cb00d1..0186b1c39755d 100644 --- a/packages/core/plugins/core-plugins-server-internal/src/discovery/plugin_manifest_parser.ts +++ b/packages/core/plugins/core-plugins-server-internal/src/discovery/plugin_manifest_parser.ts @@ -6,20 +6,18 @@ * Side Public License, v 1. */ -import { readFile, stat } from 'fs'; +import { readFile } from 'fs'; import { resolve } from 'path'; import { coerce } from 'semver'; -import { promisify } from 'util'; import { snakeCase } from 'lodash'; import { isConfigPath, PackageInfo } from '@kbn/config'; import { PluginType } from '@kbn/core-base-common'; import { PluginManifest } from '@kbn/core-plugins-server'; +import { promisify } from 'util'; import { PluginDiscoveryError } from './plugin_discovery_error'; import { isCamelCase } from './is_camel_case'; const fsReadFileAsync = promisify(readFile); -const fsStatAsync = promisify(stat); - /** * Name of the JSON manifest file that should be located in the plugin directory. */ @@ -222,21 +220,6 @@ export async function parseManifest( }; } -/** - * Checks whether specified folder contains Kibana new platform plugin. It's only - * intended to be used by the legacy systems when they need to check whether specific - * plugin path is handled by the core plugin system or not. - * @param pluginPath Path to the plugin. - * @internal - */ -export async function isNewPlatformPlugin(pluginPath: string) { - try { - return (await fsStatAsync(resolve(pluginPath, MANIFEST_FILE_NAME))).isFile(); - } catch (err) { - return false; - } -} - /** * Checks whether plugin expected Kibana version is compatible with the used Kibana version. * @param expectedKibanaVersion Kibana version expected by the plugin. diff --git a/packages/core/plugins/core-plugins-server-internal/src/index.ts b/packages/core/plugins/core-plugins-server-internal/src/index.ts index b0b1e58fba107..1ff60a83abe44 100644 --- a/packages/core/plugins/core-plugins-server-internal/src/index.ts +++ b/packages/core/plugins/core-plugins-server-internal/src/index.ts @@ -13,7 +13,5 @@ export type { DiscoveredPlugins, } from './plugins_service'; export { config } from './plugins_config'; -/** @internal */ -export { isNewPlatformPlugin } from './discovery'; export type { PluginDependencies } from './types'; export { PluginWrapper } from './plugin'; diff --git a/packages/core/root/core-root-browser-internal/src/core_system.ts b/packages/core/root/core-root-browser-internal/src/core_system.ts index 3dcef6e3858cd..4c50aaf640e2a 100644 --- a/packages/core/root/core-root-browser-internal/src/core_system.ts +++ b/packages/core/root/core-root-browser-internal/src/core_system.ts @@ -141,7 +141,7 @@ export class CoreSystem { browserSupportsCsp, kibanaVersion: injectedMetadata.version, }); - this.docLinks = new DocLinksService(); + this.docLinks = new DocLinksService(this.coreContext); this.rendering = new RenderingService(); this.application = new ApplicationService(); this.integrations = new IntegrationsService(); diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/README.md b/packages/core/saved-objects/core-saved-objects-api-server-internal/README.md index b25fd29f5441c..5adcfcfea3750 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/README.md +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/README.md @@ -28,4 +28,7 @@ helpers to have to pass all the parameters again. ### lib/apis/internals I would call them 'utilities with business logic'. These are the 'big' chunks of logic called by the APIs. -E.g preflightCheckForCreate, internalBulkResolve and so on. \ No newline at end of file +E.g preflightCheckForCreate, internalBulkResolve and so on. + +### Unit tests +Unit tests should be kept close to the implementation. While not always possible to customize test fixtures and mocks completely, it is perfectly acceptible to duplicate code, with a focus on code coverage. diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_create.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_create.test.ts index 3faeee08048ef..91df493dcad88 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_create.test.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_create.test.ts @@ -56,7 +56,6 @@ import { mockTimestampFieldsWithCreated, } from '../../test_helpers/repository.test.common'; -// BEWARE: The SavedObjectClient depends on the implementation details of the SavedObjectsRepository // so any breaking changes to this repository are considered breaking changes to the SavedObjectsClient. interface ExpectedErrorResult { @@ -65,7 +64,7 @@ interface ExpectedErrorResult { error: Record; } -describe('SavedObjectsRepository', () => { +describe('#bulkCreate', () => { let client: ReturnType; let repository: SavedObjectsRepository; let migrator: ReturnType; @@ -135,7 +134,7 @@ describe('SavedObjectsRepository', () => { references: [{ name: 'search_0', type: 'search', id: '123' }], }); - describe('#bulkCreate', () => { + describe('performBulkCreate', () => { beforeEach(() => { mockPreflightCheckForCreate.mockReset(); mockPreflightCheckForCreate.mockImplementation(({ objects }) => { diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_delete.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_delete.test.ts new file mode 100644 index 0000000000000..15b7cf5164abb --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_delete.test.ts @@ -0,0 +1,600 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* eslint-disable @typescript-eslint/no-shadow */ + +import { + pointInTimeFinderMock, + mockGetBulkOperationError, + mockGetCurrentTime, + mockDeleteLegacyUrlAliases, + mockGetSearchDsl, +} from '../repository.test.mock'; + +import type { Payload } from '@hapi/boom'; +import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; + +import type { + SavedObjectsBulkDeleteObject, + SavedObjectsBulkDeleteOptions, +} from '@kbn/core-saved-objects-api-server'; +import { ALL_NAMESPACES_STRING } from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server'; +import { SavedObjectsRepository } from '../repository'; +import { loggerMock } from '@kbn/logging-mocks'; +import { SavedObjectsSerializer } from '@kbn/core-saved-objects-base-server-internal'; +import { kibanaMigratorMock } from '../../mocks'; +import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; + +import { + NAMESPACE_AGNOSTIC_TYPE, + MULTI_NAMESPACE_TYPE, + MULTI_NAMESPACE_ISOLATED_TYPE, + HIDDEN_TYPE, + mockTimestamp, + mappings, + createRegistry, + createDocumentMigrator, + getMockMgetResponse, + type TypeIdTuple, + createSpySerializer, + expectErrorInvalidType, + expectErrorNotFound, + createBadRequestErrorPayload, + getMockEsBulkDeleteResponse, + bulkDeleteSuccess, + createBulkDeleteSuccessStatus, +} from '../../test_helpers/repository.test.common'; + +interface ExpectedErrorResult { + type: string; + id: string; + error: Record; +} + +describe('#bulkDelete', () => { + let client: ReturnType; + let repository: SavedObjectsRepository; + let migrator: ReturnType; + let logger: ReturnType; + let serializer: jest.Mocked; + + const registry = createRegistry(); + const documentMigrator = createDocumentMigrator(registry); + + beforeEach(() => { + pointInTimeFinderMock.mockClear(); + client = elasticsearchClientMock.createElasticsearchClient(); + migrator = kibanaMigratorMock.create(); + documentMigrator.prepareMigrations(); + migrator.migrateDocument = jest.fn().mockImplementation(documentMigrator.migrate); + migrator.runMigrations = jest.fn().mockResolvedValue([{ status: 'skipped' }]); + logger = loggerMock.create(); + + // create a mock serializer "shim" so we can track function calls, but use the real serializer's implementation + serializer = createSpySerializer(registry); + + const allTypes = registry.getAllTypes().map((type) => type.name); + const allowedTypes = [...new Set(allTypes.filter((type) => !registry.isHidden(type)))]; + + // @ts-expect-error must use the private constructor to use the mocked serializer + repository = new SavedObjectsRepository({ + index: '.kibana-test', + mappings, + client, + migrator, + typeRegistry: registry, + serializer, + allowedTypes, + logger, + }); + + mockGetCurrentTime.mockReturnValue(mockTimestamp); + mockGetSearchDsl.mockClear(); + }); + + // Setup migration mock for creating an object + + describe('performBulkDelete', () => { + const obj1: SavedObjectsBulkDeleteObject = { + type: 'config', + id: '6.0.0-alpha1', + }; + const obj2: SavedObjectsBulkDeleteObject = { + type: 'index-pattern', + id: 'logstash-*', + }; + + const namespace = 'foo-namespace'; + + const createNamespaceAwareGetId = (type: string, id: string) => + `${registry.isSingleNamespace(type) && namespace ? `${namespace}:` : ''}${type}:${id}`; + + // bulk delete calls only has one object for each source -- the action + const expectClientCallBulkDeleteArgsAction = ( + objects: TypeIdTuple[], + { + method, + _index = expect.any(String), + getId = () => expect.any(String), + overrides = {}, + }: { + method: string; + _index?: string; + getId?: (type: string, id: string) => string; + overrides?: Record; + } + ) => { + const body = []; + for (const { type, id } of objects) { + body.push({ + [method]: { + _index, + _id: getId(type, id), + ...overrides, + }, + }); + } + + expect(client.bulk).toHaveBeenCalledWith( + expect.objectContaining({ body }), + expect.anything() + ); + }; + + const createBulkDeleteFailStatus = ({ + type, + id, + error, + }: { + type: string; + id: string; + error?: ExpectedErrorResult['error']; + }) => ({ + type, + id, + success: false, + error: error ?? SavedObjectsErrorHelpers.createBadRequestError(), + }); + + // mocks a combination of success, error results for hidden and unknown object object types. + const repositoryBulkDeleteError = async ( + obj: SavedObjectsBulkDeleteObject, + isBulkError: boolean, + expectedErrorResult: ExpectedErrorResult + ) => { + const objects = [obj1, obj, obj2]; + const mockedBulkDeleteResponse = getMockEsBulkDeleteResponse(registry, objects); + if (isBulkError) { + mockGetBulkOperationError.mockReturnValueOnce(undefined); + mockGetBulkOperationError.mockReturnValueOnce(expectedErrorResult.error as Payload); + } + client.bulk.mockResponseOnce(mockedBulkDeleteResponse); + + const result = await repository.bulkDelete(objects); + expect(client.bulk).toHaveBeenCalled(); + expect(result).toEqual({ + statuses: [ + createBulkDeleteSuccessStatus(obj1), + createBulkDeleteFailStatus({ ...obj, error: expectedErrorResult.error }), + createBulkDeleteSuccessStatus(obj2), + ], + }); + }; + + const expectClientCallArgsAction = ( + objects: TypeIdTuple[], + { + method, + _index = expect.any(String), + getId = () => expect.any(String), + overrides = {}, + }: { + method: string; + _index?: string; + getId?: (type: string, id: string) => string; + overrides?: Record; + } + ) => { + const body = []; + for (const { type, id } of objects) { + body.push({ + [method]: { + _index, + _id: getId(type, id), + ...overrides, + }, + }); + } + expect(client.bulk).toHaveBeenCalledWith( + expect.objectContaining({ body }), + expect.anything() + ); + }; + + const bulkDeleteMultiNamespaceError = async ( + [obj1, _obj, obj2]: SavedObjectsBulkDeleteObject[], + options: SavedObjectsBulkDeleteOptions | undefined, + mgetResponse: estypes.MgetResponse, + mgetOptions?: { statusCode?: number } + ) => { + const getId = (type: string, id: string) => `${options?.namespace}:${type}:${id}`; + // mock the response for the not found doc + client.mget.mockResponseOnce(mgetResponse, { statusCode: mgetOptions?.statusCode }); + // get a mocked response for the valid docs + const bulkResponse = getMockEsBulkDeleteResponse(registry, [obj1, obj2], { namespace }); + client.bulk.mockResponseOnce(bulkResponse); + + const result = await repository.bulkDelete([obj1, _obj, obj2], options); + expect(client.bulk).toHaveBeenCalledTimes(1); + expect(client.mget).toHaveBeenCalledTimes(1); + + expectClientCallArgsAction([obj1, obj2], { method: 'delete', getId }); + expect(result).toEqual({ + statuses: [ + createBulkDeleteSuccessStatus(obj1), + { ...expectErrorNotFound(_obj), success: false }, + createBulkDeleteSuccessStatus(obj2), + ], + }); + }; + + beforeEach(() => { + mockDeleteLegacyUrlAliases.mockClear(); + mockDeleteLegacyUrlAliases.mockResolvedValue(); + }); + + describe('client calls', () => { + it(`should use the ES bulk action by default`, async () => { + await bulkDeleteSuccess(client, repository, registry, [obj1, obj2]); + expect(client.bulk).toHaveBeenCalled(); + }); + + it(`should use the ES mget action before bulk action for any types that are multi-namespace`, async () => { + const objects = [obj1, { ...obj2, type: MULTI_NAMESPACE_ISOLATED_TYPE }]; + await bulkDeleteSuccess(client, repository, registry, objects); + expect(client.bulk).toHaveBeenCalled(); + expect(client.mget).toHaveBeenCalled(); + + const docs = [ + expect.objectContaining({ _id: `${MULTI_NAMESPACE_ISOLATED_TYPE}:${obj2.id}` }), + ]; + expect(client.mget).toHaveBeenCalledWith( + expect.objectContaining({ body: { docs } }), + expect.anything() + ); + }); + + it(`should not use the ES bulk action when there are no valid documents to delete`, async () => { + const objects = [obj1, obj2].map((x) => ({ ...x, type: 'unknownType' })); + await repository.bulkDelete(objects); + expect(client.bulk).toHaveBeenCalledTimes(0); + }); + + it(`formats the ES request`, async () => { + const getId = createNamespaceAwareGetId; + await bulkDeleteSuccess(client, repository, registry, [obj1, obj2], { namespace }); + expectClientCallBulkDeleteArgsAction([obj1, obj2], { method: 'delete', getId }); + }); + + it(`formats the ES request for any types that are multi-namespace`, async () => { + const _obj2 = { ...obj2, type: MULTI_NAMESPACE_ISOLATED_TYPE }; + const getId = createNamespaceAwareGetId; + await bulkDeleteSuccess(client, repository, registry, [obj1, _obj2], { namespace }); + expectClientCallBulkDeleteArgsAction([obj1, _obj2], { method: 'delete', getId }); + }); + + it(`defaults to a refresh setting of wait_for`, async () => { + await bulkDeleteSuccess(client, repository, registry, [obj1, obj2]); + expect(client.bulk).toHaveBeenCalledWith( + expect.objectContaining({ refresh: 'wait_for' }), + expect.anything() + ); + }); + + it(`does not include the version of the existing document when not using a multi-namespace type`, async () => { + const objects = [obj1, { ...obj2, type: NAMESPACE_AGNOSTIC_TYPE }]; + await bulkDeleteSuccess(client, repository, registry, objects); + expectClientCallBulkDeleteArgsAction(objects, { method: 'delete' }); + }); + + it(`prepends namespace to the id when providing namespace for single-namespace type`, async () => { + const getId = createNamespaceAwareGetId; + await bulkDeleteSuccess(client, repository, registry, [obj1, obj2], { namespace }); + expectClientCallBulkDeleteArgsAction([obj1, obj2], { method: 'delete', getId }); + }); + + it(`doesn't prepend namespace to the id when providing no namespace for single-namespace type`, async () => { + const getId = (type: string, id: string) => `${type}:${id}`; + await bulkDeleteSuccess(client, repository, registry, [obj1, obj2]); + expectClientCallBulkDeleteArgsAction([obj1, obj2], { method: 'delete', getId }); + }); + + it(`normalizes options.namespace from 'default' to undefined`, async () => { + const getId = (type: string, id: string) => `${type}:${id}`; + await bulkDeleteSuccess(client, repository, registry, [obj1, obj2], { + namespace: 'default', + }); + expectClientCallBulkDeleteArgsAction([obj1, obj2], { method: 'delete', getId }); + }); + + it(`doesn't prepend namespace to the id when not using single-namespace type`, async () => { + const getId = (type: string, id: string) => `${type}:${id}`; // not expecting namespace prefix; + const _obj1 = { ...obj1, type: NAMESPACE_AGNOSTIC_TYPE }; + const _obj2 = { ...obj2, type: MULTI_NAMESPACE_ISOLATED_TYPE }; + + await bulkDeleteSuccess(client, repository, registry, [_obj1, _obj2], { namespace }); + expectClientCallBulkDeleteArgsAction([_obj1, _obj2], { method: 'delete', getId }); + }); + }); + + describe('legacy URL aliases', () => { + it(`doesn't delete legacy URL aliases for single-namespace object types`, async () => { + await bulkDeleteSuccess(client, repository, registry, [obj1, obj2]); + expect(mockDeleteLegacyUrlAliases).not.toHaveBeenCalled(); + }); + + it(`deletes legacy URL aliases for multi-namespace object types (all spaces)`, async () => { + const testObject = { ...obj1, type: MULTI_NAMESPACE_TYPE }; + const internalOptions = { + mockMGetResponseObjects: [ + { + ...testObject, + initialNamespaces: [ALL_NAMESPACES_STRING], + }, + ], + }; + await bulkDeleteSuccess( + client, + repository, + registry, + [testObject], + { namespace, force: true }, + internalOptions + ); + expect(mockDeleteLegacyUrlAliases).toHaveBeenCalledWith( + expect.objectContaining({ + type: MULTI_NAMESPACE_TYPE, + id: testObject.id, + namespaces: [], + deleteBehavior: 'exclusive', + }) + ); + }); + + it(`deletes legacy URL aliases for multi-namespace object types (specific space)`, async () => { + const testObject = { ...obj1, type: MULTI_NAMESPACE_TYPE }; + const internalOptions = { + mockMGetResponseObjects: [ + { + ...testObject, + initialNamespaces: [namespace], + }, + ], + }; + // specifically test against the current namespace + await bulkDeleteSuccess( + client, + repository, + registry, + [testObject], + { namespace }, + internalOptions + ); + expect(mockDeleteLegacyUrlAliases).toHaveBeenCalledWith( + expect.objectContaining({ + type: MULTI_NAMESPACE_TYPE, + id: testObject.id, + namespaces: [namespace], + deleteBehavior: 'inclusive', + }) + ); + }); + + it(`deletes legacy URL aliases for multi-namespace object types shared to many specific spaces`, async () => { + const testObject = { ...obj1, type: MULTI_NAMESPACE_TYPE }; + const initialTestObjectNamespaces = [namespace, 'bar-namespace']; + const internalOptions = { + mockMGetResponseObjects: [ + { + ...testObject, + initialNamespaces: initialTestObjectNamespaces, + }, + ], + }; + // specifically test against named spaces ('*' is handled specifically, this assures we also take care of named spaces) + await bulkDeleteSuccess( + client, + repository, + registry, + [testObject], + { namespace, force: true }, + internalOptions + ); + expect(mockDeleteLegacyUrlAliases).toHaveBeenCalledWith( + expect.objectContaining({ + type: MULTI_NAMESPACE_TYPE, + id: testObject.id, + namespaces: initialTestObjectNamespaces, + deleteBehavior: 'inclusive', + }) + ); + }); + + it(`logs a message when deleteLegacyUrlAliases returns an error`, async () => { + const testObject = { type: MULTI_NAMESPACE_ISOLATED_TYPE, id: obj1.id }; + + client.mget.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise( + getMockMgetResponse(registry, [testObject], namespace) + ) + ); + const mockedBulkResponse = getMockEsBulkDeleteResponse(registry, [testObject], { + namespace, + }); + client.bulk.mockResolvedValueOnce(mockedBulkResponse); + + mockDeleteLegacyUrlAliases.mockRejectedValueOnce(new Error('Oh no!')); + + await repository.bulkDelete([testObject], { namespace }); + + expect(client.mget).toHaveBeenCalledTimes(1); + expect(logger.error).toHaveBeenCalledTimes(1); + expect(logger.error).toHaveBeenCalledWith( + 'Unable to delete aliases when deleting an object: Oh no!' + ); + }); + }); + + describe('errors', () => { + it(`throws an error when options.namespace is '*'`, async () => { + await expect( + repository.bulkDelete([obj1], { namespace: ALL_NAMESPACES_STRING }) + ).rejects.toThrowError( + SavedObjectsErrorHelpers.createBadRequestError('"options.namespace" cannot be "*"') + ); + }); + + it(`throws an error when client bulk response is not defined`, async () => { + client.mget.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise( + getMockMgetResponse(registry, [obj1], namespace) + ) + ); + const mockedBulkResponse = undefined; + // we have to cast here to test the assumption we always get a response. + client.bulk.mockResponseOnce(mockedBulkResponse as unknown as estypes.BulkResponse); + await expect(repository.bulkDelete([obj1], { namespace })).rejects.toThrowError( + 'Unexpected error in bulkDelete saved objects: bulkDeleteResponse is undefined' + ); + }); + + it(`returns an error for the object when the object's type is invalid`, async () => { + const unknownObjType = { ...obj1, type: 'unknownType' }; + await repositoryBulkDeleteError( + unknownObjType, + false, + expectErrorInvalidType(unknownObjType) + ); + }); + + it(`returns an error for an object when the object's type is hidden`, async () => { + const hiddenObject = { ...obj1, type: HIDDEN_TYPE }; + await repositoryBulkDeleteError(hiddenObject, false, expectErrorInvalidType(hiddenObject)); + }); + + it(`returns an error when ES is unable to find the document during mget`, async () => { + const notFoundObj = { ...obj1, type: MULTI_NAMESPACE_ISOLATED_TYPE, found: false }; + const mgetResponse = getMockMgetResponse(registry, [notFoundObj], namespace); + await bulkDeleteMultiNamespaceError([obj1, notFoundObj, obj2], { namespace }, mgetResponse); + }); + + it(`returns an error when ES is unable to find the index during mget`, async () => { + const notFoundObj = { ...obj1, type: MULTI_NAMESPACE_ISOLATED_TYPE, found: false }; + await bulkDeleteMultiNamespaceError( + [obj1, notFoundObj, obj2], + { namespace }, + {} as estypes.MgetResponse, + { + statusCode: 404, + } + ); + }); + + it(`returns an error when the type is multi-namespace and the document exists, but not in this namespace`, async () => { + const obj = { + type: MULTI_NAMESPACE_ISOLATED_TYPE, + id: 'three', + namespace: 'bar-namespace', + }; + const mgetResponse = getMockMgetResponse(registry, [obj], namespace); + await bulkDeleteMultiNamespaceError([obj1, obj, obj2], { namespace }, mgetResponse); + }); + + it(`returns an error when the type is multi-namespace and the document has multiple namespaces and the force option is not enabled`, async () => { + const testObject = { ...obj1, type: MULTI_NAMESPACE_TYPE }; + const internalOptions = { + mockMGetResponseObjects: [ + { + ...testObject, + initialNamespaces: [namespace, 'bar-namespace'], + }, + ], + }; + const result = await bulkDeleteSuccess( + client, + repository, + registry, + [testObject], + { namespace }, + internalOptions + ); + expect(result.statuses[0]).toStrictEqual( + createBulkDeleteFailStatus({ + ...testObject, + error: createBadRequestErrorPayload( + 'Unable to delete saved object that exists in multiple namespaces, use the `force` option to delete it anyway' + ), + }) + ); + }); + + it(`returns an error when the type is multi-namespace and the document has all namespaces and the force option is not enabled`, async () => { + const testObject = { ...obj1, type: ALL_NAMESPACES_STRING }; + const internalOptions = { + mockMGetResponseObjects: [ + { + ...testObject, + initialNamespaces: [namespace, 'bar-namespace'], + }, + ], + }; + const result = await bulkDeleteSuccess( + client, + repository, + registry, + [testObject], + { namespace }, + internalOptions + ); + expect(result.statuses[0]).toStrictEqual( + createBulkDeleteFailStatus({ + ...testObject, + error: createBadRequestErrorPayload("Unsupported saved object type: '*'"), + }) + ); + }); + }); + + describe('returns', () => { + it(`returns early for empty objects argument`, async () => { + await repository.bulkDelete([], { namespace }); + expect(client.bulk).toHaveBeenCalledTimes(0); + }); + + it(`formats the ES response`, async () => { + const response = await bulkDeleteSuccess(client, repository, registry, [obj1, obj2], { + namespace, + }); + expect(response).toEqual({ + statuses: [obj1, obj2].map(createBulkDeleteSuccessStatus), + }); + }); + + it(`handles a mix of successful deletes and errors`, async () => { + const notFoundObj = { ...obj1, type: MULTI_NAMESPACE_ISOLATED_TYPE, found: false }; + await bulkDeleteMultiNamespaceError( + [obj1, notFoundObj, obj2], + { namespace }, + {} as estypes.MgetResponse, + { statusCode: 404 } + ); + }); + }); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_get.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_get.test.ts new file mode 100644 index 0000000000000..35e9b554a16c9 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_get.test.ts @@ -0,0 +1,420 @@ +/* + * Copyright 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 { + pointInTimeFinderMock, + mockGetBulkOperationError, + mockGetCurrentTime, + mockGetSearchDsl, +} from '../repository.test.mock'; + +import type { Payload } from '@hapi/boom'; +import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; + +import type { SavedObjectsBulkGetObject } from '@kbn/core-saved-objects-api-server'; +import { type SavedObjectsRawDocSource, type SavedObject } from '@kbn/core-saved-objects-server'; +import { ALL_NAMESPACES_STRING } from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsRepository } from '../repository'; +import { loggerMock } from '@kbn/logging-mocks'; +import { + SavedObjectsSerializer, + encodeHitVersion, +} from '@kbn/core-saved-objects-base-server-internal'; +import { kibanaMigratorMock } from '../../mocks'; +import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; + +import { + NAMESPACE_AGNOSTIC_TYPE, + MULTI_NAMESPACE_ISOLATED_TYPE, + HIDDEN_TYPE, + mockTimestamp, + mappings, + bulkGetSuccess, + createRegistry, + createDocumentMigrator, + getMockMgetResponse, + type TypeIdTuple, + createSpySerializer, + bulkGet, + expectErrorResult, + expectErrorInvalidType, + expectErrorNotFound, + expectError, + createBadRequestErrorPayload, +} from '../../test_helpers/repository.test.common'; + +interface ExpectedErrorResult { + type: string; + id: string; + error: Record; +} + +describe('#bulkGet', () => { + let client: ReturnType; + let repository: SavedObjectsRepository; + let migrator: ReturnType; + let logger: ReturnType; + let serializer: jest.Mocked; + + const registry = createRegistry(); + const documentMigrator = createDocumentMigrator(registry); + + const expectSuccess = ({ type, id }: { type: string; id: string }) => { + // @ts-expect-error TS is not aware of the extension + return expect.toBeDocumentWithoutError(type, id); + }; + + const expectMigrationArgs = (args: unknown, contains = true, n = 1) => { + const obj = contains ? expect.objectContaining(args) : expect.not.objectContaining(args); + expect(migrator.migrateDocument).toHaveBeenNthCalledWith( + n, + obj, + expect.objectContaining({ + allowDowngrade: expect.any(Boolean), + }) + ); + }; + + beforeEach(() => { + pointInTimeFinderMock.mockClear(); + client = elasticsearchClientMock.createElasticsearchClient(); + migrator = kibanaMigratorMock.create(); + documentMigrator.prepareMigrations(); + migrator.migrateDocument = jest.fn().mockImplementation(documentMigrator.migrate); + migrator.runMigrations = jest.fn().mockResolvedValue([{ status: 'skipped' }]); + logger = loggerMock.create(); + + // create a mock serializer "shim" so we can track function calls, but use the real serializer's implementation + serializer = createSpySerializer(registry); + + const allTypes = registry.getAllTypes().map((type) => type.name); + const allowedTypes = [...new Set(allTypes.filter((type) => !registry.isHidden(type)))]; + + // @ts-expect-error must use the private constructor to use the mocked serializer + repository = new SavedObjectsRepository({ + index: '.kibana-test', + mappings, + client, + migrator, + typeRegistry: registry, + serializer, + allowedTypes, + logger, + }); + + mockGetCurrentTime.mockReturnValue(mockTimestamp); + mockGetSearchDsl.mockClear(); + }); + + describe('performBulkGet', () => { + const obj1: SavedObject = { + type: 'config', + id: '6.0.0-alpha1', + attributes: { title: 'Testing' }, + references: [ + { + name: 'ref_0', + type: 'test', + id: '1', + }, + ], + originId: 'some-origin-id', // only one of the results has an originId, this is intentional to test both a positive and negative case + }; + const obj2: SavedObject = { + type: 'index-pattern', + id: 'logstash-*', + attributes: { title: 'Testing' }, + references: [ + { + name: 'ref_0', + type: 'test', + id: '2', + }, + ], + }; + const namespace = 'foo-namespace'; + + const _expectClientCallArgs = ( + objects: TypeIdTuple[], + { + _index = expect.any(String), + getId = () => expect.any(String), + }: { _index?: string; getId?: (type: string, id: string) => string } + ) => { + expect(client.mget).toHaveBeenCalledWith( + expect.objectContaining({ + body: { + docs: objects.map(({ type, id }) => + expect.objectContaining({ + _index, + _id: getId(type, id), + }) + ), + }, + }), + expect.anything() + ); + }; + + describe('client calls', () => { + it(`prepends namespace to the id when providing namespace for single-namespace type`, async () => { + const getId = (type: string, id: string) => `${namespace}:${type}:${id}`; // test that the raw document ID equals this (e.g., has a namespace prefix) + await bulkGetSuccess(client, repository, registry, [obj1, obj2], { namespace }); + _expectClientCallArgs([obj1, obj2], { getId }); + }); + + it(`prepends namespace to the id when providing namespaces for single-namespace type`, async () => { + const getId = (type: string, id: string) => `${namespace}:${type}:${id}`; // test that the raw document ID equals this (e.g., has a namespace prefix) + const objects = [obj1, obj2].map((obj) => ({ ...obj, namespaces: [namespace] })); + await bulkGetSuccess(client, repository, registry, objects, { namespace: 'some-other-ns' }); + _expectClientCallArgs([obj1, obj2], { getId }); + }); + + it(`doesn't prepend namespace to the id when providing no namespace for single-namespace type`, async () => { + const getId = (type: string, id: string) => `${type}:${id}`; // test that the raw document ID equals this (e.g., does not have a namespace prefix) + await bulkGetSuccess(client, repository, registry, [obj1, obj2]); + _expectClientCallArgs([obj1, obj2], { getId }); + }); + + it(`normalizes options.namespace from 'default' to undefined`, async () => { + const getId = (type: string, id: string) => `${type}:${id}`; // test that the raw document ID equals this (e.g., does not have a namespace prefix) + await bulkGetSuccess(client, repository, registry, [obj1, obj2], { namespace: 'default' }); + _expectClientCallArgs([obj1, obj2], { getId }); + }); + + it(`doesn't prepend namespace to the id when not using single-namespace type`, async () => { + const getId = (type: string, id: string) => `${type}:${id}`; // test that the raw document ID equals this (e.g., does not have a namespace prefix) + let objects = [obj1, obj2].map((obj) => ({ ...obj, type: NAMESPACE_AGNOSTIC_TYPE })); + await bulkGetSuccess(client, repository, registry, objects, { namespace }); + _expectClientCallArgs(objects, { getId }); + + client.mget.mockClear(); + objects = [obj1, { ...obj2, namespaces: ['some-other-ns'] }].map((obj) => ({ + ...obj, + type: MULTI_NAMESPACE_ISOLATED_TYPE, + })); + await bulkGetSuccess(client, repository, registry, objects, { namespace }); + _expectClientCallArgs(objects, { getId }); + }); + }); + + describe('errors', () => { + const bulkGetError = async ( + obj: SavedObjectsBulkGetObject & { found?: boolean }, + isBulkError: boolean, + expectedErrorResult: ExpectedErrorResult + ) => { + let response; + if (isBulkError) { + // mock the bulk error for only the second object + mockGetBulkOperationError.mockReturnValueOnce(undefined); + mockGetBulkOperationError.mockReturnValueOnce(expectedErrorResult.error as Payload); + response = getMockMgetResponse(registry, [obj1, obj, obj2]); + } else { + response = getMockMgetResponse(registry, [obj1, obj2]); + } + client.mget.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise(response) + ); + + const objects = [obj1, obj, obj2]; + const result = await bulkGet(repository, objects); + expect(client.mget).toHaveBeenCalled(); + expect(result).toEqual({ + saved_objects: [expectSuccess(obj1), expectedErrorResult, expectSuccess(obj2)], + }); + }; + + it(`throws when options.namespace is '*'`, async () => { + const obj = { type: 'dashboard', id: 'three' }; + await expect( + repository.bulkGet([obj], { namespace: ALL_NAMESPACES_STRING }) + ).rejects.toThrowError(createBadRequestErrorPayload('"options.namespace" cannot be "*"')); + }); + + it(`returns error when namespaces is used with a space-agnostic object`, async () => { + const obj = { type: NAMESPACE_AGNOSTIC_TYPE, id: 'three', namespaces: [] }; + await bulkGetError( + obj, + false, + expectErrorResult( + obj, + createBadRequestErrorPayload('"namespaces" cannot be used on space-agnostic types') + ) + ); + }); + + it(`returns error when namespaces is used with a space-isolated object and does not specify a single space`, async () => { + const doTest = async (objType: string, namespaces?: string[]) => { + const obj = { type: objType, id: 'three', namespaces }; + await bulkGetError( + obj, + false, + expectErrorResult( + obj, + createBadRequestErrorPayload( + '"namespaces" can only specify a single space when used with space-isolated types' + ) + ) + ); + }; + await doTest('dashboard', ['spacex', 'spacey']); + await doTest('dashboard', ['*']); + await doTest(MULTI_NAMESPACE_ISOLATED_TYPE, ['spacex', 'spacey']); + await doTest(MULTI_NAMESPACE_ISOLATED_TYPE, ['*']); + }); + + it(`returns error when type is invalid`, async () => { + const obj: SavedObjectsBulkGetObject = { type: 'unknownType', id: 'three' }; + await bulkGetError(obj, false, expectErrorInvalidType(obj)); + }); + + it(`returns error when type is hidden`, async () => { + const obj: SavedObjectsBulkGetObject = { type: HIDDEN_TYPE, id: 'three' }; + await bulkGetError(obj, false, expectErrorInvalidType(obj)); + }); + + it(`returns error when document is not found`, async () => { + const obj: SavedObjectsBulkGetObject & { found: boolean } = { + type: 'dashboard', + id: 'three', + found: false, + }; + await bulkGetError(obj, true, expectErrorNotFound(obj)); + }); + + it(`handles missing ids gracefully`, async () => { + const obj: SavedObjectsBulkGetObject & { found: boolean } = { + type: 'dashboard', + // @ts-expect-error id is undefined + id: undefined, + found: false, + }; + await bulkGetError(obj, true, expectErrorNotFound(obj)); + }); + + it(`returns error when type is multi-namespace and the document exists, but not in this namespace`, async () => { + const obj = { + type: MULTI_NAMESPACE_ISOLATED_TYPE, + id: 'three', + namespace: 'bar-namespace', + }; + await bulkGetError(obj, true, expectErrorNotFound(obj)); + }); + }); + + describe('returns', () => { + const expectSuccessResult = ( + { type, id }: TypeIdTuple, + doc: estypes.GetGetResult + ) => ({ + type, + id, + namespaces: doc._source!.namespaces ?? ['default'], + ...(doc._source!.originId && { originId: doc._source!.originId }), + ...(doc._source!.updated_at && { updated_at: doc._source!.updated_at }), + ...(doc._source!.created_at && { created_at: doc._source!.created_at }), + version: encodeHitVersion(doc), + attributes: doc._source![type], + references: doc._source!.references || [], + coreMigrationVersion: expect.any(String), + typeMigrationVersion: expect.any(String), + managed: expect.any(Boolean), + }); + + it(`returns early for empty objects argument`, async () => { + const result = await bulkGet(repository, []); + expect(result).toEqual({ saved_objects: [] }); + expect(client.mget).not.toHaveBeenCalled(); + }); + + it(`formats the ES response`, async () => { + const response = getMockMgetResponse(registry, [obj1, obj2]); + client.mget.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise(response) + ); + const result = await bulkGet(repository, [obj1, obj2]); + expect(client.mget).toHaveBeenCalledTimes(1); + expect(result).toEqual({ + saved_objects: [ + expectSuccessResult( + obj1, + response.docs[0] as estypes.GetGetResult + ), + expectSuccessResult( + obj2, + response.docs[1] as estypes.GetGetResult + ), + ], + }); + }); + + it(`handles a mix of successful gets and errors`, async () => { + const response = getMockMgetResponse(registry, [obj1, obj2]); + client.mget.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise(response) + ); + const obj: SavedObject = { + type: 'unknownType', + id: 'three', + attributes: {}, + references: [], + }; + const result = await bulkGet(repository, [obj1, obj, obj2]); + expect(client.mget).toHaveBeenCalledTimes(1); + expect(result).toEqual({ + saved_objects: [ + expectSuccessResult( + obj1, + response.docs[0] as estypes.GetGetResult + ), + expectError(obj), + expectSuccessResult( + obj2, + response.docs[1] as estypes.GetGetResult + ), + ], + }); + }); + + it(`includes namespaces property for single-namespace and multi-namespace documents`, async () => { + const obj: SavedObject = { + type: MULTI_NAMESPACE_ISOLATED_TYPE, + id: 'three', + attributes: {}, + references: [], + }; + const { result } = await bulkGetSuccess(client, repository, registry, [obj1, obj]); + expect(result).toEqual({ + saved_objects: [ + expect.objectContaining({ namespaces: ['default'] }), + expect.objectContaining({ namespaces: expect.any(Array) }), + ], + }); + }); + + it('migrates the fetched documents', async () => { + const response = getMockMgetResponse(registry, [obj1, obj2]); + client.mget.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise(response) + ); + migrator.migrateDocument.mockReturnValue( + 'migrated' as unknown as ReturnType + ); + + await expect(bulkGet(repository, [obj1, obj2])).resolves.toHaveProperty('saved_objects', [ + 'migrated', + 'migrated', + ]); + expect(migrator.migrateDocument).toHaveBeenCalledTimes(2); + expectMigrationArgs({ id: obj1.id }, true, 1); + expectMigrationArgs({ id: obj2.id }, true, 2); + }); + }); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_resolve.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_resolve.test.ts new file mode 100644 index 0000000000000..a71f8e27ccb98 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_resolve.test.ts @@ -0,0 +1,127 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { + pointInTimeFinderMock, + mockInternalBulkResolve, + mockGetCurrentTime, + mockGetSearchDsl, +} from '../repository.test.mock'; + +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server'; +import { SavedObjectsRepository } from '../repository'; +import { loggerMock } from '@kbn/logging-mocks'; +import { SavedObjectsSerializer } from '@kbn/core-saved-objects-base-server-internal'; +import { kibanaMigratorMock } from '../../mocks'; +import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; + +import { + mockTimestamp, + mappings, + createRegistry, + createDocumentMigrator, + createSpySerializer, +} from '../../test_helpers/repository.test.common'; + +describe('#bulkResolve', () => { + let client: ReturnType; + let repository: SavedObjectsRepository; + let migrator: ReturnType; + let logger: ReturnType; + let serializer: jest.Mocked; + + const registry = createRegistry(); + const documentMigrator = createDocumentMigrator(registry); + + beforeEach(() => { + pointInTimeFinderMock.mockClear(); + client = elasticsearchClientMock.createElasticsearchClient(); + migrator = kibanaMigratorMock.create(); + documentMigrator.prepareMigrations(); + migrator.migrateDocument = jest.fn().mockImplementation(documentMigrator.migrate); + migrator.runMigrations = jest.fn().mockResolvedValue([{ status: 'skipped' }]); + logger = loggerMock.create(); + + // create a mock serializer "shim" so we can track function calls, but use the real serializer's implementation + serializer = createSpySerializer(registry); + + const allTypes = registry.getAllTypes().map((type) => type.name); + const allowedTypes = [...new Set(allTypes.filter((type) => !registry.isHidden(type)))]; + + // @ts-expect-error must use the private constructor to use the mocked serializer + repository = new SavedObjectsRepository({ + index: '.kibana-test', + mappings, + client, + migrator, + typeRegistry: registry, + serializer, + allowedTypes, + logger, + }); + + mockGetCurrentTime.mockReturnValue(mockTimestamp); + mockGetSearchDsl.mockClear(); + }); + + describe('performBulkResolve', () => { + afterEach(() => { + mockInternalBulkResolve.mockReset(); + }); + + it('passes arguments to the internalBulkResolve module and returns the expected results', async () => { + mockInternalBulkResolve.mockResolvedValue({ + resolved_objects: [ + { + saved_object: { type: 'mock', id: 'mock-object', attributes: {}, references: [] }, + outcome: 'exactMatch', + }, + { + type: 'obj-type', + id: 'obj-id-2', + error: SavedObjectsErrorHelpers.createGenericNotFoundError('obj-type', 'obj-id-2'), + }, + ], + }); + + const objects = [ + { type: 'obj-type', id: 'obj-id-1' }, + { type: 'obj-type', id: 'obj-id-2' }, + ]; + await expect(repository.bulkResolve(objects)).resolves.toEqual({ + resolved_objects: [ + { + saved_object: { type: 'mock', id: 'mock-object', attributes: {}, references: [] }, + outcome: 'exactMatch', + }, + { + saved_object: { + type: 'obj-type', + id: 'obj-id-2', + error: { + error: 'Not Found', + message: 'Saved object [obj-type/obj-id-2] not found', + statusCode: 404, + }, + }, + outcome: 'exactMatch', + }, + ], + }); + expect(mockInternalBulkResolve).toHaveBeenCalledTimes(1); + expect(mockInternalBulkResolve).toHaveBeenCalledWith(expect.objectContaining({ objects })); + }); + + it('throws when internalBulkResolve throws', async () => { + const error = new Error('Oh no!'); + mockInternalBulkResolve.mockRejectedValue(error); + + await expect(repository.resolve('some-type', 'some-id')).rejects.toEqual(error); + }); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_update.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_update.test.ts index 60deaa64e3e63..983021d99005c 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_update.test.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/bulk_update.test.ts @@ -60,7 +60,7 @@ interface ExpectedErrorResult { error: Record; } -describe('SavedObjectsRepository', () => { +describe('#bulkUpdate', () => { let client: ReturnType; let repository: SavedObjectsRepository; let migrator: ReturnType; @@ -117,7 +117,7 @@ describe('SavedObjectsRepository', () => { mockGetSearchDsl.mockClear(); }); - describe('#bulkUpdate', () => { + describe('performBulkUpdate', () => { const obj1: SavedObjectsBulkUpdateObject = { type: 'config', id: '6.0.0-alpha1', diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/check_conflicts.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/check_conflicts.test.ts new file mode 100644 index 0000000000000..bce3a8be40d23 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/check_conflicts.test.ts @@ -0,0 +1,201 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { + pointInTimeFinderMock, + mockGetCurrentTime, + mockGetSearchDsl, +} from '../repository.test.mock'; + +import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; + +import { ALL_NAMESPACES_STRING } from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsRepository } from '../repository'; +import { loggerMock } from '@kbn/logging-mocks'; +import { SavedObjectsSerializer } from '@kbn/core-saved-objects-base-server-internal'; +import { kibanaMigratorMock } from '../../mocks'; +import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; + +import { + NAMESPACE_AGNOSTIC_TYPE, + MULTI_NAMESPACE_ISOLATED_TYPE, + HIDDEN_TYPE, + mockTimestamp, + mappings, + createRegistry, + createDocumentMigrator, + getMockGetResponse, + type TypeIdTuple, + createSpySerializer, + checkConflicts, + checkConflictsSuccess, + createBadRequestErrorPayload, + createUnsupportedTypeErrorPayload, + createConflictErrorPayload, +} from '../../test_helpers/repository.test.common'; + +describe('#checkConflicts', () => { + let client: ReturnType; + let repository: SavedObjectsRepository; + let migrator: ReturnType; + let logger: ReturnType; + let serializer: jest.Mocked; + + const registry = createRegistry(); + const documentMigrator = createDocumentMigrator(registry); + + beforeEach(() => { + pointInTimeFinderMock.mockClear(); + client = elasticsearchClientMock.createElasticsearchClient(); + migrator = kibanaMigratorMock.create(); + documentMigrator.prepareMigrations(); + migrator.migrateDocument = jest.fn().mockImplementation(documentMigrator.migrate); + migrator.runMigrations = jest.fn().mockResolvedValue([{ status: 'skipped' }]); + logger = loggerMock.create(); + + // create a mock serializer "shim" so we can track function calls, but use the real serializer's implementation + serializer = createSpySerializer(registry); + + const allTypes = registry.getAllTypes().map((type) => type.name); + const allowedTypes = [...new Set(allTypes.filter((type) => !registry.isHidden(type)))]; + + // @ts-expect-error must use the private constructor to use the mocked serializer + repository = new SavedObjectsRepository({ + index: '.kibana-test', + mappings, + client, + migrator, + typeRegistry: registry, + serializer, + allowedTypes, + logger, + }); + + mockGetCurrentTime.mockReturnValue(mockTimestamp); + mockGetSearchDsl.mockClear(); + }); + + describe('performCheckConflicts', () => { + const obj1 = { type: 'dashboard', id: 'one' }; + const obj2 = { type: 'dashboard', id: 'two' }; + const obj3 = { type: MULTI_NAMESPACE_ISOLATED_TYPE, id: 'three' }; + const obj4 = { type: MULTI_NAMESPACE_ISOLATED_TYPE, id: 'four' }; + const obj5 = { type: MULTI_NAMESPACE_ISOLATED_TYPE, id: 'five' }; + const obj6 = { type: NAMESPACE_AGNOSTIC_TYPE, id: 'six' }; + const obj7 = { type: NAMESPACE_AGNOSTIC_TYPE, id: 'seven' }; + const namespace = 'foo-namespace'; + + const _expectClientCallArgs = ( + objects: TypeIdTuple[], + { + _index = expect.any(String), + getId = () => expect.any(String), + }: { _index?: string; getId?: (type: string, id: string) => string } + ) => { + expect(client.mget).toHaveBeenCalledWith( + expect.objectContaining({ + body: { + docs: objects.map(({ type, id }) => + expect.objectContaining({ + _index, + _id: getId(type, id), + }) + ), + }, + }), + expect.anything() + ); + }; + + describe('client calls', () => { + it(`doesn't make a cluster call if the objects array is empty`, async () => { + await checkConflicts(repository, []); + expect(client.mget).not.toHaveBeenCalled(); + }); + + it(`prepends namespace to the id when providing namespace for single-namespace type`, async () => { + const getId = (type: string, id: string) => `${namespace}:${type}:${id}`; // test that the raw document ID equals this (e.g., has a namespace prefix) + await checkConflictsSuccess(client, repository, registry, [obj1, obj2], { namespace }); + _expectClientCallArgs([obj1, obj2], { getId }); + }); + + it(`doesn't prepend namespace to the id when providing no namespace for single-namespace type`, async () => { + const getId = (type: string, id: string) => `${type}:${id}`; // test that the raw document ID equals this (e.g., does not have a namespace prefix) + await checkConflictsSuccess(client, repository, registry, [obj1, obj2]); + _expectClientCallArgs([obj1, obj2], { getId }); + }); + + it(`normalizes options.namespace from 'default' to undefined`, async () => { + const getId = (type: string, id: string) => `${type}:${id}`; // test that the raw document ID equals this (e.g., does not have a namespace prefix) + await checkConflictsSuccess(client, repository, registry, [obj1, obj2], { + namespace: 'default', + }); + _expectClientCallArgs([obj1, obj2], { getId }); + }); + + it(`doesn't prepend namespace to the id when not using single-namespace type`, async () => { + const getId = (type: string, id: string) => `${type}:${id}`; // test that the raw document ID equals this (e.g., does not have a namespace prefix) + // obj3 is multi-namespace, and obj6 is namespace-agnostic + await checkConflictsSuccess(client, repository, registry, [obj3, obj6], { namespace }); + _expectClientCallArgs([obj3, obj6], { getId }); + }); + }); + + describe('errors', () => { + it(`throws when options.namespace is '*'`, async () => { + await expect( + repository.checkConflicts([obj1], { namespace: ALL_NAMESPACES_STRING }) + ).rejects.toThrowError(createBadRequestErrorPayload('"options.namespace" cannot be "*"')); + }); + }); + + describe('returns', () => { + it(`expected results`, async () => { + const unknownTypeObj = { type: 'unknownType', id: 'three' }; + const hiddenTypeObj = { type: HIDDEN_TYPE, id: 'three' }; + const objects = [unknownTypeObj, hiddenTypeObj, obj1, obj2, obj3, obj4, obj5, obj6, obj7]; + const response = { + docs: [ + getMockGetResponse(registry, obj1), + { found: false }, + getMockGetResponse(registry, obj3), + getMockGetResponse(registry, { ...obj4, namespace: 'bar-namespace' }), + { found: false }, + getMockGetResponse(registry, obj6), + { found: false }, + ], + } as estypes.MgetResponse; + client.mget.mockResolvedValue( + elasticsearchClientMock.createSuccessTransportRequestPromise(response) + ); + + const result = await checkConflicts(repository, objects); + expect(client.mget).toHaveBeenCalledTimes(1); + expect(result).toEqual({ + errors: [ + { ...unknownTypeObj, error: createUnsupportedTypeErrorPayload(unknownTypeObj.type) }, + { ...hiddenTypeObj, error: createUnsupportedTypeErrorPayload(hiddenTypeObj.type) }, + { ...obj1, error: createConflictErrorPayload(obj1.type, obj1.id) }, + // obj2 was not found so it does not result in a conflict error + { ...obj3, error: createConflictErrorPayload(obj3.type, obj3.id) }, + { + ...obj4, + error: { + ...createConflictErrorPayload(obj4.type, obj4.id), + metadata: { isNotOverwritable: true }, + }, + }, + // obj5 was not found so it does not result in a conflict error + { ...obj6, error: createConflictErrorPayload(obj6.type, obj6.id) }, + // obj7 was not found so it does not result in a conflict error + ], + }); + }); + }); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/collect_multinamespace_references.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/collect_multinamespace_references.test.ts new file mode 100644 index 0000000000000..0f793d58c7dff --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/collect_multinamespace_references.test.ts @@ -0,0 +1,105 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { + pointInTimeFinderMock, + mockCollectMultiNamespaceReferences, + mockGetCurrentTime, + mockGetSearchDsl, +} from '../repository.test.mock'; + +import type { + SavedObjectsCollectMultiNamespaceReferencesObject, + SavedObjectsCollectMultiNamespaceReferencesResponse, +} from '@kbn/core-saved-objects-api-server'; +import { SavedObjectsRepository } from '../repository'; +import { loggerMock } from '@kbn/logging-mocks'; +import { SavedObjectsSerializer } from '@kbn/core-saved-objects-base-server-internal'; +import { kibanaMigratorMock } from '../../mocks'; +import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; + +import { + mockTimestamp, + mappings, + createRegistry, + createDocumentMigrator, + createSpySerializer, +} from '../../test_helpers/repository.test.common'; + +describe('#collectMultiNamespaceReferences', () => { + let client: ReturnType; + let repository: SavedObjectsRepository; + let migrator: ReturnType; + let logger: ReturnType; + let serializer: jest.Mocked; + + const registry = createRegistry(); + const documentMigrator = createDocumentMigrator(registry); + + beforeEach(() => { + pointInTimeFinderMock.mockClear(); + client = elasticsearchClientMock.createElasticsearchClient(); + migrator = kibanaMigratorMock.create(); + documentMigrator.prepareMigrations(); + migrator.migrateDocument = jest.fn().mockImplementation(documentMigrator.migrate); + migrator.runMigrations = jest.fn().mockResolvedValue([{ status: 'skipped' }]); + logger = loggerMock.create(); + + // create a mock serializer "shim" so we can track function calls, but use the real serializer's implementation + serializer = createSpySerializer(registry); + + const allTypes = registry.getAllTypes().map((type) => type.name); + const allowedTypes = [...new Set(allTypes.filter((type) => !registry.isHidden(type)))]; + + // @ts-expect-error must use the private constructor to use the mocked serializer + repository = new SavedObjectsRepository({ + index: '.kibana-test', + mappings, + client, + migrator, + typeRegistry: registry, + serializer, + allowedTypes, + logger, + }); + + mockGetCurrentTime.mockReturnValue(mockTimestamp); + mockGetSearchDsl.mockClear(); + }); + + describe('performCollectMultiNamespaceReferences', () => { + afterEach(() => { + mockCollectMultiNamespaceReferences.mockReset(); + }); + + it('passes arguments to the collectMultiNamespaceReferences module and returns the result', async () => { + const objects: SavedObjectsCollectMultiNamespaceReferencesObject[] = [ + { type: 'foo', id: 'bar' }, + ]; + const expectedResult: SavedObjectsCollectMultiNamespaceReferencesResponse = { + objects: [{ type: 'foo', id: 'bar', spaces: ['ns-1'], inboundReferences: [] }], + }; + mockCollectMultiNamespaceReferences.mockResolvedValue(expectedResult); + + await expect(repository.collectMultiNamespaceReferences(objects)).resolves.toEqual( + expectedResult + ); + expect(mockCollectMultiNamespaceReferences).toHaveBeenCalledTimes(1); + expect(mockCollectMultiNamespaceReferences).toHaveBeenCalledWith( + expect.objectContaining({ objects }) + ); + }); + + it('returns an error from the collectMultiNamespaceReferences module', async () => { + const expectedResult = new Error('Oh no!'); + mockCollectMultiNamespaceReferences.mockRejectedValue(expectedResult); + + await expect(repository.collectMultiNamespaceReferences([])).rejects.toEqual(expectedResult); + }); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/create.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/create.test.ts new file mode 100644 index 0000000000000..336c2bb2d4899 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/create.test.ts @@ -0,0 +1,831 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* eslint-disable @typescript-eslint/no-shadow */ + +import { + pointInTimeFinderMock, + mockGetCurrentTime, + mockPreflightCheckForCreate, + mockGetSearchDsl, +} from '../repository.test.mock'; + +import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; + +import type { SavedObjectsCreateOptions } from '@kbn/core-saved-objects-api-server'; +import { + type SavedObjectsRawDoc, + type SavedObjectsRawDocSource, + type SavedObjectUnsanitizedDoc, + type SavedObjectReference, +} from '@kbn/core-saved-objects-server'; +import { ALL_NAMESPACES_STRING } from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsRepository } from '../repository'; +import { loggerMock } from '@kbn/logging-mocks'; +import { SavedObjectsSerializer } from '@kbn/core-saved-objects-base-server-internal'; +import { kibanaMigratorMock } from '../../mocks'; +import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; + +import { + CUSTOM_INDEX_TYPE, + NAMESPACE_AGNOSTIC_TYPE, + MULTI_NAMESPACE_TYPE, + MULTI_NAMESPACE_ISOLATED_TYPE, + HIDDEN_TYPE, + mockVersionProps, + mockTimestamp, + mappings, + mockVersion, + createRegistry, + createDocumentMigrator, + createSpySerializer, + createBadRequestErrorPayload, + createUnsupportedTypeErrorPayload, + createConflictErrorPayload, + mockTimestampFieldsWithCreated, +} from '../../test_helpers/repository.test.common'; + +describe('#create', () => { + let client: ReturnType; + let repository: SavedObjectsRepository; + let migrator: ReturnType; + let logger: ReturnType; + let serializer: jest.Mocked; + + const registry = createRegistry(); + const documentMigrator = createDocumentMigrator(registry); + + const expectMigrationArgs = (args: unknown, contains = true, n = 1) => { + const obj = contains ? expect.objectContaining(args) : expect.not.objectContaining(args); + expect(migrator.migrateDocument).toHaveBeenNthCalledWith( + n, + obj, + expect.objectContaining({ + allowDowngrade: expect.any(Boolean), + }) + ); + }; + + beforeEach(() => { + pointInTimeFinderMock.mockClear(); + client = elasticsearchClientMock.createElasticsearchClient(); + migrator = kibanaMigratorMock.create(); + documentMigrator.prepareMigrations(); + migrator.migrateDocument = jest.fn().mockImplementation(documentMigrator.migrate); + migrator.runMigrations = jest.fn().mockResolvedValue([{ status: 'skipped' }]); + logger = loggerMock.create(); + + // create a mock serializer "shim" so we can track function calls, but use the real serializer's implementation + serializer = createSpySerializer(registry); + + const allTypes = registry.getAllTypes().map((type) => type.name); + const allowedTypes = [...new Set(allTypes.filter((type) => !registry.isHidden(type)))]; + + // @ts-expect-error must use the private constructor to use the mocked serializer + repository = new SavedObjectsRepository({ + index: '.kibana-test', + mappings, + client, + migrator, + typeRegistry: registry, + serializer, + allowedTypes, + logger, + }); + + mockGetCurrentTime.mockReturnValue(mockTimestamp); + mockGetSearchDsl.mockClear(); + }); + + // Setup migration mock for creating an object + const mockMigrationVersion = { foo: '2.3.4' }; + const mockMigrateDocument = (doc: SavedObjectUnsanitizedDoc) => ({ + ...doc, + attributes: { + ...doc.attributes, + ...(doc.attributes?.title && { title: `${doc.attributes.title}!!` }), + }, + migrationVersion: mockMigrationVersion, + managed: doc.managed ?? false, + references: [{ name: 'search_0', type: 'search', id: '123' }], + }); + + describe('performCreate', () => { + beforeEach(() => { + mockPreflightCheckForCreate.mockReset(); + mockPreflightCheckForCreate.mockImplementation(({ objects }) => { + return Promise.resolve(objects.map(({ type, id }) => ({ type, id }))); // respond with no errors by default + }); + client.create.mockResponseImplementation((params) => { + return { + body: { + _id: params.id, + ...mockVersionProps, + } as estypes.CreateResponse, + }; + }); + }); + + const type = 'index-pattern'; + const attributes = { title: 'Logstash' }; + const id = 'logstash-*'; + const namespace = 'foo-namespace'; + const references = [ + { + name: 'ref_0', + type: 'test', + id: '123', + }, + ]; + + const createSuccess = async ( + type: string, + attributes: T, + options?: SavedObjectsCreateOptions + ) => { + return await repository.create(type, attributes, options); + }; + + describe('client calls', () => { + it(`should use the ES index action if ID is not defined`, async () => { + await createSuccess(type, attributes, { overwrite: true }); + expect(mockPreflightCheckForCreate).not.toHaveBeenCalled(); + expect(client.index).toHaveBeenCalled(); + }); + + it(`should use the ES index action if ID is not defined and a doc has managed=true`, async () => { + await createSuccess(type, attributes, { overwrite: true, managed: true }); + expect(mockPreflightCheckForCreate).not.toHaveBeenCalled(); + expect(client.index).toHaveBeenCalled(); + }); + + it(`should use the ES index action if ID is not defined and a doc has managed=false`, async () => { + await createSuccess(type, attributes, { overwrite: true, managed: false }); + expect(mockPreflightCheckForCreate).not.toHaveBeenCalled(); + expect(client.index).toHaveBeenCalled(); + }); + + it(`should use the ES create action if ID is not defined and overwrite=false`, async () => { + await createSuccess(type, attributes); + expect(mockPreflightCheckForCreate).not.toHaveBeenCalled(); + expect(client.create).toHaveBeenCalled(); + }); + + it(`should use the ES create action if ID is not defined, overwrite=false and a doc has managed=true`, async () => { + await createSuccess(type, attributes, { managed: true }); + expect(mockPreflightCheckForCreate).not.toHaveBeenCalled(); + expect(client.create).toHaveBeenCalled(); + }); + + it(`should use the ES create action if ID is not defined, overwrite=false and a doc has managed=false`, async () => { + await createSuccess(type, attributes, { managed: false }); + expect(mockPreflightCheckForCreate).not.toHaveBeenCalled(); + expect(client.create).toHaveBeenCalled(); + }); + + it(`should use the ES index with version if ID and version are defined and overwrite=true`, async () => { + await createSuccess(type, attributes, { id, overwrite: true, version: mockVersion }); + expect(mockPreflightCheckForCreate).not.toHaveBeenCalled(); + expect(client.index).toHaveBeenCalled(); + expect(client.index.mock.calls[0][0]).toMatchObject({ + if_seq_no: mockVersionProps._seq_no, + if_primary_term: mockVersionProps._primary_term, + }); + }); + + it(`should use the ES create action if ID is defined and overwrite=false`, async () => { + await createSuccess(type, attributes, { id }); + expect(mockPreflightCheckForCreate).not.toHaveBeenCalled(); + expect(client.create).toHaveBeenCalled(); + }); + + it(`should use the preflightCheckForCreate action then create action if type is multi-namespace, ID is defined, and overwrite=false`, async () => { + await createSuccess(MULTI_NAMESPACE_TYPE, attributes, { id }); + expect(mockPreflightCheckForCreate).toHaveBeenCalled(); + expect(mockPreflightCheckForCreate).toHaveBeenCalledWith( + expect.objectContaining({ + objects: [ + { type: MULTI_NAMESPACE_TYPE, id, overwrite: false, namespaces: ['default'] }, + ], + }) + ); + expect(client.create).toHaveBeenCalled(); + }); + + it(`should use the preflightCheckForCreate action then index action if type is multi-namespace, ID is defined, and overwrite=true`, async () => { + await createSuccess(MULTI_NAMESPACE_ISOLATED_TYPE, attributes, { id, overwrite: true }); + expect(mockPreflightCheckForCreate).toHaveBeenCalled(); + expect(mockPreflightCheckForCreate).toHaveBeenCalledWith( + expect.objectContaining({ + objects: [ + { type: MULTI_NAMESPACE_ISOLATED_TYPE, id, overwrite: true, namespaces: ['default'] }, + ], + }) + ); + expect(client.index).toHaveBeenCalled(); + }); + + it(`defaults to empty references array`, async () => { + await createSuccess(type, attributes, { id }); + expect( + (client.create.mock.calls[0][0] as estypes.CreateRequest).body! + .references + ).toEqual([]); + }); + + it(`accepts custom references array`, async () => { + const test = async (references: SavedObjectReference[]) => { + await createSuccess(type, attributes, { id, references }); + expect( + (client.create.mock.calls[0][0] as estypes.CreateRequest) + .body!.references + ).toEqual(references); + client.create.mockClear(); + }; + await test(references); + await test([{ type: 'type', id: 'id', name: 'some ref' }]); + await test([]); + }); + + it(`doesn't accept custom references if not an array`, async () => { + const test = async (references: unknown) => { + // @ts-expect-error references is unknown + await createSuccess(type, attributes, { id, references }); + expect( + (client.create.mock.calls[0][0] as estypes.CreateRequest) + .body!.references + ).not.toBeDefined(); + client.create.mockClear(); + }; + await test('string'); + await test(123); + await test(true); + await test(null); + }); + + describe('originId', () => { + for (const objType of [type, NAMESPACE_AGNOSTIC_TYPE]) { + it(`throws an error if originId is set for non-multi-namespace type`, async () => { + await expect( + repository.create(objType, attributes, { originId: 'some-originId' }) + ).rejects.toThrowError( + createBadRequestErrorPayload( + '"originId" can only be set for multi-namespace object types' + ) + ); + }); + } + + for (const objType of [MULTI_NAMESPACE_TYPE, MULTI_NAMESPACE_ISOLATED_TYPE]) { + it(`${objType} defaults to no originId`, async () => { + await createSuccess(objType, attributes, { id }); + expect(client.create).toHaveBeenCalledWith( + expect.objectContaining({ + body: expect.not.objectContaining({ originId: expect.anything() }), + }), + expect.anything() + ); + }); + + describe(`${objType} with existing originId`, () => { + beforeEach(() => { + mockPreflightCheckForCreate.mockImplementation(({ objects }) => { + const existingDocument = { + _source: { originId: 'existing-originId' }, + } as SavedObjectsRawDoc; + return Promise.resolve( + objects.map(({ type, id }) => ({ type, id, existingDocument })) + ); + }); + }); + + it(`accepts custom originId for multi-namespace type`, async () => { + // The preflight result has `existing-originId`, but that is discarded + await createSuccess(objType, attributes, { id, originId: 'some-originId' }); + expect(client.create).toHaveBeenCalledWith( + expect.objectContaining({ + body: expect.objectContaining({ originId: 'some-originId' }), + }), + expect.anything() + ); + }); + + it(`accepts undefined originId`, async () => { + // The preflight result has `existing-originId`, but that is discarded + await createSuccess(objType, attributes, { id, originId: undefined }); + expect(client.create).toHaveBeenCalledWith( + expect.objectContaining({ + body: expect.not.objectContaining({ originId: expect.anything() }), + }), + expect.anything() + ); + }); + + it(`preserves existing originId if originId option is not set`, async () => { + await createSuccess(objType, attributes, { id }); + expect(client.create).toHaveBeenCalledWith( + expect.objectContaining({ + body: expect.objectContaining({ originId: 'existing-originId' }), + }), + expect.anything() + ); + }); + }); + } + }); + + it(`defaults to a refresh setting of wait_for`, async () => { + await createSuccess(type, attributes); + expect(client.create).toHaveBeenCalledWith( + expect.objectContaining({ refresh: 'wait_for' }), + expect.anything() + ); + }); + + it(`should use default index`, async () => { + await createSuccess(type, attributes, { id }); + expect(client.create).toHaveBeenCalledWith( + expect.objectContaining({ index: '.kibana-test_8.0.0-testing' }), + expect.anything() + ); + }); + + it(`should use custom index`, async () => { + await createSuccess(CUSTOM_INDEX_TYPE, attributes, { id }); + expect(client.create).toHaveBeenCalledWith( + expect.objectContaining({ index: 'custom_8.0.0-testing' }), + expect.anything() + ); + }); + + it(`self-generates an id if none is provided`, async () => { + await createSuccess(type, attributes); + expect(client.create).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + id: expect.objectContaining(/index-pattern:[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}/), + }), + expect.anything() + ); + await createSuccess(type, attributes, { id: '' }); + expect(client.create).toHaveBeenNthCalledWith( + 2, + expect.objectContaining({ + id: expect.objectContaining(/index-pattern:[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}/), + }), + expect.anything() + ); + }); + + it(`prepends namespace to the id and adds namespace to the body when providing namespace for single-namespace type`, async () => { + await createSuccess(type, attributes, { id, namespace }); + expect(client.create).toHaveBeenCalledWith( + expect.objectContaining({ + id: `${namespace}:${type}:${id}`, + body: expect.objectContaining({ namespace }), + }), + expect.anything() + ); + }); + + it(`doesn't prepend namespace to the id or add namespace to the body when providing no namespace for single-namespace type`, async () => { + await createSuccess(type, attributes, { id }); + expect(client.create).toHaveBeenCalledWith( + expect.objectContaining({ + id: `${type}:${id}`, + body: expect.not.objectContaining({ namespace: expect.anything() }), + }), + expect.anything() + ); + }); + + it(`normalizes options.namespace from 'default' to undefined`, async () => { + await createSuccess(type, attributes, { id, namespace: 'default' }); + expect(client.create).toHaveBeenCalledWith( + expect.objectContaining({ + id: `${type}:${id}`, + body: expect.not.objectContaining({ namespace: expect.anything() }), + }), + expect.anything() + ); + }); + + it(`doesn't prepend namespace to the id and adds namespaces to body when using multi-namespace type`, async () => { + // first object does not have an existing document to overwrite + await createSuccess(MULTI_NAMESPACE_TYPE, attributes, { id, namespace }); + mockPreflightCheckForCreate.mockResolvedValueOnce([ + { + type: MULTI_NAMESPACE_TYPE, + id, + existingDocument: { + _id: id, + _source: { type: MULTI_NAMESPACE_TYPE, namespaces: ['*'] }, + }, // second object does have an existing document to overwrite + }, + ]); + await createSuccess(MULTI_NAMESPACE_ISOLATED_TYPE, attributes, { + id, + namespace, + overwrite: true, + }); + + expect(mockPreflightCheckForCreate).toHaveBeenCalledTimes(2); + expect(mockPreflightCheckForCreate).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + objects: [ + { type: MULTI_NAMESPACE_TYPE, id, overwrite: false, namespaces: [namespace] }, + ], + }) + ); + expect(mockPreflightCheckForCreate).toHaveBeenNthCalledWith( + 2, + expect.objectContaining({ + objects: [ + { type: MULTI_NAMESPACE_ISOLATED_TYPE, id, overwrite: true, namespaces: [namespace] }, + ], + }) + ); + + expect(client.create).toHaveBeenCalledTimes(1); + expect(client.create).toHaveBeenCalledWith( + expect.objectContaining({ + id: `${MULTI_NAMESPACE_TYPE}:${id}`, + body: expect.objectContaining({ namespaces: [namespace] }), + }), + expect.anything() + ); + expect(client.index).toHaveBeenCalledTimes(1); + expect(client.index).toHaveBeenCalledWith( + expect.objectContaining({ + id: `${MULTI_NAMESPACE_ISOLATED_TYPE}:${id}`, + body: expect.objectContaining({ namespaces: ['*'] }), + }), + expect.anything() + ); + }); + + it(`adds initialNamespaces instead of namespace`, async () => { + const ns2 = 'bar-namespace'; + const ns3 = 'baz-namespace'; + // first object does not get passed in to preflightCheckForCreate at all + await repository.create('dashboard', attributes, { + id, + namespace, + initialNamespaces: [ns2], + }); + // second object does not have an existing document to overwrite + await repository.create(MULTI_NAMESPACE_TYPE, attributes, { + id, + namespace, + initialNamespaces: [ns2, ns3], + }); + mockPreflightCheckForCreate.mockResolvedValueOnce([ + { + type: MULTI_NAMESPACE_ISOLATED_TYPE, + id, + existingDocument: { + _id: id, + _source: { type: MULTI_NAMESPACE_ISOLATED_TYPE, namespaces: ['something-else'] }, + }, // third object does have an existing document to overwrite + }, + ]); + await repository.create(MULTI_NAMESPACE_ISOLATED_TYPE, attributes, { + id, + namespace, + initialNamespaces: [ns2], + overwrite: true, + }); + + expect(mockPreflightCheckForCreate).toHaveBeenCalledTimes(2); + expect(mockPreflightCheckForCreate).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + objects: [{ type: MULTI_NAMESPACE_TYPE, id, overwrite: false, namespaces: [ns2, ns3] }], + }) + ); + expect(mockPreflightCheckForCreate).toHaveBeenNthCalledWith( + 2, + expect.objectContaining({ + objects: [ + { type: MULTI_NAMESPACE_ISOLATED_TYPE, id, overwrite: true, namespaces: [ns2] }, + ], + }) + ); + + expect(client.create).toHaveBeenCalledTimes(2); + expect(client.create).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + id: `${ns2}:dashboard:${id}`, + body: expect.objectContaining({ namespace: ns2 }), + }), + expect.anything() + ); + expect(client.create).toHaveBeenNthCalledWith( + 2, + expect.objectContaining({ + id: `${MULTI_NAMESPACE_TYPE}:${id}`, + body: expect.objectContaining({ namespaces: [ns2, ns3] }), + }), + expect.anything() + ); + expect(client.index).toHaveBeenCalledTimes(1); + expect(client.index).toHaveBeenCalledWith( + expect.objectContaining({ + id: `${MULTI_NAMESPACE_ISOLATED_TYPE}:${id}`, + body: expect.objectContaining({ namespaces: [ns2] }), + }), + expect.anything() + ); + }); + + it(`normalizes initialNamespaces from 'default' to undefined`, async () => { + await repository.create('dashboard', attributes, { + id, + namespace, + initialNamespaces: ['default'], + }); + + expect(client.create).toHaveBeenCalledTimes(1); + expect(client.create).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + id: `dashboard:${id}`, + body: expect.not.objectContaining({ namespace: 'default' }), + }), + expect.anything() + ); + }); + + it(`doesn't prepend namespace to the id or add namespace or namespaces fields when using namespace-agnostic type`, async () => { + await createSuccess(NAMESPACE_AGNOSTIC_TYPE, attributes, { id, namespace }); + expect(client.create).toHaveBeenCalledWith( + expect.objectContaining({ + id: `${NAMESPACE_AGNOSTIC_TYPE}:${id}`, + body: expect.not.objectContaining({ + namespace: expect.anything(), + namespaces: expect.anything(), + }), + }), + expect.anything() + ); + }); + }); + + describe('errors', () => { + it(`throws when options.initialNamespaces is used with a space-agnostic object`, async () => { + await expect( + repository.create(NAMESPACE_AGNOSTIC_TYPE, attributes, { + initialNamespaces: [namespace], + }) + ).rejects.toThrowError( + createBadRequestErrorPayload('"initialNamespaces" cannot be used on space-agnostic types') + ); + }); + + it(`throws when options.initialNamespaces is empty`, async () => { + await expect( + repository.create(MULTI_NAMESPACE_TYPE, attributes, { initialNamespaces: [] }) + ).rejects.toThrowError( + createBadRequestErrorPayload('"initialNamespaces" must be a non-empty array of strings') + ); + }); + + it(`throws when options.initialNamespaces is used with a space-isolated object and does not specify a single space`, async () => { + const doTest = async (objType: string, initialNamespaces?: string[]) => { + await expect( + repository.create(objType, attributes, { initialNamespaces }) + ).rejects.toThrowError( + createBadRequestErrorPayload( + '"initialNamespaces" can only specify a single space when used with space-isolated types' + ) + ); + }; + await doTest('dashboard', ['spacex', 'spacey']); + await doTest('dashboard', ['*']); + await doTest(MULTI_NAMESPACE_ISOLATED_TYPE, ['spacex', 'spacey']); + await doTest(MULTI_NAMESPACE_ISOLATED_TYPE, ['*']); + }); + + it(`throws when options.namespace is '*'`, async () => { + await expect( + repository.create(type, attributes, { namespace: ALL_NAMESPACES_STRING }) + ).rejects.toThrowError(createBadRequestErrorPayload('"options.namespace" cannot be "*"')); + }); + + it(`throws when type is invalid`, async () => { + await expect(repository.create('unknownType', attributes)).rejects.toThrowError( + createUnsupportedTypeErrorPayload('unknownType') + ); + expect(client.create).not.toHaveBeenCalled(); + }); + + it(`throws when type is hidden`, async () => { + await expect(repository.create(HIDDEN_TYPE, attributes)).rejects.toThrowError( + createUnsupportedTypeErrorPayload(HIDDEN_TYPE) + ); + expect(client.create).not.toHaveBeenCalled(); + }); + + it(`throws when schema validation fails`, async () => { + await expect( + repository.create('dashboard', { title: 123 }) + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"[attributes.title]: expected value of type [string] but got [number]: Bad Request"` + ); + expect(client.create).not.toHaveBeenCalled(); + }); + + it(`throws when there is a conflict from preflightCheckForCreate`, async () => { + mockPreflightCheckForCreate.mockResolvedValueOnce([ + { type: MULTI_NAMESPACE_ISOLATED_TYPE, id, error: { type: 'unresolvableConflict' } }, // error type and metadata dont matter + ]); + await expect( + repository.create(MULTI_NAMESPACE_ISOLATED_TYPE, attributes, { + id, + overwrite: true, + namespace, + }) + ).rejects.toThrowError(createConflictErrorPayload(MULTI_NAMESPACE_ISOLATED_TYPE, id)); + expect(mockPreflightCheckForCreate).toHaveBeenCalled(); + }); + + it.todo(`throws when automatic index creation fails`); + + it.todo(`throws when an unexpected failure occurs`); + }); + + describe('migration', () => { + beforeEach(() => { + migrator.migrateDocument.mockImplementation(mockMigrateDocument); + }); + + it(`migrates a document and serializes the migrated doc`, async () => { + const migrationVersion = mockMigrationVersion; + const coreMigrationVersion = '8.0.0'; + const managed = false; + await createSuccess(type, attributes, { + id, + references, + migrationVersion, + coreMigrationVersion, + managed, + }); + const doc = { + type, + id, + attributes, + references, + managed, + migrationVersion, + coreMigrationVersion, + ...mockTimestampFieldsWithCreated, + }; + expectMigrationArgs(doc); + + const migratedDoc = migrator.migrateDocument(doc); + expect(serializer.savedObjectToRaw).toHaveBeenLastCalledWith(migratedDoc); + }); + + it(`migrates a document, adds managed=false and serializes the migrated doc`, async () => { + const migrationVersion = mockMigrationVersion; + const coreMigrationVersion = '8.0.0'; + await createSuccess(type, attributes, { + id, + references, + migrationVersion, + coreMigrationVersion, + managed: undefined, + }); + const doc = { + type, + id, + attributes, + references, + managed: undefined, + migrationVersion, + coreMigrationVersion, + ...mockTimestampFieldsWithCreated, + }; + expectMigrationArgs({ ...doc, managed: false }); + + const migratedDoc = migrator.migrateDocument(doc); + expect(migratedDoc.managed).toBe(false); + expect(serializer.savedObjectToRaw).toHaveBeenLastCalledWith(migratedDoc); + }); + + it(`migrates a document, does not change managed=true to managed=false and serializes the migrated doc`, async () => { + const migrationVersion = mockMigrationVersion; + const coreMigrationVersion = '8.0.0'; + await createSuccess(type, attributes, { + id, + references, + migrationVersion, + coreMigrationVersion, + managed: true, + }); + const doc = { + type, + id, + attributes, + references, + managed: true, + migrationVersion, + coreMigrationVersion, + ...mockTimestampFieldsWithCreated, + }; + expectMigrationArgs(doc); + + const migratedDoc = migrator.migrateDocument(doc); + expect(migratedDoc.managed).toBe(true); + expect(serializer.savedObjectToRaw).toHaveBeenLastCalledWith(migratedDoc); + }); + + it(`adds namespace to body when providing namespace for single-namespace type`, async () => { + await createSuccess(type, attributes, { id, namespace }); + expectMigrationArgs({ namespace }); + }); + + it(`doesn't add namespace to body when providing no namespace for single-namespace type`, async () => { + await createSuccess(type, attributes, { id }); + expectMigrationArgs({ namespace: expect.anything() }, false); + }); + + it(`doesn't add namespace to body when not using single-namespace type`, async () => { + await createSuccess(NAMESPACE_AGNOSTIC_TYPE, attributes, { id, namespace }); + expectMigrationArgs({ namespace: expect.anything() }, false, 1); + + client.create.mockClear(); + await createSuccess(MULTI_NAMESPACE_ISOLATED_TYPE, attributes, { id }); + expectMigrationArgs({ namespace: expect.anything() }, false, 2); + }); + + it(`adds namespaces to body when providing namespace for multi-namespace type`, async () => { + await createSuccess(MULTI_NAMESPACE_ISOLATED_TYPE, attributes, { id, namespace }); + expectMigrationArgs({ namespaces: [namespace] }); + }); + + it(`adds default namespaces to body when providing no namespace for multi-namespace type`, async () => { + await createSuccess(MULTI_NAMESPACE_ISOLATED_TYPE, attributes, { id }); + expectMigrationArgs({ namespaces: ['default'] }); + }); + + it(`doesn't add namespaces to body when not using multi-namespace type`, async () => { + await createSuccess(type, attributes, { id }); + expectMigrationArgs({ namespaces: expect.anything() }, false, 1); + + client.create.mockClear(); + await createSuccess(NAMESPACE_AGNOSTIC_TYPE, attributes, { id }); + expectMigrationArgs({ namespaces: expect.anything() }, false, 2); + }); + }); + + describe('returns', () => { + it(`formats the ES response`, async () => { + const result = await createSuccess(MULTI_NAMESPACE_TYPE, attributes, { + id, + namespace, + references, + }); + expect(result).toEqual({ + type: MULTI_NAMESPACE_TYPE, + id, + ...mockTimestampFieldsWithCreated, + version: mockVersion, + attributes, + references, + namespaces: [namespace ?? 'default'], + coreMigrationVersion: expect.any(String), + typeMigrationVersion: '1.1.1', + managed: false, + }); + }); + it(`allows setting 'managed' to true`, async () => { + const result = await createSuccess(MULTI_NAMESPACE_TYPE, attributes, { + id, + namespace, + references, + managed: true, + }); + expect(result).toEqual({ + type: MULTI_NAMESPACE_TYPE, + id, + ...mockTimestampFieldsWithCreated, + version: mockVersion, + attributes, + references, + namespaces: [namespace ?? 'default'], + coreMigrationVersion: expect.any(String), + typeMigrationVersion: '1.1.1', + managed: true, + }); + }); + }); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/delete.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/delete.test.ts new file mode 100644 index 0000000000000..d852bf0906b3c --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/delete.test.ts @@ -0,0 +1,382 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* eslint-disable @typescript-eslint/no-shadow */ + +import { + pointInTimeFinderMock, + mockGetCurrentTime, + mockDeleteLegacyUrlAliases, + mockGetSearchDsl, +} from '../repository.test.mock'; + +import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; + +import type { SavedObjectsDeleteOptions } from '@kbn/core-saved-objects-api-server'; +import { ALL_NAMESPACES_STRING } from '@kbn/core-saved-objects-utils-server'; + +import { SavedObjectsRepository } from '../repository'; +import { loggerMock } from '@kbn/logging-mocks'; +import { SavedObjectsSerializer } from '@kbn/core-saved-objects-base-server-internal'; +import { kibanaMigratorMock } from '../../mocks'; +import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; + +import { + NAMESPACE_AGNOSTIC_TYPE, + MULTI_NAMESPACE_TYPE, + MULTI_NAMESPACE_ISOLATED_TYPE, + HIDDEN_TYPE, + mockVersionProps, + mockTimestamp, + mappings, + createRegistry, + createDocumentMigrator, + getMockGetResponse, + createSpySerializer, + deleteSuccess, + createBadRequestErrorPayload, + createGenericNotFoundErrorPayload, +} from '../../test_helpers/repository.test.common'; + +describe('#delete', () => { + let client: ReturnType; + let repository: SavedObjectsRepository; + let migrator: ReturnType; + let logger: ReturnType; + let serializer: jest.Mocked; + + const registry = createRegistry(); + const documentMigrator = createDocumentMigrator(registry); + + beforeEach(() => { + pointInTimeFinderMock.mockClear(); + client = elasticsearchClientMock.createElasticsearchClient(); + migrator = kibanaMigratorMock.create(); + documentMigrator.prepareMigrations(); + migrator.migrateDocument = jest.fn().mockImplementation(documentMigrator.migrate); + migrator.runMigrations = jest.fn().mockResolvedValue([{ status: 'skipped' }]); + logger = loggerMock.create(); + + // create a mock serializer "shim" so we can track function calls, but use the real serializer's implementation + serializer = createSpySerializer(registry); + + const allTypes = registry.getAllTypes().map((type) => type.name); + const allowedTypes = [...new Set(allTypes.filter((type) => !registry.isHidden(type)))]; + + // @ts-expect-error must use the private constructor to use the mocked serializer + repository = new SavedObjectsRepository({ + index: '.kibana-test', + mappings, + client, + migrator, + typeRegistry: registry, + serializer, + allowedTypes, + logger, + }); + + mockGetCurrentTime.mockReturnValue(mockTimestamp); + mockGetSearchDsl.mockClear(); + }); + + describe('performDelete', () => { + const type = 'index-pattern'; + const id = 'logstash-*'; + const namespace = 'foo-namespace'; + + beforeEach(() => { + mockDeleteLegacyUrlAliases.mockClear(); + mockDeleteLegacyUrlAliases.mockResolvedValue(); + }); + + describe('client calls', () => { + it(`should use the ES delete action when not using a multi-namespace type`, async () => { + await deleteSuccess(client, repository, registry, type, id); + expect(client.get).not.toHaveBeenCalled(); + expect(client.delete).toHaveBeenCalledTimes(1); + }); + + it(`should use ES get action then delete action when using a multi-namespace type`, async () => { + await deleteSuccess(client, repository, registry, MULTI_NAMESPACE_ISOLATED_TYPE, id); + expect(client.get).toHaveBeenCalledTimes(1); + expect(client.delete).toHaveBeenCalledTimes(1); + }); + + it(`does not includes the version of the existing document when using a multi-namespace type`, async () => { + await deleteSuccess(client, repository, registry, MULTI_NAMESPACE_ISOLATED_TYPE, id); + const versionProperties = { + if_seq_no: mockVersionProps._seq_no, + if_primary_term: mockVersionProps._primary_term, + }; + expect(client.delete).toHaveBeenCalledWith( + expect.not.objectContaining(versionProperties), + expect.anything() + ); + }); + + it(`defaults to a refresh setting of wait_for`, async () => { + await deleteSuccess(client, repository, registry, type, id); + expect(client.delete).toHaveBeenCalledWith( + expect.objectContaining({ refresh: 'wait_for' }), + expect.anything() + ); + }); + + it(`prepends namespace to the id when providing namespace for single-namespace type`, async () => { + await deleteSuccess(client, repository, registry, type, id, { namespace }); + expect(client.delete).toHaveBeenCalledWith( + expect.objectContaining({ id: `${namespace}:${type}:${id}` }), + expect.anything() + ); + }); + + it(`doesn't prepend namespace to the id when providing no namespace for single-namespace type`, async () => { + await deleteSuccess(client, repository, registry, type, id); + expect(client.delete).toHaveBeenCalledWith( + expect.objectContaining({ id: `${type}:${id}` }), + expect.anything() + ); + }); + + it(`normalizes options.namespace from 'default' to undefined`, async () => { + await deleteSuccess(client, repository, registry, type, id, { namespace: 'default' }); + expect(client.delete).toHaveBeenCalledWith( + expect.objectContaining({ id: `${type}:${id}` }), + expect.anything() + ); + }); + + it(`doesn't prepend namespace to the id when not using single-namespace type`, async () => { + await deleteSuccess(client, repository, registry, NAMESPACE_AGNOSTIC_TYPE, id, { + namespace, + }); + expect(client.delete).toHaveBeenCalledWith( + expect.objectContaining({ id: `${NAMESPACE_AGNOSTIC_TYPE}:${id}` }), + expect.anything() + ); + + client.delete.mockClear(); + await deleteSuccess(client, repository, registry, MULTI_NAMESPACE_ISOLATED_TYPE, id, { + namespace, + }); + expect(client.delete).toHaveBeenCalledWith( + expect.objectContaining({ id: `${MULTI_NAMESPACE_ISOLATED_TYPE}:${id}` }), + expect.anything() + ); + }); + }); + + describe('legacy URL aliases', () => { + it(`doesn't delete legacy URL aliases for single-namespace object types`, async () => { + await deleteSuccess(client, repository, registry, type, id, { namespace }); + expect(mockDeleteLegacyUrlAliases).not.toHaveBeenCalled(); + }); + + // We intentionally do not include a test case for a multi-namespace object with a "not found" preflight result, because that throws + // an error (without deleting aliases) and we already have a test case for that + + it(`deletes legacy URL aliases for multi-namespace object types (all spaces)`, async () => { + const internalOptions = { + mockGetResponseValue: getMockGetResponse( + registry, + { type: MULTI_NAMESPACE_TYPE, id }, + ALL_NAMESPACES_STRING + ), + }; + await deleteSuccess( + client, + repository, + registry, + MULTI_NAMESPACE_TYPE, + id, + { namespace, force: true }, + internalOptions + ); + expect(mockDeleteLegacyUrlAliases).toHaveBeenCalledWith( + expect.objectContaining({ + type: MULTI_NAMESPACE_TYPE, + id, + namespaces: [], + deleteBehavior: 'exclusive', + }) + ); + }); + + it(`deletes legacy URL aliases for multi-namespace object types (specific spaces)`, async () => { + await deleteSuccess(client, repository, registry, MULTI_NAMESPACE_TYPE, id, { namespace }); // this function mocks a preflight response with the given namespace by default + expect(mockDeleteLegacyUrlAliases).toHaveBeenCalledWith( + expect.objectContaining({ + type: MULTI_NAMESPACE_TYPE, + id, + namespaces: [namespace], + deleteBehavior: 'inclusive', + }) + ); + }); + + it(`logs a message when deleteLegacyUrlAliases returns an error`, async () => { + client.get.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise( + getMockGetResponse(registry, { type: MULTI_NAMESPACE_ISOLATED_TYPE, id, namespace }) + ) + ); + client.delete.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise({ + result: 'deleted', + } as estypes.DeleteResponse) + ); + mockDeleteLegacyUrlAliases.mockRejectedValueOnce(new Error('Oh no!')); + await repository.delete(MULTI_NAMESPACE_ISOLATED_TYPE, id, { namespace }); + expect(client.get).toHaveBeenCalledTimes(1); + expect(logger.error).toHaveBeenCalledTimes(1); + expect(logger.error).toHaveBeenCalledWith( + 'Unable to delete aliases when deleting an object: Oh no!' + ); + }); + }); + + describe('errors', () => { + const expectNotFoundError = async ( + type: string, + id: string, + options?: SavedObjectsDeleteOptions + ) => { + await expect(repository.delete(type, id, options)).rejects.toThrowError( + createGenericNotFoundErrorPayload(type, id) + ); + }; + + it(`throws when options.namespace is '*'`, async () => { + await expect( + repository.delete(type, id, { namespace: ALL_NAMESPACES_STRING }) + ).rejects.toThrowError(createBadRequestErrorPayload('"options.namespace" cannot be "*"')); + }); + + it(`throws when type is invalid`, async () => { + await expectNotFoundError('unknownType', id); + expect(client.delete).not.toHaveBeenCalled(); + }); + + it(`throws when type is hidden`, async () => { + await expectNotFoundError(HIDDEN_TYPE, id); + expect(client.delete).not.toHaveBeenCalled(); + }); + + it(`throws when ES is unable to find the document during get`, async () => { + client.get.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise({ + found: false, + } as estypes.GetResponse) + ); + await expectNotFoundError(MULTI_NAMESPACE_ISOLATED_TYPE, id); + expect(client.get).toHaveBeenCalledTimes(1); + }); + + it(`throws when ES is unable to find the index during get`, async () => { + client.get.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise({} as estypes.GetResponse, { + statusCode: 404, + }) + ); + await expectNotFoundError(MULTI_NAMESPACE_ISOLATED_TYPE, id); + expect(client.get).toHaveBeenCalledTimes(1); + }); + + it(`throws when the type is multi-namespace and the document exists, but not in this namespace`, async () => { + const response = getMockGetResponse( + registry, + { type: MULTI_NAMESPACE_ISOLATED_TYPE, id }, + namespace + ); + client.get.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise(response) + ); + await expectNotFoundError(MULTI_NAMESPACE_ISOLATED_TYPE, id, { + namespace: 'bar-namespace', + }); + expect(client.get).toHaveBeenCalledTimes(1); + }); + + it(`throws when the type is multi-namespace and the document has multiple namespaces and the force option is not enabled`, async () => { + const response = getMockGetResponse(registry, { + type: MULTI_NAMESPACE_ISOLATED_TYPE, + id, + namespace, + }); + response._source!.namespaces = [namespace, 'bar-namespace']; + client.get.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise(response) + ); + await expect( + repository.delete(MULTI_NAMESPACE_ISOLATED_TYPE, id, { namespace }) + ).rejects.toThrowError( + 'Unable to delete saved object that exists in multiple namespaces, use the `force` option to delete it anyway' + ); + expect(client.get).toHaveBeenCalledTimes(1); + }); + + it(`throws when the type is multi-namespace and the document has all namespaces and the force option is not enabled`, async () => { + const response = getMockGetResponse(registry, { + type: MULTI_NAMESPACE_ISOLATED_TYPE, + id, + namespace, + }); + response._source!.namespaces = ['*']; + client.get.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise(response) + ); + await expect( + repository.delete(MULTI_NAMESPACE_ISOLATED_TYPE, id, { namespace }) + ).rejects.toThrowError( + 'Unable to delete saved object that exists in multiple namespaces, use the `force` option to delete it anyway' + ); + expect(client.get).toHaveBeenCalledTimes(1); + }); + + it(`throws when ES is unable to find the document during delete`, async () => { + client.delete.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise({ + result: 'not_found', + } as estypes.DeleteResponse) + ); + await expectNotFoundError(type, id); + expect(client.delete).toHaveBeenCalledTimes(1); + }); + + it(`throws when ES is unable to find the index during delete`, async () => { + client.delete.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise({ + // @elastic/elasticsearch doesn't declare error on DeleteResponse + error: { type: 'index_not_found_exception' }, + } as unknown as estypes.DeleteResponse) + ); + await expectNotFoundError(type, id); + expect(client.delete).toHaveBeenCalledTimes(1); + }); + + it(`throws when ES returns an unexpected response`, async () => { + client.delete.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise({ + result: 'something unexpected' as estypes.Result, + } as estypes.DeleteResponse) + ); + await expect(repository.delete(type, id)).rejects.toThrowError( + 'Unexpected Elasticsearch DELETE response' + ); + expect(client.delete).toHaveBeenCalledTimes(1); + }); + }); + + describe('returns', () => { + it(`returns an empty object on success`, async () => { + const result = await deleteSuccess(client, repository, registry, type, id); + expect(result).toEqual({}); + }); + }); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/delete_by_namespace.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/delete_by_namespace.test.ts new file mode 100644 index 0000000000000..f0208d4cc056b --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/delete_by_namespace.test.ts @@ -0,0 +1,162 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* eslint-disable @typescript-eslint/no-shadow */ + +import { + pointInTimeFinderMock, + mockGetCurrentTime, + mockGetSearchDsl, +} from '../repository.test.mock'; + +import type { SavedObjectsDeleteByNamespaceOptions } from '@kbn/core-saved-objects-api-server'; +import { ALL_NAMESPACES_STRING } from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsRepository } from '../repository'; +import { loggerMock } from '@kbn/logging-mocks'; +import { + SavedObjectsSerializer, + LEGACY_URL_ALIAS_TYPE, +} from '@kbn/core-saved-objects-base-server-internal'; +import { kibanaMigratorMock } from '../../mocks'; +import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; + +import { + mockTimestamp, + mappings, + createRegistry, + createDocumentMigrator, + createSpySerializer, +} from '../../test_helpers/repository.test.common'; + +describe('#deleteByNamespace', () => { + let client: ReturnType; + let repository: SavedObjectsRepository; + let migrator: ReturnType; + let logger: ReturnType; + let serializer: jest.Mocked; + + const registry = createRegistry(); + const documentMigrator = createDocumentMigrator(registry); + + beforeEach(() => { + pointInTimeFinderMock.mockClear(); + client = elasticsearchClientMock.createElasticsearchClient(); + migrator = kibanaMigratorMock.create(); + documentMigrator.prepareMigrations(); + migrator.migrateDocument = jest.fn().mockImplementation(documentMigrator.migrate); + migrator.runMigrations = jest.fn().mockResolvedValue([{ status: 'skipped' }]); + logger = loggerMock.create(); + + // create a mock serializer "shim" so we can track function calls, but use the real serializer's implementation + serializer = createSpySerializer(registry); + + const allTypes = registry.getAllTypes().map((type) => type.name); + const allowedTypes = [...new Set(allTypes.filter((type) => !registry.isHidden(type)))]; + + // @ts-expect-error must use the private constructor to use the mocked serializer + repository = new SavedObjectsRepository({ + index: '.kibana-test', + mappings, + client, + migrator, + typeRegistry: registry, + serializer, + allowedTypes, + logger, + }); + + mockGetCurrentTime.mockReturnValue(mockTimestamp); + mockGetSearchDsl.mockClear(); + }); + + describe('performDeleteByNamespace', () => { + const namespace = 'foo-namespace'; + const mockUpdateResults = { + took: 15, + timed_out: false, + total: 3, + updated: 2, + deleted: 1, + batches: 1, + version_conflicts: 0, + noops: 0, + retries: { bulk: 0, search: 0 }, + throttled_millis: 0, + requests_per_second: -1.0, + throttled_until_millis: 0, + failures: [], + }; + + const deleteByNamespaceSuccess = async ( + namespace: string, + options?: SavedObjectsDeleteByNamespaceOptions + ) => { + client.updateByQuery.mockResponseOnce(mockUpdateResults); + const result = await repository.deleteByNamespace(namespace, options); + expect(mockGetSearchDsl).toHaveBeenCalledTimes(1); + expect(client.updateByQuery).toHaveBeenCalledTimes(1); + return result; + }; + + describe('client calls', () => { + it(`should use the ES updateByQuery action`, async () => { + await deleteByNamespaceSuccess(namespace); + expect(client.updateByQuery).toHaveBeenCalledTimes(1); + }); + + it(`should use all indices for types that are not namespace-agnostic`, async () => { + await deleteByNamespaceSuccess(namespace); + expect(client.updateByQuery).toHaveBeenCalledWith( + expect.objectContaining({ + index: ['.kibana-test_8.0.0-testing', 'custom_8.0.0-testing'], + }), + expect.anything() + ); + }); + }); + + describe('errors', () => { + it(`throws when namespace is not a string or is '*'`, async () => { + const test = async (namespace: unknown) => { + // @ts-expect-error namespace is unknown + await expect(repository.deleteByNamespace(namespace)).rejects.toThrowError( + `namespace is required, and must be a string` + ); + expect(client.updateByQuery).not.toHaveBeenCalled(); + }; + await test(undefined); + await test(['namespace']); + await test(123); + await test(true); + await test(ALL_NAMESPACES_STRING); + }); + }); + + describe('returns', () => { + it(`returns the query results on success`, async () => { + const result = await deleteByNamespaceSuccess(namespace); + expect(result).toEqual(mockUpdateResults); + }); + }); + + describe('search dsl', () => { + it(`constructs a query using all multi-namespace types, and another using all single-namespace types`, async () => { + await deleteByNamespaceSuccess(namespace); + const allTypes = registry.getAllTypes().map((type) => type.name); + expect(mockGetSearchDsl).toHaveBeenCalledWith(mappings, registry, { + namespaces: [namespace], + type: [ + ...allTypes.filter((type) => !registry.isNamespaceAgnostic(type)), + LEGACY_URL_ALIAS_TYPE, + ], + kueryNode: expect.anything(), + }); + }); + }); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/find.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/find.test.ts new file mode 100644 index 0000000000000..6a16b71b1d9af --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/find.test.ts @@ -0,0 +1,521 @@ +/* + * Copyright 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 { + pointInTimeFinderMock, + mockGetCurrentTime, + mockGetSearchDsl, +} from '../repository.test.mock'; + +import type { SavedObjectsFindOptions } from '@kbn/core-saved-objects-api-server'; + +import { SavedObjectsRepository } from '../repository'; +import { loggerMock } from '@kbn/logging-mocks'; +import { SavedObjectsSerializer } from '@kbn/core-saved-objects-base-server-internal'; +import { kibanaMigratorMock } from '../../mocks'; +import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; +import * as esKuery from '@kbn/es-query'; + +import { + NAMESPACE_AGNOSTIC_TYPE, + HIDDEN_TYPE, + mockTimestampFields, + mockTimestamp, + mappings, + mockVersion, + createRegistry, + createDocumentMigrator, + createSpySerializer, + generateIndexPatternSearchResults, + findSuccess, +} from '../../test_helpers/repository.test.common'; + +const { nodeTypes } = esKuery; + +describe('find', () => { + let client: ReturnType; + let repository: SavedObjectsRepository; + let migrator: ReturnType; + let logger: ReturnType; + let serializer: jest.Mocked; + + const registry = createRegistry(); + const documentMigrator = createDocumentMigrator(registry); + + const expectMigrationArgs = (args: unknown, contains = true, n = 1) => { + const obj = contains ? expect.objectContaining(args) : expect.not.objectContaining(args); + expect(migrator.migrateDocument).toHaveBeenNthCalledWith( + n, + obj, + expect.objectContaining({ + allowDowngrade: expect.any(Boolean), + }) + ); + }; + + beforeEach(() => { + pointInTimeFinderMock.mockClear(); + client = elasticsearchClientMock.createElasticsearchClient(); + migrator = kibanaMigratorMock.create(); + documentMigrator.prepareMigrations(); + migrator.migrateDocument = jest.fn().mockImplementation(documentMigrator.migrate); + migrator.runMigrations = jest.fn().mockResolvedValue([{ status: 'skipped' }]); + logger = loggerMock.create(); + + // create a mock serializer "shim" so we can track function calls, but use the real serializer's implementation + serializer = createSpySerializer(registry); + + const allTypes = registry.getAllTypes().map((type) => type.name); + const allowedTypes = [...new Set(allTypes.filter((type) => !registry.isHidden(type)))]; + + // @ts-expect-error must use the private constructor to use the mocked serializer + repository = new SavedObjectsRepository({ + index: '.kibana-test', + mappings, + client, + migrator, + typeRegistry: registry, + serializer, + allowedTypes, + logger, + }); + + mockGetCurrentTime.mockReturnValue(mockTimestamp); + mockGetSearchDsl.mockClear(); + }); + + describe('performFind', () => { + const type = 'index-pattern'; + const namespace = 'foo-namespace'; + + describe('client calls', () => { + it(`should use the ES search action`, async () => { + await findSuccess(client, repository, { type }); + expect(client.search).toHaveBeenCalledTimes(1); + }); + + it(`merges output of getSearchDsl into es request body`, async () => { + const query = { query: 1, aggregations: 2 }; + mockGetSearchDsl.mockReturnValue(query); + await findSuccess(client, repository, { type }); + + expect(client.search).toHaveBeenCalledWith( + expect.objectContaining({ + body: expect.objectContaining({ ...query }), + }), + expect.anything() + ); + }); + + it(`accepts per_page/page`, async () => { + await findSuccess(client, repository, { type, perPage: 10, page: 6 }); + expect(client.search).toHaveBeenCalledWith( + expect.objectContaining({ + size: 10, + from: 50, + }), + expect.anything() + ); + }); + + it(`accepts preference`, async () => { + await findSuccess(client, repository, { type, preference: 'pref' }); + expect(client.search).toHaveBeenCalledWith( + expect.objectContaining({ + preference: 'pref', + }), + expect.anything() + ); + }); + + it(`can filter by fields`, async () => { + await findSuccess(client, repository, { type, fields: ['title'] }); + expect(client.search).toHaveBeenCalledWith( + expect.objectContaining({ + body: expect.objectContaining({ + _source: [ + `${type}.title`, + 'namespace', + 'namespaces', + 'type', + 'references', + 'migrationVersion', + 'coreMigrationVersion', + 'typeMigrationVersion', + 'managed', + 'updated_at', + 'created_at', + 'originId', + ], + }), + }), + expect.anything() + ); + }); + + it(`should set rest_total_hits_as_int to true on a request`, async () => { + await findSuccess(client, repository, { type }); + expect(client.search).toHaveBeenCalledWith( + expect.objectContaining({ + rest_total_hits_as_int: true, + }), + expect.anything() + ); + }); + + it(`should not make a client call when attempting to find only invalid or hidden types`, async () => { + const test = async (types: string | string[]) => { + await repository.find({ type: types }); + expect(client.search).not.toHaveBeenCalled(); + }; + + await test('unknownType'); + await test(HIDDEN_TYPE); + await test(['unknownType', HIDDEN_TYPE]); + }); + }); + + describe('errors', () => { + it(`throws when type is not defined`, async () => { + // @ts-expect-error type should be defined + await expect(repository.find({})).rejects.toThrowError( + 'options.type must be a string or an array of strings' + ); + expect(client.search).not.toHaveBeenCalled(); + }); + + it(`throws when namespaces is an empty array`, async () => { + await expect(repository.find({ type: 'foo', namespaces: [] })).rejects.toThrowError( + 'options.namespaces cannot be an empty array' + ); + expect(client.search).not.toHaveBeenCalled(); + }); + + it(`throws when searchFields is defined but not an array`, async () => { + await expect( + // @ts-expect-error searchFields is an array + repository.find({ type, searchFields: 'string' }) + ).rejects.toThrowError('options.searchFields must be an array'); + expect(client.search).not.toHaveBeenCalled(); + }); + + it(`throws when fields is defined but not an array`, async () => { + // @ts-expect-error fields is an array + await expect(repository.find({ type, fields: 'string' })).rejects.toThrowError( + 'options.fields must be an array' + ); + expect(client.search).not.toHaveBeenCalled(); + }); + + it(`throws when a preference is provided with pit`, async () => { + await expect( + repository.find({ type: 'foo', pit: { id: 'abc123' }, preference: 'hi' }) + ).rejects.toThrowError('options.preference must be excluded when options.pit is used'); + expect(client.search).not.toHaveBeenCalled(); + }); + + it(`throws when KQL filter syntax is invalid`, async () => { + const findOpts: SavedObjectsFindOptions = { + namespaces: [namespace], + search: 'foo*', + searchFields: ['foo'], + type: ['dashboard'], + sortField: 'name', + sortOrder: 'desc', + defaultSearchOperator: 'AND', + hasReference: { + type: 'foo', + id: '1', + }, + filter: 'dashboard.attributes.otherField:<', + }; + + await expect(repository.find(findOpts)).rejects.toMatchInlineSnapshot(` + [Error: KQLSyntaxError: Expected "(", "{", value, whitespace but "<" found. + dashboard.attributes.otherField:< + --------------------------------^: Bad Request] + `); + expect(mockGetSearchDsl).not.toHaveBeenCalled(); + expect(client.search).not.toHaveBeenCalled(); + }); + }); + + describe('returns', () => { + it(`formats the ES response when there is no namespace`, async () => { + const noNamespaceSearchResults = generateIndexPatternSearchResults(); + client.search.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise(noNamespaceSearchResults) + ); + const count = noNamespaceSearchResults.hits.hits.length; + + const response = await repository.find({ type }); + + expect(response.total).toBe(count); + expect(response.saved_objects).toHaveLength(count); + + noNamespaceSearchResults.hits.hits.forEach((doc, i) => { + expect(response.saved_objects[i]).toEqual({ + id: doc._id.replace(/(index-pattern|config|globalType)\:/, ''), + type: doc._source!.type, + originId: doc._source!.originId, + ...mockTimestampFields, + version: mockVersion, + score: doc._score, + attributes: doc._source![doc._source!.type], + references: [], + namespaces: doc._source!.type === NAMESPACE_AGNOSTIC_TYPE ? undefined : ['default'], + coreMigrationVersion: expect.any(String), + typeMigrationVersion: expect.any(String), + managed: expect.any(Boolean), + }); + }); + }); + + it(`formats the ES response when there is a namespace`, async () => { + const namespacedSearchResults = generateIndexPatternSearchResults(namespace); + client.search.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise(namespacedSearchResults) + ); + const count = namespacedSearchResults.hits.hits.length; + + const response = await repository.find({ type, namespaces: [namespace] }); + + expect(response.total).toBe(count); + expect(response.saved_objects).toHaveLength(count); + + namespacedSearchResults.hits.hits.forEach((doc, i) => { + expect(response.saved_objects[i]).toEqual({ + id: doc._id.replace(/(foo-namespace\:)?(index-pattern|config|globalType)\:/, ''), + type: doc._source!.type, + originId: doc._source!.originId, + ...mockTimestampFields, + version: mockVersion, + score: doc._score, + attributes: doc._source![doc._source!.type], + references: [], + namespaces: doc._source!.type === NAMESPACE_AGNOSTIC_TYPE ? undefined : [namespace], + coreMigrationVersion: expect.any(String), + typeMigrationVersion: expect.any(String), + managed: expect.any(Boolean), + }); + }); + }); + + it(`should return empty results when attempting to find only invalid or hidden types`, async () => { + const test = async (types: string | string[]) => { + const result = await repository.find({ type: types }); + expect(result).toEqual(expect.objectContaining({ saved_objects: [] })); + expect(client.search).not.toHaveBeenCalled(); + }; + + await test('unknownType'); + await test(HIDDEN_TYPE); + await test(['unknownType', HIDDEN_TYPE]); + }); + + it('migrates the found document', async () => { + const noNamespaceSearchResults = generateIndexPatternSearchResults(); + client.search.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise(noNamespaceSearchResults) + ); + migrator.migrateDocument.mockImplementationOnce((doc) => ({ ...doc, migrated: true })); + await expect(repository.find({ type })).resolves.toHaveProperty( + 'saved_objects.0.migrated', + true + ); + expect(migrator.migrateDocument).toHaveBeenCalledTimes( + noNamespaceSearchResults.hits.hits.length + ); + expectMigrationArgs({ + type, + id: noNamespaceSearchResults.hits.hits[0]._id.replace( + /(index-pattern|config|globalType)\:/, + '' + ), + }); + }); + }); + + describe('search dsl', () => { + const commonOptions: SavedObjectsFindOptions = { + type: [type], + namespaces: [namespace], + search: 'foo*', + searchFields: ['foo'], + sortField: 'name', + sortOrder: 'desc', + defaultSearchOperator: 'AND', + hasReference: { + type: 'foo', + id: '1', + }, + hasNoReference: { + type: 'bar', + id: '1', + }, + }; + + it(`passes mappings, registry, and search options to getSearchDsl`, async () => { + await findSuccess(client, repository, commonOptions, namespace); + expect(mockGetSearchDsl).toHaveBeenCalledWith(mappings, registry, commonOptions); + }); + + it(`accepts hasReferenceOperator`, async () => { + const relevantOpts: SavedObjectsFindOptions = { + ...commonOptions, + hasReferenceOperator: 'AND', + }; + + await findSuccess(client, repository, relevantOpts, namespace); + expect(mockGetSearchDsl).toHaveBeenCalledWith(mappings, registry, { + ...relevantOpts, + hasReferenceOperator: 'AND', + }); + }); + + it(`accepts searchAfter`, async () => { + const relevantOpts: SavedObjectsFindOptions = { + ...commonOptions, + searchAfter: ['1', 'a'], + }; + + await findSuccess(client, repository, relevantOpts, namespace); + expect(mockGetSearchDsl).toHaveBeenCalledWith(mappings, registry, { + ...relevantOpts, + searchAfter: ['1', 'a'], + }); + }); + + it(`accepts pit`, async () => { + const relevantOpts: SavedObjectsFindOptions = { + ...commonOptions, + pit: { id: 'abc123', keepAlive: '2m' }, + }; + + await findSuccess(client, repository, relevantOpts, namespace); + expect(mockGetSearchDsl).toHaveBeenCalledWith(mappings, registry, { + ...relevantOpts, + pit: { id: 'abc123', keepAlive: '2m' }, + }); + }); + + it(`accepts KQL expression filter and passes KueryNode to getSearchDsl`, async () => { + const findOpts: SavedObjectsFindOptions = { + namespaces: [namespace], + search: 'foo*', + searchFields: ['foo'], + type: ['dashboard'], + sortField: 'name', + sortOrder: 'desc', + defaultSearchOperator: 'AND', + hasReference: { + type: 'foo', + id: '1', + }, + filter: 'dashboard.attributes.otherField: *', + }; + + await findSuccess(client, repository, findOpts, namespace); + const { kueryNode } = mockGetSearchDsl.mock.calls[0][2]; + expect(kueryNode).toMatchInlineSnapshot(` + Object { + "arguments": Array [ + Object { + "isQuoted": false, + "type": "literal", + "value": "dashboard.otherField", + }, + Object { + "type": "wildcard", + "value": "@kuery-wildcard@", + }, + ], + "function": "is", + "type": "function", + } + `); + }); + + it(`accepts KQL KueryNode filter and passes KueryNode to getSearchDsl`, async () => { + const findOpts: SavedObjectsFindOptions = { + namespaces: [namespace], + search: 'foo*', + searchFields: ['foo'], + type: ['dashboard'], + sortField: 'name', + sortOrder: 'desc', + defaultSearchOperator: 'AND', + hasReference: { + type: 'foo', + id: '1', + }, + filter: nodeTypes.function.buildNode('is', `dashboard.attributes.otherField`, '*'), + }; + + await findSuccess(client, repository, findOpts, namespace); + const { kueryNode } = mockGetSearchDsl.mock.calls[0][2]; + expect(kueryNode).toMatchInlineSnapshot(` + Object { + "arguments": Array [ + Object { + "isQuoted": false, + "type": "literal", + "value": "dashboard.otherField", + }, + Object { + "type": "wildcard", + "value": "@kuery-wildcard@", + }, + ], + "function": "is", + "type": "function", + } + `); + }); + + it(`supports multiple types`, async () => { + const types = ['config', 'index-pattern']; + await findSuccess(client, repository, { type: types }); + + expect(mockGetSearchDsl).toHaveBeenCalledWith( + mappings, + registry, + expect.objectContaining({ + type: types, + }) + ); + }); + + it(`filters out invalid types`, async () => { + const types = ['config', 'unknownType', 'index-pattern']; + await findSuccess(client, repository, { type: types }); + + expect(mockGetSearchDsl).toHaveBeenCalledWith( + mappings, + registry, + expect.objectContaining({ + type: ['config', 'index-pattern'], + }) + ); + }); + + it(`filters out hidden types`, async () => { + const types = ['config', HIDDEN_TYPE, 'index-pattern']; + await findSuccess(client, repository, { type: types }); + + expect(mockGetSearchDsl).toHaveBeenCalledWith( + mappings, + registry, + expect.objectContaining({ + type: ['config', 'index-pattern'], + }) + ); + }); + }); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/get.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/get.test.ts new file mode 100644 index 0000000000000..c1896e67a6205 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/get.test.ts @@ -0,0 +1,279 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* eslint-disable @typescript-eslint/no-shadow */ + +import { + pointInTimeFinderMock, + mockGetCurrentTime, + mockGetSearchDsl, +} from '../repository.test.mock'; + +import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; + +import type { SavedObjectsBaseOptions } from '@kbn/core-saved-objects-api-server'; +import { ALL_NAMESPACES_STRING } from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsRepository } from '../repository'; +import { loggerMock } from '@kbn/logging-mocks'; +import { SavedObjectsSerializer } from '@kbn/core-saved-objects-base-server-internal'; +import { kibanaMigratorMock } from '../../mocks'; +import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; + +import { + NAMESPACE_AGNOSTIC_TYPE, + MULTI_NAMESPACE_ISOLATED_TYPE, + HIDDEN_TYPE, + mockTimestamp, + mappings, + mockVersion, + createRegistry, + createDocumentMigrator, + getMockGetResponse, + createSpySerializer, + getSuccess, + createBadRequestErrorPayload, + createGenericNotFoundErrorPayload, +} from '../../test_helpers/repository.test.common'; + +describe('#get', () => { + let client: ReturnType; + let repository: SavedObjectsRepository; + let migrator: ReturnType; + let logger: ReturnType; + let serializer: jest.Mocked; + + const registry = createRegistry(); + const documentMigrator = createDocumentMigrator(registry); + + const expectMigrationArgs = (args: unknown, contains = true, n = 1) => { + const obj = contains ? expect.objectContaining(args) : expect.not.objectContaining(args); + expect(migrator.migrateDocument).toHaveBeenNthCalledWith( + n, + obj, + expect.objectContaining({ + allowDowngrade: expect.any(Boolean), + }) + ); + }; + + beforeEach(() => { + pointInTimeFinderMock.mockClear(); + client = elasticsearchClientMock.createElasticsearchClient(); + migrator = kibanaMigratorMock.create(); + documentMigrator.prepareMigrations(); + migrator.migrateDocument = jest.fn().mockImplementation(documentMigrator.migrate); + migrator.runMigrations = jest.fn().mockResolvedValue([{ status: 'skipped' }]); + logger = loggerMock.create(); + + // create a mock serializer "shim" so we can track function calls, but use the real serializer's implementation + serializer = createSpySerializer(registry); + + const allTypes = registry.getAllTypes().map((type) => type.name); + const allowedTypes = [...new Set(allTypes.filter((type) => !registry.isHidden(type)))]; + + // @ts-expect-error must use the private constructor to use the mocked serializer + repository = new SavedObjectsRepository({ + index: '.kibana-test', + mappings, + client, + migrator, + typeRegistry: registry, + serializer, + allowedTypes, + logger, + }); + + mockGetCurrentTime.mockReturnValue(mockTimestamp); + mockGetSearchDsl.mockClear(); + }); + + describe('performGet', () => { + const type = 'index-pattern'; + const id = 'logstash-*'; + const namespace = 'foo-namespace'; + const originId = 'some-origin-id'; + + describe('client calls', () => { + it(`should use the ES get action`, async () => { + await getSuccess(client, repository, registry, type, id); + expect(client.get).toHaveBeenCalledTimes(1); + }); + + it(`prepends namespace to the id when providing namespace for single-namespace type`, async () => { + await getSuccess(client, repository, registry, type, id, { namespace }); + expect(client.get).toHaveBeenCalledWith( + expect.objectContaining({ + id: `${namespace}:${type}:${id}`, + }), + expect.anything() + ); + }); + + it(`doesn't prepend namespace to the id when providing no namespace for single-namespace type`, async () => { + await getSuccess(client, repository, registry, type, id); + expect(client.get).toHaveBeenCalledWith( + expect.objectContaining({ + id: `${type}:${id}`, + }), + expect.anything() + ); + }); + + it(`normalizes options.namespace from 'default' to undefined`, async () => { + await getSuccess(client, repository, registry, type, id, { namespace: 'default' }); + expect(client.get).toHaveBeenCalledWith( + expect.objectContaining({ + id: `${type}:${id}`, + }), + expect.anything() + ); + }); + + it(`doesn't prepend namespace to the id when not using single-namespace type`, async () => { + await getSuccess(client, repository, registry, NAMESPACE_AGNOSTIC_TYPE, id, { namespace }); + expect(client.get).toHaveBeenCalledWith( + expect.objectContaining({ + id: `${NAMESPACE_AGNOSTIC_TYPE}:${id}`, + }), + expect.anything() + ); + + client.get.mockClear(); + await getSuccess(client, repository, registry, MULTI_NAMESPACE_ISOLATED_TYPE, id, { + namespace, + }); + expect(client.get).toHaveBeenCalledWith( + expect.objectContaining({ + id: `${MULTI_NAMESPACE_ISOLATED_TYPE}:${id}`, + }), + expect.anything() + ); + }); + }); + + describe('errors', () => { + const expectNotFoundError = async ( + type: string, + id: string, + options?: SavedObjectsBaseOptions + ) => { + await expect(repository.get(type, id, options)).rejects.toThrowError( + createGenericNotFoundErrorPayload(type, id) + ); + }; + + it(`throws when options.namespace is '*'`, async () => { + await expect( + repository.get(type, id, { namespace: ALL_NAMESPACES_STRING }) + ).rejects.toThrowError(createBadRequestErrorPayload('"options.namespace" cannot be "*"')); + }); + + it(`throws when type is invalid`, async () => { + await expectNotFoundError('unknownType', id); + expect(client.get).not.toHaveBeenCalled(); + }); + + it(`throws when type is hidden`, async () => { + await expectNotFoundError(HIDDEN_TYPE, id); + expect(client.get).not.toHaveBeenCalled(); + }); + + it(`throws when ES is unable to find the document during get`, async () => { + client.get.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise({ + found: false, + } as estypes.GetResponse) + ); + await expectNotFoundError(type, id); + expect(client.get).toHaveBeenCalledTimes(1); + }); + + it(`throws when ES is unable to find the index during get`, async () => { + client.get.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise({} as estypes.GetResponse, { + statusCode: 404, + }) + ); + await expectNotFoundError(type, id); + expect(client.get).toHaveBeenCalledTimes(1); + }); + + it(`throws when type is multi-namespace and the document exists, but not in this namespace`, async () => { + const response = getMockGetResponse( + registry, + { type: MULTI_NAMESPACE_ISOLATED_TYPE, id }, + namespace + ); + client.get.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise(response) + ); + await expectNotFoundError(MULTI_NAMESPACE_ISOLATED_TYPE, id, { + namespace: 'bar-namespace', + }); + expect(client.get).toHaveBeenCalledTimes(1); + }); + }); + + describe('returns', () => { + it(`formats the ES response`, async () => { + const result = await getSuccess(client, repository, registry, type, id); + expect(result).toEqual({ + id, + type, + updated_at: mockTimestamp, + version: mockVersion, + attributes: { + title: 'Testing', + }, + references: [], + namespaces: ['default'], + coreMigrationVersion: expect.any(String), + typeMigrationVersion: expect.any(String), + managed: expect.any(Boolean), + }); + }); + + it(`includes namespaces if type is multi-namespace`, async () => { + const result = await getSuccess( + client, + repository, + registry, + MULTI_NAMESPACE_ISOLATED_TYPE, + id + ); + expect(result).toMatchObject({ + namespaces: expect.any(Array), + }); + }); + + it(`include namespaces if type is not multi-namespace`, async () => { + const result = await getSuccess(client, repository, registry, type, id); + expect(result).toMatchObject({ + namespaces: ['default'], + }); + }); + + it(`includes originId property if present in cluster call response`, async () => { + const result = await getSuccess(client, repository, registry, type, id, {}, originId); + expect(result).toMatchObject({ originId }); + }); + }); + + it('migrates the fetched document', async () => { + migrator.migrateDocument.mockReturnValueOnce( + 'migrated' as unknown as ReturnType + ); + await expect(getSuccess(client, repository, registry, type, id)).resolves.toBe('migrated'); + expect(migrator.migrateDocument).toHaveBeenCalledTimes(1); + expectMigrationArgs({ + id, + type, + }); + }); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/increment_counter.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/increment_counter.test.ts new file mode 100644 index 0000000000000..3e976295dc59e --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/increment_counter.test.ts @@ -0,0 +1,530 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* eslint-disable @typescript-eslint/no-shadow */ + +import { + pointInTimeFinderMock, + mockGetCurrentTime, + mockPreflightCheckForCreate, + mockGetSearchDsl, +} from '../repository.test.mock'; + +import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; + +import type { + SavedObjectsIncrementCounterField, + SavedObjectsIncrementCounterOptions, +} from '@kbn/core-saved-objects-api-server'; +import { + type SavedObjectUnsanitizedDoc, + MAIN_SAVED_OBJECT_INDEX, +} from '@kbn/core-saved-objects-server'; +import { ALL_NAMESPACES_STRING } from '@kbn/core-saved-objects-utils-server'; +import { SavedObjectsRepository } from '../repository'; +import { loggerMock } from '@kbn/logging-mocks'; +import { SavedObjectsSerializer } from '@kbn/core-saved-objects-base-server-internal'; +import { kibanaMigratorMock } from '../../mocks'; +import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; + +import { + NAMESPACE_AGNOSTIC_TYPE, + MULTI_NAMESPACE_ISOLATED_TYPE, + HIDDEN_TYPE, + mockVersionProps, + mockTimestampFields, + mockTimestamp, + mappings, + mockVersion, + createRegistry, + createDocumentMigrator, + getMockGetResponse, + createSpySerializer, + createBadRequestErrorPayload, + createUnsupportedTypeErrorPayload, + createConflictErrorPayload, +} from '../../test_helpers/repository.test.common'; + +describe('#incrementCounter', () => { + let client: ReturnType; + let repository: SavedObjectsRepository; + let migrator: ReturnType; + let logger: ReturnType; + let serializer: jest.Mocked; + + const registry = createRegistry(); + const documentMigrator = createDocumentMigrator(registry); + + const expectMigrationArgs = (args: unknown, contains = true, n = 1) => { + const obj = contains ? expect.objectContaining(args) : expect.not.objectContaining(args); + expect(migrator.migrateDocument).toHaveBeenNthCalledWith( + n, + obj, + expect.objectContaining({ + allowDowngrade: expect.any(Boolean), + }) + ); + }; + + beforeEach(() => { + pointInTimeFinderMock.mockClear(); + client = elasticsearchClientMock.createElasticsearchClient(); + migrator = kibanaMigratorMock.create(); + documentMigrator.prepareMigrations(); + migrator.migrateDocument = jest.fn().mockImplementation(documentMigrator.migrate); + migrator.runMigrations = jest.fn().mockResolvedValue([{ status: 'skipped' }]); + logger = loggerMock.create(); + + // create a mock serializer "shim" so we can track function calls, but use the real serializer's implementation + serializer = createSpySerializer(registry); + + const allTypes = registry.getAllTypes().map((type) => type.name); + const allowedTypes = [...new Set(allTypes.filter((type) => !registry.isHidden(type)))]; + + // @ts-expect-error must use the private constructor to use the mocked serializer + repository = new SavedObjectsRepository({ + index: '.kibana-test', + mappings, + client, + migrator, + typeRegistry: registry, + serializer, + allowedTypes, + logger, + }); + + mockGetCurrentTime.mockReturnValue(mockTimestamp); + mockGetSearchDsl.mockClear(); + }); + + // Setup migration mock for creating an object + const mockMigrationVersion = { foo: '2.3.4' }; + const mockMigrateDocument = (doc: SavedObjectUnsanitizedDoc) => ({ + ...doc, + attributes: { + ...doc.attributes, + ...(doc.attributes?.title && { title: `${doc.attributes.title}!!` }), + }, + migrationVersion: mockMigrationVersion, + managed: doc.managed ?? false, + references: [{ name: 'search_0', type: 'search', id: '123' }], + }); + + describe('performIncrementCounter', () => { + const type = 'config'; + const id = 'one'; + const counterFields = ['buildNum', 'apiCallsCount']; + const namespace = 'foo-namespace'; + const originId = 'some-origin-id'; + + const incrementCounterSuccess = async ( + type: string, + id: string, + fields: Array, + options?: SavedObjectsIncrementCounterOptions, + internalOptions: { mockGetResponseValue?: estypes.GetResponse } = {} + ) => { + const { mockGetResponseValue } = internalOptions; + const isMultiNamespace = registry.isMultiNamespace(type); + if (isMultiNamespace) { + const response = + mockGetResponseValue ?? getMockGetResponse(registry, { type, id }, options?.namespace); + client.get.mockResponseOnce(response); + } + + client.update.mockResponseImplementation((params) => { + return { + body: { + _id: params.id, + ...mockVersionProps, + _index: MAIN_SAVED_OBJECT_INDEX, + get: { + found: true, + _source: { + type, + ...mockTimestampFields, + [type]: { + ...fields.reduce((acc, field) => { + acc[typeof field === 'string' ? field : field.fieldName] = 8468; + return acc; + }, {} as Record), + defaultIndex: 'logstash-*', + }, + }, + }, + } as estypes.UpdateResponse, + }; + }); + + const result = await repository.incrementCounter(type, id, fields, options); + expect(client.get).toHaveBeenCalledTimes(isMultiNamespace ? 1 : 0); + return result; + }; + + beforeEach(() => { + mockPreflightCheckForCreate.mockReset(); + mockPreflightCheckForCreate.mockImplementation(({ objects }) => { + return Promise.resolve(objects.map(({ type, id }) => ({ type, id }))); // respond with no errors by default + }); + }); + + describe('client calls', () => { + it(`should use the ES update action if type is not multi-namespace`, async () => { + await incrementCounterSuccess(type, id, counterFields, { namespace }); + expect(client.get).not.toHaveBeenCalled(); + expect(mockPreflightCheckForCreate).not.toHaveBeenCalled(); + expect(client.update).toHaveBeenCalledTimes(1); + }); + + it(`should use the ES get action then update action if type is multi-namespace, ID is defined, and overwrite=true`, async () => { + await incrementCounterSuccess(MULTI_NAMESPACE_ISOLATED_TYPE, id, counterFields, { + namespace, + }); + expect(client.get).toHaveBeenCalledTimes(1); + expect(mockPreflightCheckForCreate).not.toHaveBeenCalled(); + expect(client.update).toHaveBeenCalledTimes(1); + }); + + it(`should check for alias conflicts if a new multi-namespace object would be created`, async () => { + await incrementCounterSuccess( + MULTI_NAMESPACE_ISOLATED_TYPE, + id, + counterFields, + { namespace }, + { mockGetResponseValue: { found: false } as estypes.GetResponse } + ); + expect(client.get).toHaveBeenCalledTimes(1); + expect(mockPreflightCheckForCreate).toHaveBeenCalledTimes(1); + expect(client.update).toHaveBeenCalledTimes(1); + }); + + it(`defaults to a refresh setting of wait_for`, async () => { + await incrementCounterSuccess(type, id, counterFields, { namespace }); + expect(client.update).toHaveBeenCalledWith( + expect.objectContaining({ + refresh: 'wait_for', + }), + expect.anything() + ); + }); + + it(`uses the 'upsertAttributes' option when specified`, async () => { + const upsertAttributes = { + foo: 'bar', + hello: 'dolly', + }; + await incrementCounterSuccess(type, id, counterFields, { namespace, upsertAttributes }); + expect(client.update).toHaveBeenCalledWith( + expect.objectContaining({ + body: expect.objectContaining({ + upsert: expect.objectContaining({ + [type]: { + foo: 'bar', + hello: 'dolly', + ...counterFields.reduce((aggs, field) => { + return { + ...aggs, + [field]: 1, + }; + }, {}), + }, + }), + }), + }), + expect.anything() + ); + }); + + it(`prepends namespace to the id when providing namespace for single-namespace type`, async () => { + await incrementCounterSuccess(type, id, counterFields, { namespace }); + expect(client.update).toHaveBeenCalledWith( + expect.objectContaining({ + id: `${namespace}:${type}:${id}`, + }), + expect.anything() + ); + }); + + it(`doesn't prepend namespace to the id when providing no namespace for single-namespace type`, async () => { + await incrementCounterSuccess(type, id, counterFields); + expect(client.update).toHaveBeenCalledWith( + expect.objectContaining({ + id: `${type}:${id}`, + }), + expect.anything() + ); + }); + + it(`normalizes options.namespace from 'default' to undefined`, async () => { + await incrementCounterSuccess(type, id, counterFields, { namespace: 'default' }); + expect(client.update).toHaveBeenCalledWith( + expect.objectContaining({ + id: `${type}:${id}`, + }), + expect.anything() + ); + }); + + it(`doesn't prepend namespace to the id when not using single-namespace type`, async () => { + await incrementCounterSuccess(NAMESPACE_AGNOSTIC_TYPE, id, counterFields, { namespace }); + expect(client.update).toHaveBeenCalledWith( + expect.objectContaining({ + id: `${NAMESPACE_AGNOSTIC_TYPE}:${id}`, + }), + expect.anything() + ); + + client.update.mockClear(); + await incrementCounterSuccess(MULTI_NAMESPACE_ISOLATED_TYPE, id, counterFields, { + namespace, + }); + expect(client.update).toHaveBeenCalledWith( + expect.objectContaining({ + id: `${MULTI_NAMESPACE_ISOLATED_TYPE}:${id}`, + }), + expect.anything() + ); + }); + }); + + describe('errors', () => { + const expectUnsupportedTypeError = async ( + type: string, + id: string, + field: Array + ) => { + await expect(repository.incrementCounter(type, id, field)).rejects.toThrowError( + createUnsupportedTypeErrorPayload(type) + ); + }; + + it(`throws when options.namespace is '*'`, async () => { + await expect( + repository.incrementCounter(type, id, counterFields, { + namespace: ALL_NAMESPACES_STRING, + }) + ).rejects.toThrowError(createBadRequestErrorPayload('"options.namespace" cannot be "*"')); + }); + + it(`throws when type is not a string`, async () => { + const test = async (type: unknown) => { + await expect( + // @ts-expect-error type is supposed to be a string + repository.incrementCounter(type, id, counterFields) + ).rejects.toThrowError(`"type" argument must be a string`); + expect(client.update).not.toHaveBeenCalled(); + }; + + await test(null); + await test(42); + await test(false); + await test({}); + }); + + it(`throws when id is empty`, async () => { + await expect(repository.incrementCounter(type, '', counterFields)).rejects.toThrowError( + createBadRequestErrorPayload('id cannot be empty') + ); + expect(client.update).not.toHaveBeenCalled(); + }); + + it(`throws when counterField is not CounterField type`, async () => { + const test = async (field: unknown[]) => { + await expect( + // @ts-expect-error field is of wrong type + repository.incrementCounter(type, id, field) + ).rejects.toThrowError( + `"counterFields" argument must be of type Array` + ); + expect(client.update).not.toHaveBeenCalled(); + }; + + await test([null]); + await test([42]); + await test([false]); + await test([{}]); + await test([{}, false, 42, null, 'string']); + await test([{ fieldName: 'string' }, false, null, 'string']); + }); + + it(`throws when type is invalid`, async () => { + await expectUnsupportedTypeError('unknownType', id, counterFields); + expect(client.update).not.toHaveBeenCalled(); + }); + + it(`throws when type is hidden`, async () => { + await expectUnsupportedTypeError(HIDDEN_TYPE, id, counterFields); + expect(client.update).not.toHaveBeenCalled(); + }); + + it(`throws when there is a conflict with an existing multi-namespace saved object (get)`, async () => { + const response = getMockGetResponse( + registry, + { type: MULTI_NAMESPACE_ISOLATED_TYPE, id }, + 'bar-namespace' + ); + client.get.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise(response) + ); + await expect( + repository.incrementCounter(MULTI_NAMESPACE_ISOLATED_TYPE, id, counterFields, { + namespace, + }) + ).rejects.toThrowError(createConflictErrorPayload(MULTI_NAMESPACE_ISOLATED_TYPE, id)); + expect(client.get).toHaveBeenCalledTimes(1); + expect(mockPreflightCheckForCreate).not.toHaveBeenCalled(); + expect(client.update).not.toHaveBeenCalled(); + }); + + it(`throws when there is an alias conflict from preflightCheckForCreate`, async () => { + client.get.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise({ + found: false, + } as estypes.GetResponse) + ); + mockPreflightCheckForCreate.mockResolvedValue([ + { type: 'foo', id: 'bar', error: { type: 'aliasConflict' } }, + ]); + await expect( + repository.incrementCounter(MULTI_NAMESPACE_ISOLATED_TYPE, id, counterFields, { + namespace, + }) + ).rejects.toThrowError(createConflictErrorPayload(MULTI_NAMESPACE_ISOLATED_TYPE, id)); + expect(client.get).toHaveBeenCalledTimes(1); + expect(mockPreflightCheckForCreate).toHaveBeenCalledTimes(1); + expect(client.update).not.toHaveBeenCalled(); + }); + + it(`does not throw when there is a different error from preflightCheckForCreate`, async () => { + client.get.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise({ + found: false, + } as estypes.GetResponse) + ); + mockPreflightCheckForCreate.mockResolvedValue([ + { type: 'foo', id: 'bar', error: { type: 'conflict' } }, + ]); + await incrementCounterSuccess( + MULTI_NAMESPACE_ISOLATED_TYPE, + id, + counterFields, + { namespace }, + { mockGetResponseValue: { found: false } as estypes.GetResponse } + ); + expect(client.get).toHaveBeenCalledTimes(1); + expect(mockPreflightCheckForCreate).toHaveBeenCalledTimes(1); + expect(client.update).toHaveBeenCalledTimes(1); + }); + }); + + describe('migration', () => { + beforeEach(() => { + migrator.migrateDocument.mockImplementation(mockMigrateDocument); + }); + + it(`migrates a document and serializes the migrated doc`, async () => { + const migrationVersion = mockMigrationVersion; + await incrementCounterSuccess(type, id, counterFields, { migrationVersion }); + const attributes = { buildNum: 1, apiCallsCount: 1 }; // this is added by the incrementCounter function + const doc = { type, id, attributes, migrationVersion, ...mockTimestampFields }; + expectMigrationArgs(doc); + + const migratedDoc = migrator.migrateDocument(doc); + expect(serializer.savedObjectToRaw).toHaveBeenLastCalledWith(migratedDoc); + }); + }); + + describe('returns', () => { + it(`formats the ES response`, async () => { + client.update.mockResponseImplementation((params) => { + return { + body: { + _id: params.id, + ...mockVersionProps, + _index: MAIN_SAVED_OBJECT_INDEX, + get: { + found: true, + _source: { + type: 'config', + ...mockTimestampFields, + config: { + buildNum: 8468, + apiCallsCount: 100, + defaultIndex: 'logstash-*', + }, + originId, + }, + }, + } as estypes.UpdateResponse, + }; + }); + + const response = await repository.incrementCounter( + 'config', + '6.0.0-alpha1', + ['buildNum', 'apiCallsCount'], + { + namespace: 'foo-namespace', + } + ); + + expect(response).toEqual({ + type: 'config', + id: '6.0.0-alpha1', + ...mockTimestampFields, + version: mockVersion, + references: [], + attributes: { + buildNum: 8468, + apiCallsCount: 100, + defaultIndex: 'logstash-*', + }, + originId, + }); + }); + + it('increments counter by incrementBy config', async () => { + await incrementCounterSuccess(type, id, [{ fieldName: counterFields[0], incrementBy: 3 }]); + + expect(client.update).toBeCalledTimes(1); + expect(client.update).toBeCalledWith( + expect.objectContaining({ + body: expect.objectContaining({ + script: expect.objectContaining({ + params: expect.objectContaining({ + counterFieldNames: [counterFields[0]], + counts: [3], + }), + }), + }), + }), + expect.anything() + ); + }); + + it('does not increment counter when incrementBy is 0', async () => { + await incrementCounterSuccess(type, id, [{ fieldName: counterFields[0], incrementBy: 0 }]); + + expect(client.update).toBeCalledTimes(1); + expect(client.update).toBeCalledWith( + expect.objectContaining({ + body: expect.objectContaining({ + script: expect.objectContaining({ + params: expect.objectContaining({ + counterFieldNames: [counterFields[0]], + counts: [0], + }), + }), + }), + }), + expect.anything() + ); + }); + }); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/open_point_in_time.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/open_point_in_time.test.ts new file mode 100644 index 0000000000000..04b107d0a1028 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/open_point_in_time.test.ts @@ -0,0 +1,237 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/* eslint-disable @typescript-eslint/no-shadow */ + +import { + pointInTimeFinderMock, + mockGetCurrentTime, + mockGetSearchDsl, +} from '../repository.test.mock'; + +import type { + SavedObjectsCreatePointInTimeFinderDependencies, + SavedObjectsCreatePointInTimeFinderOptions, + SavedObjectsOpenPointInTimeOptions, +} from '@kbn/core-saved-objects-api-server'; +import { SavedObjectsRepository } from '../repository'; +import { loggerMock } from '@kbn/logging-mocks'; +import { SavedObjectsSerializer } from '@kbn/core-saved-objects-base-server-internal'; +import { kibanaMigratorMock } from '../../mocks'; +import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; + +import { + HIDDEN_TYPE, + mockTimestamp, + mappings, + createRegistry, + createDocumentMigrator, + createSpySerializer, + createGenericNotFoundErrorPayload, +} from '../../test_helpers/repository.test.common'; +import { PointInTimeFinder } from '../point_in_time_finder'; + +describe('SavedObjectsRepository', () => { + let client: ReturnType; + let repository: SavedObjectsRepository; + let migrator: ReturnType; + let logger: ReturnType; + let serializer: jest.Mocked; + + const registry = createRegistry(); + const documentMigrator = createDocumentMigrator(registry); + + beforeEach(() => { + pointInTimeFinderMock.mockClear(); + client = elasticsearchClientMock.createElasticsearchClient(); + migrator = kibanaMigratorMock.create(); + documentMigrator.prepareMigrations(); + migrator.migrateDocument = jest.fn().mockImplementation(documentMigrator.migrate); + migrator.runMigrations = jest.fn().mockResolvedValue([{ status: 'skipped' }]); + logger = loggerMock.create(); + + // create a mock serializer "shim" so we can track function calls, but use the real serializer's implementation + serializer = createSpySerializer(registry); + + const allTypes = registry.getAllTypes().map((type) => type.name); + const allowedTypes = [...new Set(allTypes.filter((type) => !registry.isHidden(type)))]; + + // @ts-expect-error must use the private constructor to use the mocked serializer + repository = new SavedObjectsRepository({ + index: '.kibana-test', + mappings, + client, + migrator, + typeRegistry: registry, + serializer, + allowedTypes, + logger, + }); + + mockGetCurrentTime.mockReturnValue(mockTimestamp); + mockGetSearchDsl.mockClear(); + }); + + describe('#openPointInTimeForType', () => { + const type = 'index-pattern'; + + const generateResults = (id?: string) => ({ id: id || 'id' }); + const successResponse = async (type: string, options?: SavedObjectsOpenPointInTimeOptions) => { + client.openPointInTime.mockResponseOnce(generateResults()); + const result = await repository.openPointInTimeForType(type, options); + expect(client.openPointInTime).toHaveBeenCalledTimes(1); + return result; + }; + + describe('client calls', () => { + it(`should use the ES PIT API`, async () => { + await successResponse(type); + expect(client.openPointInTime).toHaveBeenCalledTimes(1); + }); + + it(`accepts preference`, async () => { + await successResponse(type, { preference: 'pref' }); + expect(client.openPointInTime).toHaveBeenCalledWith( + expect.objectContaining({ + preference: 'pref', + }), + expect.anything() + ); + }); + + it(`accepts keepAlive`, async () => { + await successResponse(type, { keepAlive: '2m' }); + expect(client.openPointInTime).toHaveBeenCalledWith( + expect.objectContaining({ + keep_alive: '2m', + }), + expect.anything() + ); + }); + + it(`defaults keepAlive to 5m`, async () => { + await successResponse(type); + expect(client.openPointInTime).toHaveBeenCalledWith( + expect.objectContaining({ + keep_alive: '5m', + }), + expect.anything() + ); + }); + }); + + describe('errors', () => { + const expectNotFoundError = async (types: string | string[]) => { + await expect(repository.openPointInTimeForType(types)).rejects.toThrowError( + createGenericNotFoundErrorPayload() + ); + }; + + it(`throws when ES is unable to find the index`, async () => { + client.openPointInTime.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise( + { id: 'error' }, + { statusCode: 404 } + ) + ); + await expectNotFoundError(type); + expect(client.openPointInTime).toHaveBeenCalledTimes(1); + }); + + it(`should return generic not found error when attempting to find only invalid or hidden types`, async () => { + const test = async (types: string | string[]) => { + await expectNotFoundError(types); + expect(client.openPointInTime).not.toHaveBeenCalled(); + }; + + await test('unknownType'); + await test(HIDDEN_TYPE); + await test(['unknownType', HIDDEN_TYPE]); + }); + }); + + describe('returns', () => { + it(`returns id in the expected format`, async () => { + const id = 'abc123'; + const results = generateResults(id); + client.openPointInTime.mockResolvedValueOnce( + elasticsearchClientMock.createSuccessTransportRequestPromise(results) + ); + const response = await repository.openPointInTimeForType(type); + expect(response).toEqual({ id }); + }); + }); + }); + + describe('#closePointInTime', () => { + const generateResults = () => ({ succeeded: true, num_freed: 3 }); + const successResponse = async (id: string) => { + client.closePointInTime.mockResponseOnce(generateResults()); + const result = await repository.closePointInTime(id); + expect(client.closePointInTime).toHaveBeenCalledTimes(1); + return result; + }; + + describe('client calls', () => { + it(`should use the ES PIT API`, async () => { + await successResponse('abc123'); + expect(client.closePointInTime).toHaveBeenCalledTimes(1); + }); + + it(`accepts id`, async () => { + await successResponse('abc123'); + expect(client.closePointInTime).toHaveBeenCalledWith( + expect.objectContaining({ + body: expect.objectContaining({ + id: 'abc123', + }), + }), + expect.anything() + ); + }); + }); + + describe('returns', () => { + it(`returns response body from ES`, async () => { + const results = generateResults(); + client.closePointInTime.mockResponseOnce(results); + const response = await repository.closePointInTime('abc123'); + expect(response).toEqual(results); + }); + }); + }); + + describe('#createPointInTimeFinder', () => { + it('returns a new PointInTimeFinder instance', async () => { + const result = await repository.createPointInTimeFinder({ type: 'PIT' }); + expect(result).toBeInstanceOf(PointInTimeFinder); + }); + + it('calls PointInTimeFinder with the provided options and dependencies', async () => { + const options: SavedObjectsCreatePointInTimeFinderOptions = { + type: 'my-type', + }; + const dependencies: SavedObjectsCreatePointInTimeFinderDependencies = { + client: { + find: jest.fn(), + openPointInTimeForType: jest.fn(), + closePointInTime: jest.fn(), + }, + }; + + await repository.createPointInTimeFinder(options, dependencies); + expect(pointInTimeFinderMock).toHaveBeenCalledWith( + options, + expect.objectContaining({ + ...dependencies, + logger, + }) + ); + }); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/remove_references_to.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/remove_references_to.test.ts index 78c3e8d1faf92..70e601235f184 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/remove_references_to.test.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/remove_references_to.test.ts @@ -6,71 +6,277 @@ * Side Public License, v 1. */ -import { apiContextMock, ApiExecutionContextMock } from '../../mocks'; -import { createType } from '../../test_helpers/repository.test.common'; +/* eslint-disable @typescript-eslint/no-shadow */ + +import { + pointInTimeFinderMock, + mockGetCurrentTime, + mockGetSearchDsl, +} from '../repository.test.mock'; + +import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; + +import { SavedObjectsRepository } from '../repository'; +import { loggerMock } from '@kbn/logging-mocks'; +import { SavedObjectsSerializer } from '@kbn/core-saved-objects-base-server-internal'; +import { apiContextMock, ApiExecutionContextMock, kibanaMigratorMock } from '../../mocks'; +import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; + +import { + mockTimestamp, + mappings, + createRegistry, + createDocumentMigrator, + removeReferencesToSuccess, + createSpySerializer, + createConflictErrorPayload, + createType, +} from '../../test_helpers/repository.test.common'; import { performRemoveReferencesTo } from './remove_references_to'; -const fooType = createType('foo', {}); -const barType = createType('bar', {}); +describe('SavedObjectsRepository', () => { + let client: ReturnType; + let repository: SavedObjectsRepository; + let migrator: ReturnType; + let logger: ReturnType; + let serializer: jest.Mocked; -describe('performRemoveReferencesTo', () => { - const namespace = 'some_ns'; - const indices = ['.kib_1', '.kib_2']; - let apiExecutionContext: ApiExecutionContextMock; + const registry = createRegistry(); + const documentMigrator = createDocumentMigrator(registry); + const fooType = createType('foo', {}); + const barType = createType('bar', {}); beforeEach(() => { - apiExecutionContext = apiContextMock.create(); - apiExecutionContext.registry.registerType(fooType); - apiExecutionContext.registry.registerType(barType); - - apiExecutionContext.helpers.common.getCurrentNamespace.mockImplementation( - (space) => space ?? 'default' - ); - apiExecutionContext.helpers.common.getIndicesForTypes.mockReturnValue(indices); + pointInTimeFinderMock.mockClear(); + client = elasticsearchClientMock.createElasticsearchClient(); + migrator = kibanaMigratorMock.create(); + documentMigrator.prepareMigrations(); + migrator.migrateDocument = jest.fn().mockImplementation(documentMigrator.migrate); + migrator.runMigrations = jest.fn().mockResolvedValue([{ status: 'skipped' }]); + logger = loggerMock.create(); + + // create a mock serializer "shim" so we can track function calls, but use the real serializer's implementation + serializer = createSpySerializer(registry); + + const allTypes = registry.getAllTypes().map((type) => type.name); + const allowedTypes = [...new Set(allTypes.filter((type) => !registry.isHidden(type)))]; + + // @ts-expect-error must use the private constructor to use the mocked serializer + repository = new SavedObjectsRepository({ + index: '.kibana-test', + mappings, + client, + migrator, + typeRegistry: registry, + serializer, + allowedTypes, + logger, + }); + + mockGetCurrentTime.mockReturnValue(mockTimestamp); + mockGetSearchDsl.mockClear(); }); - describe('with all extensions enabled', () => { - it('calls getCurrentNamespace with the correct parameters', async () => { - await performRemoveReferencesTo( - { type: 'foo', id: 'id', options: { namespace } }, - apiExecutionContext - ); + describe('#removeReferencesTo', () => { + const type = 'type'; + const id = 'id'; + const defaultOptions = {}; + const updatedCount = 42; + + describe('client calls', () => { + it('should use the ES updateByQuery action', async () => { + await removeReferencesToSuccess(client, repository, type, id); + expect(client.updateByQuery).toHaveBeenCalledTimes(1); + }); - const commonHelper = apiExecutionContext.helpers.common; - expect(commonHelper.getCurrentNamespace).toHaveBeenCalledTimes(1); - expect(commonHelper.getCurrentNamespace).toHaveBeenLastCalledWith(namespace); + it('uses the correct default `refresh` value', async () => { + await removeReferencesToSuccess(client, repository, type, id); + expect(client.updateByQuery).toHaveBeenCalledWith( + expect.objectContaining({ + refresh: true, + }), + expect.any(Object) + ); + }); + + it('merges output of getSearchDsl into es request body', async () => { + const query = { query: 1, aggregations: 2 }; + mockGetSearchDsl.mockReturnValue(query); + await removeReferencesToSuccess(client, repository, type, id, { type }); + + expect(client.updateByQuery).toHaveBeenCalledWith( + expect.objectContaining({ + body: expect.objectContaining({ ...query }), + }), + expect.anything() + ); + }); + + it('should set index to all known SO indices on the request', async () => { + await removeReferencesToSuccess(client, repository, type, id); + expect(client.updateByQuery).toHaveBeenCalledWith( + expect.objectContaining({ + index: ['.kibana-test_8.0.0-testing', 'custom_8.0.0-testing'], + }), + expect.anything() + ); + }); + + it('should use the `refresh` option in the request', async () => { + const refresh = Symbol(); + + await removeReferencesToSuccess(client, repository, type, id, { refresh }); + expect(client.updateByQuery).toHaveBeenCalledWith( + expect.objectContaining({ + refresh, + }), + expect.anything() + ); + }); + + it('should pass the correct parameters to the update script', async () => { + await removeReferencesToSuccess(client, repository, type, id); + expect(client.updateByQuery).toHaveBeenCalledWith( + expect.objectContaining({ + body: expect.objectContaining({ + script: expect.objectContaining({ + params: { + type, + id, + }, + }), + }), + }), + expect.anything() + ); + }); }); - it('calls authorizeRemoveReferences with the correct parameters', async () => { - await performRemoveReferencesTo( - { type: 'foo', id: 'id', options: { namespace } }, - apiExecutionContext - ); + describe('search dsl', () => { + it(`passes mappings and registry to getSearchDsl`, async () => { + await removeReferencesToSuccess(client, repository, type, id); + expect(mockGetSearchDsl).toHaveBeenCalledWith(mappings, registry, expect.anything()); + }); + + it('passes namespace to getSearchDsl', async () => { + await removeReferencesToSuccess(client, repository, type, id, { namespace: 'some-ns' }); + expect(mockGetSearchDsl).toHaveBeenCalledWith( + mappings, + registry, + expect.objectContaining({ + namespaces: ['some-ns'], + }) + ); + }); - const securityExt = apiExecutionContext.extensions.securityExtension!; - expect(securityExt.authorizeRemoveReferences).toHaveBeenCalledTimes(1); - expect(securityExt.authorizeRemoveReferences).toHaveBeenLastCalledWith({ - namespace, - object: { type: 'foo', id: 'id' }, + it('passes hasReference to getSearchDsl', async () => { + await removeReferencesToSuccess(client, repository, type, id); + expect(mockGetSearchDsl).toHaveBeenCalledWith( + mappings, + registry, + expect.objectContaining({ + hasReference: { + type, + id, + }, + }) + ); + }); + + it('passes all known types to getSearchDsl', async () => { + await removeReferencesToSuccess(client, repository, type, id); + expect(mockGetSearchDsl).toHaveBeenCalledWith( + mappings, + registry, + expect.objectContaining({ + type: registry.getAllTypes().map((type) => type.name), + }) + ); }); }); - it('calls client.updateByQuery with the correct parameters', async () => { - await performRemoveReferencesTo( - { type: 'foo', id: 'id', options: { namespace, refresh: false } }, - apiExecutionContext - ); + describe('returns', () => { + it('returns the updated count from the ES response', async () => { + const response = await removeReferencesToSuccess(client, repository, type, id); + expect(response.updated).toBe(updatedCount); + }); + }); + + describe('errors', () => { + it(`throws when ES returns failures`, async () => { + client.updateByQuery.mockResponseOnce({ + updated: 7, + failures: [ + { id: 'failure' } as estypes.BulkIndexByScrollFailure, + { id: 'another-failure' } as estypes.BulkIndexByScrollFailure, + ], + }); + + await expect(repository.removeReferencesTo(type, id, defaultOptions)).rejects.toThrowError( + createConflictErrorPayload(type, id) + ); + }); + }); + }); + describe('performRemoveReferencesTo', () => { + const namespace = 'some_ns'; + const indices = ['.kib_1', '.kib_2']; + let apiExecutionContext: ApiExecutionContextMock; + + beforeEach(() => { + apiExecutionContext = apiContextMock.create(); + apiExecutionContext.registry.registerType(fooType); + apiExecutionContext.registry.registerType(barType); - const client = apiExecutionContext.client; - expect(client.updateByQuery).toHaveBeenCalledTimes(1); - expect(client.updateByQuery).toHaveBeenLastCalledWith( - { - refresh: false, - index: indices, - body: expect.any(Object), - }, - { ignore: [404], meta: true } + apiExecutionContext.helpers.common.getCurrentNamespace.mockImplementation( + (space) => space ?? 'default' ); + apiExecutionContext.helpers.common.getIndicesForTypes.mockReturnValue(indices); + }); + + describe('with all extensions enabled', () => { + it('calls getCurrentNamespace with the correct parameters', async () => { + await performRemoveReferencesTo( + { type: 'foo', id: 'id', options: { namespace } }, + apiExecutionContext + ); + + const commonHelper = apiExecutionContext.helpers.common; + expect(commonHelper.getCurrentNamespace).toHaveBeenCalledTimes(1); + expect(commonHelper.getCurrentNamespace).toHaveBeenLastCalledWith(namespace); + }); + + it('calls authorizeRemoveReferences with the correct parameters', async () => { + await performRemoveReferencesTo( + { type: 'foo', id: 'id', options: { namespace } }, + apiExecutionContext + ); + + const securityExt = apiExecutionContext.extensions.securityExtension!; + expect(securityExt.authorizeRemoveReferences).toHaveBeenCalledTimes(1); + expect(securityExt.authorizeRemoveReferences).toHaveBeenLastCalledWith({ + namespace, + object: { type: 'foo', id: 'id' }, + }); + }); + + it('calls client.updateByQuery with the correct parameters', async () => { + await performRemoveReferencesTo( + { type: 'foo', id: 'id', options: { namespace, refresh: false } }, + apiExecutionContext + ); + + const client = apiExecutionContext.client; + expect(client.updateByQuery).toHaveBeenCalledTimes(1); + expect(client.updateByQuery).toHaveBeenLastCalledWith( + { + refresh: false, + index: indices, + body: expect.any(Object), + }, + { ignore: [404], meta: true } + ); + }); }); }); }); diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/resolve.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/resolve.test.ts new file mode 100644 index 0000000000000..25acc55cc77c2 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/resolve.test.ts @@ -0,0 +1,108 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { + pointInTimeFinderMock, + mockInternalBulkResolve, + mockGetCurrentTime, + mockGetSearchDsl, +} from '../repository.test.mock'; + +import type { SavedObjectsResolveResponse } from '@kbn/core-saved-objects-api-server'; +import { type BulkResolveError } from '@kbn/core-saved-objects-server'; +import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server'; +import { SavedObjectsRepository } from '../repository'; +import { loggerMock } from '@kbn/logging-mocks'; +import { SavedObjectsSerializer } from '@kbn/core-saved-objects-base-server-internal'; +import { kibanaMigratorMock } from '../../mocks'; +import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; + +import { + mockTimestamp, + mappings, + createRegistry, + createDocumentMigrator, + createSpySerializer, +} from '../../test_helpers/repository.test.common'; + +describe('SavedObjectsRepository', () => { + let client: ReturnType; + let repository: SavedObjectsRepository; + let migrator: ReturnType; + let logger: ReturnType; + let serializer: jest.Mocked; + + const registry = createRegistry(); + const documentMigrator = createDocumentMigrator(registry); + + beforeEach(() => { + pointInTimeFinderMock.mockClear(); + client = elasticsearchClientMock.createElasticsearchClient(); + migrator = kibanaMigratorMock.create(); + documentMigrator.prepareMigrations(); + migrator.migrateDocument = jest.fn().mockImplementation(documentMigrator.migrate); + migrator.runMigrations = jest.fn().mockResolvedValue([{ status: 'skipped' }]); + logger = loggerMock.create(); + + // create a mock serializer "shim" so we can track function calls, but use the real serializer's implementation + serializer = createSpySerializer(registry); + + const allTypes = registry.getAllTypes().map((type) => type.name); + const allowedTypes = [...new Set(allTypes.filter((type) => !registry.isHidden(type)))]; + + // @ts-expect-error must use the private constructor to use the mocked serializer + repository = new SavedObjectsRepository({ + index: '.kibana-test', + mappings, + client, + migrator, + typeRegistry: registry, + serializer, + allowedTypes, + logger, + }); + + mockGetCurrentTime.mockReturnValue(mockTimestamp); + mockGetSearchDsl.mockClear(); + }); + + describe('#resolve', () => { + afterEach(() => { + mockInternalBulkResolve.mockReset(); + }); + + it('passes arguments to the internalBulkResolve module and returns the result', async () => { + const expectedResult: SavedObjectsResolveResponse = { + saved_object: { type: 'type', id: 'id', attributes: {}, references: [] }, + outcome: 'exactMatch', + }; + mockInternalBulkResolve.mockResolvedValue({ resolved_objects: [expectedResult] }); + + await expect(repository.resolve('obj-type', 'obj-id')).resolves.toEqual(expectedResult); + expect(mockInternalBulkResolve).toHaveBeenCalledTimes(1); + expect(mockInternalBulkResolve).toHaveBeenCalledWith( + expect.objectContaining({ objects: [{ type: 'obj-type', id: 'obj-id' }] }) + ); + }); + + it('throws when internalBulkResolve result is an error', async () => { + const error = SavedObjectsErrorHelpers.decorateBadRequestError(new Error('Oh no!')); + const expectedResult: BulkResolveError = { type: 'obj-type', id: 'obj-id', error }; + mockInternalBulkResolve.mockResolvedValue({ resolved_objects: [expectedResult] }); + + await expect(repository.resolve('foo', '2')).rejects.toEqual(error); + }); + + it('throws when internalBulkResolve throws', async () => { + const error = new Error('Oh no!'); + mockInternalBulkResolve.mockRejectedValue(error); + + await expect(repository.resolve('foo', '2')).rejects.toEqual(error); + }); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/update.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/update.test.ts index dd5c51c6b433d..67076f912973a 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/update.test.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/update.test.ts @@ -45,7 +45,7 @@ import { updateSuccess, } from '../../test_helpers/repository.test.common'; -describe('SavedObjectsRepository', () => { +describe('#update', () => { let client: ReturnType; let repository: SavedObjectsRepository; let migrator: ReturnType; @@ -95,7 +95,7 @@ describe('SavedObjectsRepository', () => { mockGetCurrentTime.mockReturnValue(mockTimestamp); }); - describe('#update', () => { + describe('performUpdate', () => { const id = 'logstash-*'; const type = 'index-pattern'; const attributes = { title: 'Testing' }; diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/update_objects_spaces.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/update_objects_spaces.test.ts new file mode 100644 index 0000000000000..b88d0007bcd55 --- /dev/null +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/update_objects_spaces.test.ts @@ -0,0 +1,113 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { + pointInTimeFinderMock, + mockUpdateObjectsSpaces, + mockGetCurrentTime, + mockGetSearchDsl, +} from '../repository.test.mock'; + +import type { + SavedObjectsUpdateObjectsSpacesResponse, + SavedObjectsUpdateObjectsSpacesObject, + SavedObjectsUpdateObjectsSpacesOptions, +} from '@kbn/core-saved-objects-api-server'; +import { SavedObjectsRepository } from '../repository'; +import { loggerMock } from '@kbn/logging-mocks'; +import { SavedObjectsSerializer } from '@kbn/core-saved-objects-base-server-internal'; +import { kibanaMigratorMock } from '../../mocks'; +import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; + +import { + mockTimestamp, + mappings, + createRegistry, + createDocumentMigrator, + createSpySerializer, +} from '../../test_helpers/repository.test.common'; + +describe('#updateObjectsSpaces', () => { + let client: ReturnType; + let repository: SavedObjectsRepository; + let migrator: ReturnType; + let logger: ReturnType; + let serializer: jest.Mocked; + + const registry = createRegistry(); + const documentMigrator = createDocumentMigrator(registry); + + beforeEach(() => { + pointInTimeFinderMock.mockClear(); + client = elasticsearchClientMock.createElasticsearchClient(); + migrator = kibanaMigratorMock.create(); + documentMigrator.prepareMigrations(); + migrator.migrateDocument = jest.fn().mockImplementation(documentMigrator.migrate); + migrator.runMigrations = jest.fn().mockResolvedValue([{ status: 'skipped' }]); + logger = loggerMock.create(); + + // create a mock serializer "shim" so we can track function calls, but use the real serializer's implementation + serializer = createSpySerializer(registry); + + const allTypes = registry.getAllTypes().map((type) => type.name); + const allowedTypes = [...new Set(allTypes.filter((type) => !registry.isHidden(type)))]; + + // @ts-expect-error must use the private constructor to use the mocked serializer + repository = new SavedObjectsRepository({ + index: '.kibana-test', + mappings, + client, + migrator, + typeRegistry: registry, + serializer, + allowedTypes, + logger, + }); + + mockGetCurrentTime.mockReturnValue(mockTimestamp); + mockGetSearchDsl.mockClear(); + }); + + describe('performUpdateObjectsSpaces', () => { + afterEach(() => { + mockUpdateObjectsSpaces.mockReset(); + }); + + it('passes arguments to the updateObjectsSpaces module and returns the result', async () => { + const objects: SavedObjectsUpdateObjectsSpacesObject[] = [{ type: 'type', id: 'id' }]; + const spacesToAdd = ['to-add', 'also-to-add']; + const spacesToRemove = ['to-remove']; + const options: SavedObjectsUpdateObjectsSpacesOptions = { namespace: 'ns-1' }; + const expectedResult: SavedObjectsUpdateObjectsSpacesResponse = { + objects: [ + { + type: 'type', + id: 'id', + spaces: ['foo', 'bar'], + }, + ], + }; + mockUpdateObjectsSpaces.mockResolvedValue(expectedResult); + + await expect( + repository.updateObjectsSpaces(objects, spacesToAdd, spacesToRemove, options) + ).resolves.toEqual(expectedResult); + expect(mockUpdateObjectsSpaces).toHaveBeenCalledTimes(1); + expect(mockUpdateObjectsSpaces).toHaveBeenCalledWith( + expect.objectContaining({ objects, spacesToAdd, spacesToRemove, options }) + ); + }); + + it('returns an error from the updateObjectsSpaces module', async () => { + const expectedResult = new Error('Oh no!'); + mockUpdateObjectsSpaces.mockRejectedValue(expectedResult); + + await expect(repository.updateObjectsSpaces([], [], [])).rejects.toEqual(expectedResult); + }); + }); +}); diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.test.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.test.ts index 3547d653e3de4..27f45aaa9c084 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.test.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/repository.test.ts @@ -6,118 +6,26 @@ * Side Public License, v 1. */ -/* eslint-disable @typescript-eslint/no-shadow */ - import { pointInTimeFinderMock, - mockCollectMultiNamespaceReferences, - mockGetBulkOperationError, - mockInternalBulkResolve, - mockUpdateObjectsSpaces, mockGetCurrentTime, - mockPreflightCheckForCreate, - mockDeleteLegacyUrlAliases, mockGetSearchDsl, } from './repository.test.mock'; -import type { Payload } from '@hapi/boom'; -import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; - -import type { - SavedObjectsBaseOptions, - SavedObjectsFindOptions, - SavedObjectsUpdateObjectsSpacesResponse, - SavedObjectsDeleteByNamespaceOptions, - SavedObjectsIncrementCounterField, - SavedObjectsIncrementCounterOptions, - SavedObjectsCreatePointInTimeFinderDependencies, - SavedObjectsCreatePointInTimeFinderOptions, - SavedObjectsBulkGetObject, - SavedObjectsCreateOptions, - SavedObjectsDeleteOptions, - SavedObjectsOpenPointInTimeOptions, - SavedObjectsResolveResponse, - SavedObjectsCollectMultiNamespaceReferencesObject, - SavedObjectsCollectMultiNamespaceReferencesResponse, - SavedObjectsUpdateObjectsSpacesObject, - SavedObjectsUpdateObjectsSpacesOptions, - SavedObjectsBulkDeleteObject, - SavedObjectsBulkDeleteOptions, -} from '@kbn/core-saved-objects-api-server'; -import { - type SavedObjectsRawDoc, - type SavedObjectsRawDocSource, - type SavedObjectUnsanitizedDoc, - type SavedObject, - type SavedObjectReference, - type BulkResolveError, - MAIN_SAVED_OBJECT_INDEX, -} from '@kbn/core-saved-objects-server'; -import { ALL_NAMESPACES_STRING } from '@kbn/core-saved-objects-utils-server'; -import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server'; import { SavedObjectsRepository } from './repository'; -import { PointInTimeFinder } from './point_in_time_finder'; import { loggerMock } from '@kbn/logging-mocks'; -import { - SavedObjectsSerializer, - encodeHitVersion, - LEGACY_URL_ALIAS_TYPE, -} from '@kbn/core-saved-objects-base-server-internal'; +import { SavedObjectsSerializer } from '@kbn/core-saved-objects-base-server-internal'; import { kibanaMigratorMock } from '../mocks'; import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; -import * as esKuery from '@kbn/es-query'; import { - CUSTOM_INDEX_TYPE, - NAMESPACE_AGNOSTIC_TYPE, - MULTI_NAMESPACE_TYPE, - MULTI_NAMESPACE_ISOLATED_TYPE, - HIDDEN_TYPE, - mockVersionProps, - mockTimestampFields, mockTimestamp, mappings, - mockVersion, - bulkGetSuccess, createRegistry, createDocumentMigrator, - getMockGetResponse, - getMockMgetResponse, - type TypeIdTuple, createSpySerializer, - bulkGet, - expectErrorResult, - expectErrorInvalidType, - expectErrorNotFound, - expectError, - generateIndexPatternSearchResults, - findSuccess, - deleteSuccess, - removeReferencesToSuccess, - checkConflicts, - checkConflictsSuccess, - getSuccess, - createBadRequestErrorPayload, - createUnsupportedTypeErrorPayload, - createConflictErrorPayload, - createGenericNotFoundErrorPayload, - mockTimestampFieldsWithCreated, - getMockEsBulkDeleteResponse, - bulkDeleteSuccess, - createBulkDeleteSuccessStatus, } from '../test_helpers/repository.test.common'; -const { nodeTypes } = esKuery; - -// BEWARE: The SavedObjectClient depends on the implementation details of the SavedObjectsRepository -// so any breaking changes to this repository are considered breaking changes to the SavedObjectsClient. - -interface ExpectedErrorResult { - type: string; - id: string; - error: Record; -} - describe('SavedObjectsRepository', () => { let client: ReturnType; let repository: SavedObjectsRepository; @@ -128,22 +36,6 @@ describe('SavedObjectsRepository', () => { const registry = createRegistry(); const documentMigrator = createDocumentMigrator(registry); - const expectSuccess = ({ type, id }: { type: string; id: string }) => { - // @ts-expect-error TS is not aware of the extension - return expect.toBeDocumentWithoutError(type, id); - }; - - const expectMigrationArgs = (args: unknown, contains = true, n = 1) => { - const obj = contains ? expect.objectContaining(args) : expect.not.objectContaining(args); - expect(migrator.migrateDocument).toHaveBeenNthCalledWith( - n, - obj, - expect.objectContaining({ - allowDowngrade: expect.any(Boolean), - }) - ); - }; - beforeEach(() => { pointInTimeFinderMock.mockClear(); client = elasticsearchClientMock.createElasticsearchClient(); @@ -175,3527 +67,6 @@ describe('SavedObjectsRepository', () => { mockGetSearchDsl.mockClear(); }); - // Setup migration mock for creating an object - const mockMigrationVersion = { foo: '2.3.4' }; - const mockMigrateDocument = (doc: SavedObjectUnsanitizedDoc) => ({ - ...doc, - attributes: { - ...doc.attributes, - ...(doc.attributes?.title && { title: `${doc.attributes.title}!!` }), - }, - migrationVersion: mockMigrationVersion, - managed: doc.managed ?? false, - references: [{ name: 'search_0', type: 'search', id: '123' }], - }); - - describe('#bulkGet', () => { - const obj1: SavedObject = { - type: 'config', - id: '6.0.0-alpha1', - attributes: { title: 'Testing' }, - references: [ - { - name: 'ref_0', - type: 'test', - id: '1', - }, - ], - originId: 'some-origin-id', // only one of the results has an originId, this is intentional to test both a positive and negative case - }; - const obj2: SavedObject = { - type: 'index-pattern', - id: 'logstash-*', - attributes: { title: 'Testing' }, - references: [ - { - name: 'ref_0', - type: 'test', - id: '2', - }, - ], - }; - const namespace = 'foo-namespace'; - - const _expectClientCallArgs = ( - objects: TypeIdTuple[], - { - _index = expect.any(String), - getId = () => expect.any(String), - }: { _index?: string; getId?: (type: string, id: string) => string } - ) => { - expect(client.mget).toHaveBeenCalledWith( - expect.objectContaining({ - body: { - docs: objects.map(({ type, id }) => - expect.objectContaining({ - _index, - _id: getId(type, id), - }) - ), - }, - }), - expect.anything() - ); - }; - - describe('client calls', () => { - it(`prepends namespace to the id when providing namespace for single-namespace type`, async () => { - const getId = (type: string, id: string) => `${namespace}:${type}:${id}`; // test that the raw document ID equals this (e.g., has a namespace prefix) - await bulkGetSuccess(client, repository, registry, [obj1, obj2], { namespace }); - _expectClientCallArgs([obj1, obj2], { getId }); - }); - - it(`prepends namespace to the id when providing namespaces for single-namespace type`, async () => { - const getId = (type: string, id: string) => `${namespace}:${type}:${id}`; // test that the raw document ID equals this (e.g., has a namespace prefix) - const objects = [obj1, obj2].map((obj) => ({ ...obj, namespaces: [namespace] })); - await bulkGetSuccess(client, repository, registry, objects, { namespace: 'some-other-ns' }); - _expectClientCallArgs([obj1, obj2], { getId }); - }); - - it(`doesn't prepend namespace to the id when providing no namespace for single-namespace type`, async () => { - const getId = (type: string, id: string) => `${type}:${id}`; // test that the raw document ID equals this (e.g., does not have a namespace prefix) - await bulkGetSuccess(client, repository, registry, [obj1, obj2]); - _expectClientCallArgs([obj1, obj2], { getId }); - }); - - it(`normalizes options.namespace from 'default' to undefined`, async () => { - const getId = (type: string, id: string) => `${type}:${id}`; // test that the raw document ID equals this (e.g., does not have a namespace prefix) - await bulkGetSuccess(client, repository, registry, [obj1, obj2], { namespace: 'default' }); - _expectClientCallArgs([obj1, obj2], { getId }); - }); - - it(`doesn't prepend namespace to the id when not using single-namespace type`, async () => { - const getId = (type: string, id: string) => `${type}:${id}`; // test that the raw document ID equals this (e.g., does not have a namespace prefix) - let objects = [obj1, obj2].map((obj) => ({ ...obj, type: NAMESPACE_AGNOSTIC_TYPE })); - await bulkGetSuccess(client, repository, registry, objects, { namespace }); - _expectClientCallArgs(objects, { getId }); - - client.mget.mockClear(); - objects = [obj1, { ...obj2, namespaces: ['some-other-ns'] }].map((obj) => ({ - ...obj, - type: MULTI_NAMESPACE_ISOLATED_TYPE, - })); - await bulkGetSuccess(client, repository, registry, objects, { namespace }); - _expectClientCallArgs(objects, { getId }); - }); - }); - - describe('errors', () => { - const bulkGetError = async ( - obj: SavedObjectsBulkGetObject & { found?: boolean }, - isBulkError: boolean, - expectedErrorResult: ExpectedErrorResult - ) => { - let response; - if (isBulkError) { - // mock the bulk error for only the second object - mockGetBulkOperationError.mockReturnValueOnce(undefined); - mockGetBulkOperationError.mockReturnValueOnce(expectedErrorResult.error as Payload); - response = getMockMgetResponse(registry, [obj1, obj, obj2]); - } else { - response = getMockMgetResponse(registry, [obj1, obj2]); - } - client.mget.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise(response) - ); - - const objects = [obj1, obj, obj2]; - const result = await bulkGet(repository, objects); - expect(client.mget).toHaveBeenCalled(); - expect(result).toEqual({ - saved_objects: [expectSuccess(obj1), expectedErrorResult, expectSuccess(obj2)], - }); - }; - - it(`throws when options.namespace is '*'`, async () => { - const obj = { type: 'dashboard', id: 'three' }; - await expect( - repository.bulkGet([obj], { namespace: ALL_NAMESPACES_STRING }) - ).rejects.toThrowError(createBadRequestErrorPayload('"options.namespace" cannot be "*"')); - }); - - it(`returns error when namespaces is used with a space-agnostic object`, async () => { - const obj = { type: NAMESPACE_AGNOSTIC_TYPE, id: 'three', namespaces: [] }; - await bulkGetError( - obj, - false, - expectErrorResult( - obj, - createBadRequestErrorPayload('"namespaces" cannot be used on space-agnostic types') - ) - ); - }); - - it(`returns error when namespaces is used with a space-isolated object and does not specify a single space`, async () => { - const doTest = async (objType: string, namespaces?: string[]) => { - const obj = { type: objType, id: 'three', namespaces }; - await bulkGetError( - obj, - false, - expectErrorResult( - obj, - createBadRequestErrorPayload( - '"namespaces" can only specify a single space when used with space-isolated types' - ) - ) - ); - }; - await doTest('dashboard', ['spacex', 'spacey']); - await doTest('dashboard', ['*']); - await doTest(MULTI_NAMESPACE_ISOLATED_TYPE, ['spacex', 'spacey']); - await doTest(MULTI_NAMESPACE_ISOLATED_TYPE, ['*']); - }); - - it(`returns error when type is invalid`, async () => { - const obj: SavedObjectsBulkGetObject = { type: 'unknownType', id: 'three' }; - await bulkGetError(obj, false, expectErrorInvalidType(obj)); - }); - - it(`returns error when type is hidden`, async () => { - const obj: SavedObjectsBulkGetObject = { type: HIDDEN_TYPE, id: 'three' }; - await bulkGetError(obj, false, expectErrorInvalidType(obj)); - }); - - it(`returns error when document is not found`, async () => { - const obj: SavedObjectsBulkGetObject & { found: boolean } = { - type: 'dashboard', - id: 'three', - found: false, - }; - await bulkGetError(obj, true, expectErrorNotFound(obj)); - }); - - it(`handles missing ids gracefully`, async () => { - const obj: SavedObjectsBulkGetObject & { found: boolean } = { - type: 'dashboard', - // @ts-expect-error id is undefined - id: undefined, - found: false, - }; - await bulkGetError(obj, true, expectErrorNotFound(obj)); - }); - - it(`returns error when type is multi-namespace and the document exists, but not in this namespace`, async () => { - const obj = { - type: MULTI_NAMESPACE_ISOLATED_TYPE, - id: 'three', - namespace: 'bar-namespace', - }; - await bulkGetError(obj, true, expectErrorNotFound(obj)); - }); - }); - - describe('returns', () => { - const expectSuccessResult = ( - { type, id }: TypeIdTuple, - doc: estypes.GetGetResult - ) => ({ - type, - id, - namespaces: doc._source!.namespaces ?? ['default'], - ...(doc._source!.originId && { originId: doc._source!.originId }), - ...(doc._source!.updated_at && { updated_at: doc._source!.updated_at }), - ...(doc._source!.created_at && { created_at: doc._source!.created_at }), - version: encodeHitVersion(doc), - attributes: doc._source![type], - references: doc._source!.references || [], - coreMigrationVersion: expect.any(String), - typeMigrationVersion: expect.any(String), - managed: expect.any(Boolean), - }); - - it(`returns early for empty objects argument`, async () => { - const result = await bulkGet(repository, []); - expect(result).toEqual({ saved_objects: [] }); - expect(client.mget).not.toHaveBeenCalled(); - }); - - it(`formats the ES response`, async () => { - const response = getMockMgetResponse(registry, [obj1, obj2]); - client.mget.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise(response) - ); - const result = await bulkGet(repository, [obj1, obj2]); - expect(client.mget).toHaveBeenCalledTimes(1); - expect(result).toEqual({ - saved_objects: [ - expectSuccessResult( - obj1, - response.docs[0] as estypes.GetGetResult - ), - expectSuccessResult( - obj2, - response.docs[1] as estypes.GetGetResult - ), - ], - }); - }); - - it(`handles a mix of successful gets and errors`, async () => { - const response = getMockMgetResponse(registry, [obj1, obj2]); - client.mget.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise(response) - ); - const obj: SavedObject = { - type: 'unknownType', - id: 'three', - attributes: {}, - references: [], - }; - const result = await bulkGet(repository, [obj1, obj, obj2]); - expect(client.mget).toHaveBeenCalledTimes(1); - expect(result).toEqual({ - saved_objects: [ - expectSuccessResult( - obj1, - response.docs[0] as estypes.GetGetResult - ), - expectError(obj), - expectSuccessResult( - obj2, - response.docs[1] as estypes.GetGetResult - ), - ], - }); - }); - - it(`includes namespaces property for single-namespace and multi-namespace documents`, async () => { - const obj: SavedObject = { - type: MULTI_NAMESPACE_ISOLATED_TYPE, - id: 'three', - attributes: {}, - references: [], - }; - const { result } = await bulkGetSuccess(client, repository, registry, [obj1, obj]); - expect(result).toEqual({ - saved_objects: [ - expect.objectContaining({ namespaces: ['default'] }), - expect.objectContaining({ namespaces: expect.any(Array) }), - ], - }); - }); - - it('migrates the fetched documents', async () => { - const response = getMockMgetResponse(registry, [obj1, obj2]); - client.mget.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise(response) - ); - migrator.migrateDocument.mockReturnValue( - 'migrated' as unknown as ReturnType - ); - - await expect(bulkGet(repository, [obj1, obj2])).resolves.toHaveProperty('saved_objects', [ - 'migrated', - 'migrated', - ]); - expect(migrator.migrateDocument).toHaveBeenCalledTimes(2); - expectMigrationArgs({ id: obj1.id }, true, 1); - expectMigrationArgs({ id: obj2.id }, true, 2); - }); - }); - }); - - describe('#bulkResolve', () => { - afterEach(() => { - mockInternalBulkResolve.mockReset(); - }); - - it('passes arguments to the internalBulkResolve module and returns the expected results', async () => { - mockInternalBulkResolve.mockResolvedValue({ - resolved_objects: [ - { - saved_object: { type: 'mock', id: 'mock-object', attributes: {}, references: [] }, - outcome: 'exactMatch', - }, - { - type: 'obj-type', - id: 'obj-id-2', - error: SavedObjectsErrorHelpers.createGenericNotFoundError('obj-type', 'obj-id-2'), - }, - ], - }); - - const objects = [ - { type: 'obj-type', id: 'obj-id-1' }, - { type: 'obj-type', id: 'obj-id-2' }, - ]; - await expect(repository.bulkResolve(objects)).resolves.toEqual({ - resolved_objects: [ - { - saved_object: { type: 'mock', id: 'mock-object', attributes: {}, references: [] }, - outcome: 'exactMatch', - }, - { - saved_object: { - type: 'obj-type', - id: 'obj-id-2', - error: { - error: 'Not Found', - message: 'Saved object [obj-type/obj-id-2] not found', - statusCode: 404, - }, - }, - outcome: 'exactMatch', - }, - ], - }); - expect(mockInternalBulkResolve).toHaveBeenCalledTimes(1); - expect(mockInternalBulkResolve).toHaveBeenCalledWith(expect.objectContaining({ objects })); - }); - - it('throws when internalBulkResolve throws', async () => { - const error = new Error('Oh no!'); - mockInternalBulkResolve.mockRejectedValue(error); - - await expect(repository.resolve('some-type', 'some-id')).rejects.toEqual(error); - }); - }); - - describe('#bulkDelete', () => { - const obj1: SavedObjectsBulkDeleteObject = { - type: 'config', - id: '6.0.0-alpha1', - }; - const obj2: SavedObjectsBulkDeleteObject = { - type: 'index-pattern', - id: 'logstash-*', - }; - - const namespace = 'foo-namespace'; - - const createNamespaceAwareGetId = (type: string, id: string) => - `${registry.isSingleNamespace(type) && namespace ? `${namespace}:` : ''}${type}:${id}`; - - // bulk delete calls only has one object for each source -- the action - const expectClientCallBulkDeleteArgsAction = ( - objects: TypeIdTuple[], - { - method, - _index = expect.any(String), - getId = () => expect.any(String), - overrides = {}, - }: { - method: string; - _index?: string; - getId?: (type: string, id: string) => string; - overrides?: Record; - } - ) => { - const body = []; - for (const { type, id } of objects) { - body.push({ - [method]: { - _index, - _id: getId(type, id), - ...overrides, - }, - }); - } - - expect(client.bulk).toHaveBeenCalledWith( - expect.objectContaining({ body }), - expect.anything() - ); - }; - - const createBulkDeleteFailStatus = ({ - type, - id, - error, - }: { - type: string; - id: string; - error?: ExpectedErrorResult['error']; - }) => ({ - type, - id, - success: false, - error: error ?? SavedObjectsErrorHelpers.createBadRequestError(), - }); - - // mocks a combination of success, error results for hidden and unknown object object types. - const repositoryBulkDeleteError = async ( - obj: SavedObjectsBulkDeleteObject, - isBulkError: boolean, - expectedErrorResult: ExpectedErrorResult - ) => { - const objects = [obj1, obj, obj2]; - const mockedBulkDeleteResponse = getMockEsBulkDeleteResponse(registry, objects); - if (isBulkError) { - mockGetBulkOperationError.mockReturnValueOnce(undefined); - mockGetBulkOperationError.mockReturnValueOnce(expectedErrorResult.error as Payload); - } - client.bulk.mockResponseOnce(mockedBulkDeleteResponse); - - const result = await repository.bulkDelete(objects); - expect(client.bulk).toHaveBeenCalled(); - expect(result).toEqual({ - statuses: [ - createBulkDeleteSuccessStatus(obj1), - createBulkDeleteFailStatus({ ...obj, error: expectedErrorResult.error }), - createBulkDeleteSuccessStatus(obj2), - ], - }); - }; - - const expectClientCallArgsAction = ( - objects: TypeIdTuple[], - { - method, - _index = expect.any(String), - getId = () => expect.any(String), - overrides = {}, - }: { - method: string; - _index?: string; - getId?: (type: string, id: string) => string; - overrides?: Record; - } - ) => { - const body = []; - for (const { type, id } of objects) { - body.push({ - [method]: { - _index, - _id: getId(type, id), - ...overrides, - }, - }); - } - expect(client.bulk).toHaveBeenCalledWith( - expect.objectContaining({ body }), - expect.anything() - ); - }; - - const bulkDeleteMultiNamespaceError = async ( - [obj1, _obj, obj2]: SavedObjectsBulkDeleteObject[], - options: SavedObjectsBulkDeleteOptions | undefined, - mgetResponse: estypes.MgetResponse, - mgetOptions?: { statusCode?: number } - ) => { - const getId = (type: string, id: string) => `${options?.namespace}:${type}:${id}`; - // mock the response for the not found doc - client.mget.mockResponseOnce(mgetResponse, { statusCode: mgetOptions?.statusCode }); - // get a mocked response for the valid docs - const bulkResponse = getMockEsBulkDeleteResponse(registry, [obj1, obj2], { namespace }); - client.bulk.mockResponseOnce(bulkResponse); - - const result = await repository.bulkDelete([obj1, _obj, obj2], options); - expect(client.bulk).toHaveBeenCalledTimes(1); - expect(client.mget).toHaveBeenCalledTimes(1); - - expectClientCallArgsAction([obj1, obj2], { method: 'delete', getId }); - expect(result).toEqual({ - statuses: [ - createBulkDeleteSuccessStatus(obj1), - { ...expectErrorNotFound(_obj), success: false }, - createBulkDeleteSuccessStatus(obj2), - ], - }); - }; - - beforeEach(() => { - mockDeleteLegacyUrlAliases.mockClear(); - mockDeleteLegacyUrlAliases.mockResolvedValue(); - }); - - describe('client calls', () => { - it(`should use the ES bulk action by default`, async () => { - await bulkDeleteSuccess(client, repository, registry, [obj1, obj2]); - expect(client.bulk).toHaveBeenCalled(); - }); - - it(`should use the ES mget action before bulk action for any types that are multi-namespace`, async () => { - const objects = [obj1, { ...obj2, type: MULTI_NAMESPACE_ISOLATED_TYPE }]; - await bulkDeleteSuccess(client, repository, registry, objects); - expect(client.bulk).toHaveBeenCalled(); - expect(client.mget).toHaveBeenCalled(); - - const docs = [ - expect.objectContaining({ _id: `${MULTI_NAMESPACE_ISOLATED_TYPE}:${obj2.id}` }), - ]; - expect(client.mget).toHaveBeenCalledWith( - expect.objectContaining({ body: { docs } }), - expect.anything() - ); - }); - - it(`should not use the ES bulk action when there are no valid documents to delete`, async () => { - const objects = [obj1, obj2].map((x) => ({ ...x, type: 'unknownType' })); - await repository.bulkDelete(objects); - expect(client.bulk).toHaveBeenCalledTimes(0); - }); - - it(`formats the ES request`, async () => { - const getId = createNamespaceAwareGetId; - await bulkDeleteSuccess(client, repository, registry, [obj1, obj2], { namespace }); - expectClientCallBulkDeleteArgsAction([obj1, obj2], { method: 'delete', getId }); - }); - - it(`formats the ES request for any types that are multi-namespace`, async () => { - const _obj2 = { ...obj2, type: MULTI_NAMESPACE_ISOLATED_TYPE }; - const getId = createNamespaceAwareGetId; - await bulkDeleteSuccess(client, repository, registry, [obj1, _obj2], { namespace }); - expectClientCallBulkDeleteArgsAction([obj1, _obj2], { method: 'delete', getId }); - }); - - it(`defaults to a refresh setting of wait_for`, async () => { - await bulkDeleteSuccess(client, repository, registry, [obj1, obj2]); - expect(client.bulk).toHaveBeenCalledWith( - expect.objectContaining({ refresh: 'wait_for' }), - expect.anything() - ); - }); - - it(`does not include the version of the existing document when not using a multi-namespace type`, async () => { - const objects = [obj1, { ...obj2, type: NAMESPACE_AGNOSTIC_TYPE }]; - await bulkDeleteSuccess(client, repository, registry, objects); - expectClientCallBulkDeleteArgsAction(objects, { method: 'delete' }); - }); - - it(`prepends namespace to the id when providing namespace for single-namespace type`, async () => { - const getId = createNamespaceAwareGetId; - await bulkDeleteSuccess(client, repository, registry, [obj1, obj2], { namespace }); - expectClientCallBulkDeleteArgsAction([obj1, obj2], { method: 'delete', getId }); - }); - - it(`doesn't prepend namespace to the id when providing no namespace for single-namespace type`, async () => { - const getId = (type: string, id: string) => `${type}:${id}`; - await bulkDeleteSuccess(client, repository, registry, [obj1, obj2]); - expectClientCallBulkDeleteArgsAction([obj1, obj2], { method: 'delete', getId }); - }); - - it(`normalizes options.namespace from 'default' to undefined`, async () => { - const getId = (type: string, id: string) => `${type}:${id}`; - await bulkDeleteSuccess(client, repository, registry, [obj1, obj2], { - namespace: 'default', - }); - expectClientCallBulkDeleteArgsAction([obj1, obj2], { method: 'delete', getId }); - }); - - it(`doesn't prepend namespace to the id when not using single-namespace type`, async () => { - const getId = (type: string, id: string) => `${type}:${id}`; // not expecting namespace prefix; - const _obj1 = { ...obj1, type: NAMESPACE_AGNOSTIC_TYPE }; - const _obj2 = { ...obj2, type: MULTI_NAMESPACE_ISOLATED_TYPE }; - - await bulkDeleteSuccess(client, repository, registry, [_obj1, _obj2], { namespace }); - expectClientCallBulkDeleteArgsAction([_obj1, _obj2], { method: 'delete', getId }); - }); - }); - - describe('legacy URL aliases', () => { - it(`doesn't delete legacy URL aliases for single-namespace object types`, async () => { - await bulkDeleteSuccess(client, repository, registry, [obj1, obj2]); - expect(mockDeleteLegacyUrlAliases).not.toHaveBeenCalled(); - }); - - it(`deletes legacy URL aliases for multi-namespace object types (all spaces)`, async () => { - const testObject = { ...obj1, type: MULTI_NAMESPACE_TYPE }; - const internalOptions = { - mockMGetResponseObjects: [ - { - ...testObject, - initialNamespaces: [ALL_NAMESPACES_STRING], - }, - ], - }; - await bulkDeleteSuccess( - client, - repository, - registry, - [testObject], - { namespace, force: true }, - internalOptions - ); - expect(mockDeleteLegacyUrlAliases).toHaveBeenCalledWith( - expect.objectContaining({ - type: MULTI_NAMESPACE_TYPE, - id: testObject.id, - namespaces: [], - deleteBehavior: 'exclusive', - }) - ); - }); - - it(`deletes legacy URL aliases for multi-namespace object types (specific space)`, async () => { - const testObject = { ...obj1, type: MULTI_NAMESPACE_TYPE }; - const internalOptions = { - mockMGetResponseObjects: [ - { - ...testObject, - initialNamespaces: [namespace], - }, - ], - }; - // specifically test against the current namespace - await bulkDeleteSuccess( - client, - repository, - registry, - [testObject], - { namespace }, - internalOptions - ); - expect(mockDeleteLegacyUrlAliases).toHaveBeenCalledWith( - expect.objectContaining({ - type: MULTI_NAMESPACE_TYPE, - id: testObject.id, - namespaces: [namespace], - deleteBehavior: 'inclusive', - }) - ); - }); - - it(`deletes legacy URL aliases for multi-namespace object types shared to many specific spaces`, async () => { - const testObject = { ...obj1, type: MULTI_NAMESPACE_TYPE }; - const initialTestObjectNamespaces = [namespace, 'bar-namespace']; - const internalOptions = { - mockMGetResponseObjects: [ - { - ...testObject, - initialNamespaces: initialTestObjectNamespaces, - }, - ], - }; - // specifically test against named spaces ('*' is handled specifically, this assures we also take care of named spaces) - await bulkDeleteSuccess( - client, - repository, - registry, - [testObject], - { namespace, force: true }, - internalOptions - ); - expect(mockDeleteLegacyUrlAliases).toHaveBeenCalledWith( - expect.objectContaining({ - type: MULTI_NAMESPACE_TYPE, - id: testObject.id, - namespaces: initialTestObjectNamespaces, - deleteBehavior: 'inclusive', - }) - ); - }); - - it(`logs a message when deleteLegacyUrlAliases returns an error`, async () => { - const testObject = { type: MULTI_NAMESPACE_ISOLATED_TYPE, id: obj1.id }; - - client.mget.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise( - getMockMgetResponse(registry, [testObject], namespace) - ) - ); - const mockedBulkResponse = getMockEsBulkDeleteResponse(registry, [testObject], { - namespace, - }); - client.bulk.mockResolvedValueOnce(mockedBulkResponse); - - mockDeleteLegacyUrlAliases.mockRejectedValueOnce(new Error('Oh no!')); - - await repository.bulkDelete([testObject], { namespace }); - - expect(client.mget).toHaveBeenCalledTimes(1); - expect(logger.error).toHaveBeenCalledTimes(1); - expect(logger.error).toHaveBeenCalledWith( - 'Unable to delete aliases when deleting an object: Oh no!' - ); - }); - }); - - describe('errors', () => { - it(`throws an error when options.namespace is '*'`, async () => { - await expect( - repository.bulkDelete([obj1], { namespace: ALL_NAMESPACES_STRING }) - ).rejects.toThrowError( - SavedObjectsErrorHelpers.createBadRequestError('"options.namespace" cannot be "*"') - ); - }); - - it(`throws an error when client bulk response is not defined`, async () => { - client.mget.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise( - getMockMgetResponse(registry, [obj1], namespace) - ) - ); - const mockedBulkResponse = undefined; - // we have to cast here to test the assumption we always get a response. - client.bulk.mockResponseOnce(mockedBulkResponse as unknown as estypes.BulkResponse); - await expect(repository.bulkDelete([obj1], { namespace })).rejects.toThrowError( - 'Unexpected error in bulkDelete saved objects: bulkDeleteResponse is undefined' - ); - }); - - it(`returns an error for the object when the object's type is invalid`, async () => { - const unknownObjType = { ...obj1, type: 'unknownType' }; - await repositoryBulkDeleteError( - unknownObjType, - false, - expectErrorInvalidType(unknownObjType) - ); - }); - - it(`returns an error for an object when the object's type is hidden`, async () => { - const hiddenObject = { ...obj1, type: HIDDEN_TYPE }; - await repositoryBulkDeleteError(hiddenObject, false, expectErrorInvalidType(hiddenObject)); - }); - - it(`returns an error when ES is unable to find the document during mget`, async () => { - const notFoundObj = { ...obj1, type: MULTI_NAMESPACE_ISOLATED_TYPE, found: false }; - const mgetResponse = getMockMgetResponse(registry, [notFoundObj], namespace); - await bulkDeleteMultiNamespaceError([obj1, notFoundObj, obj2], { namespace }, mgetResponse); - }); - - it(`returns an error when ES is unable to find the index during mget`, async () => { - const notFoundObj = { ...obj1, type: MULTI_NAMESPACE_ISOLATED_TYPE, found: false }; - await bulkDeleteMultiNamespaceError( - [obj1, notFoundObj, obj2], - { namespace }, - {} as estypes.MgetResponse, - { - statusCode: 404, - } - ); - }); - - it(`returns an error when the type is multi-namespace and the document exists, but not in this namespace`, async () => { - const obj = { - type: MULTI_NAMESPACE_ISOLATED_TYPE, - id: 'three', - namespace: 'bar-namespace', - }; - const mgetResponse = getMockMgetResponse(registry, [obj], namespace); - await bulkDeleteMultiNamespaceError([obj1, obj, obj2], { namespace }, mgetResponse); - }); - - it(`returns an error when the type is multi-namespace and the document has multiple namespaces and the force option is not enabled`, async () => { - const testObject = { ...obj1, type: MULTI_NAMESPACE_TYPE }; - const internalOptions = { - mockMGetResponseObjects: [ - { - ...testObject, - initialNamespaces: [namespace, 'bar-namespace'], - }, - ], - }; - const result = await bulkDeleteSuccess( - client, - repository, - registry, - [testObject], - { namespace }, - internalOptions - ); - expect(result.statuses[0]).toStrictEqual( - createBulkDeleteFailStatus({ - ...testObject, - error: createBadRequestErrorPayload( - 'Unable to delete saved object that exists in multiple namespaces, use the `force` option to delete it anyway' - ), - }) - ); - }); - - it(`returns an error when the type is multi-namespace and the document has all namespaces and the force option is not enabled`, async () => { - const testObject = { ...obj1, type: ALL_NAMESPACES_STRING }; - const internalOptions = { - mockMGetResponseObjects: [ - { - ...testObject, - initialNamespaces: [namespace, 'bar-namespace'], - }, - ], - }; - const result = await bulkDeleteSuccess( - client, - repository, - registry, - [testObject], - { namespace }, - internalOptions - ); - expect(result.statuses[0]).toStrictEqual( - createBulkDeleteFailStatus({ - ...testObject, - error: createBadRequestErrorPayload("Unsupported saved object type: '*'"), - }) - ); - }); - }); - - describe('returns', () => { - it(`returns early for empty objects argument`, async () => { - await repository.bulkDelete([], { namespace }); - expect(client.bulk).toHaveBeenCalledTimes(0); - }); - - it(`formats the ES response`, async () => { - const response = await bulkDeleteSuccess(client, repository, registry, [obj1, obj2], { - namespace, - }); - expect(response).toEqual({ - statuses: [obj1, obj2].map(createBulkDeleteSuccessStatus), - }); - }); - - it(`handles a mix of successful deletes and errors`, async () => { - const notFoundObj = { ...obj1, type: MULTI_NAMESPACE_ISOLATED_TYPE, found: false }; - await bulkDeleteMultiNamespaceError( - [obj1, notFoundObj, obj2], - { namespace }, - {} as estypes.MgetResponse, - { statusCode: 404 } - ); - }); - }); - }); - - describe('#checkConflicts', () => { - const obj1 = { type: 'dashboard', id: 'one' }; - const obj2 = { type: 'dashboard', id: 'two' }; - const obj3 = { type: MULTI_NAMESPACE_ISOLATED_TYPE, id: 'three' }; - const obj4 = { type: MULTI_NAMESPACE_ISOLATED_TYPE, id: 'four' }; - const obj5 = { type: MULTI_NAMESPACE_ISOLATED_TYPE, id: 'five' }; - const obj6 = { type: NAMESPACE_AGNOSTIC_TYPE, id: 'six' }; - const obj7 = { type: NAMESPACE_AGNOSTIC_TYPE, id: 'seven' }; - const namespace = 'foo-namespace'; - - const _expectClientCallArgs = ( - objects: TypeIdTuple[], - { - _index = expect.any(String), - getId = () => expect.any(String), - }: { _index?: string; getId?: (type: string, id: string) => string } - ) => { - expect(client.mget).toHaveBeenCalledWith( - expect.objectContaining({ - body: { - docs: objects.map(({ type, id }) => - expect.objectContaining({ - _index, - _id: getId(type, id), - }) - ), - }, - }), - expect.anything() - ); - }; - - describe('client calls', () => { - it(`doesn't make a cluster call if the objects array is empty`, async () => { - await checkConflicts(repository, []); - expect(client.mget).not.toHaveBeenCalled(); - }); - - it(`prepends namespace to the id when providing namespace for single-namespace type`, async () => { - const getId = (type: string, id: string) => `${namespace}:${type}:${id}`; // test that the raw document ID equals this (e.g., has a namespace prefix) - await checkConflictsSuccess(client, repository, registry, [obj1, obj2], { namespace }); - _expectClientCallArgs([obj1, obj2], { getId }); - }); - - it(`doesn't prepend namespace to the id when providing no namespace for single-namespace type`, async () => { - const getId = (type: string, id: string) => `${type}:${id}`; // test that the raw document ID equals this (e.g., does not have a namespace prefix) - await checkConflictsSuccess(client, repository, registry, [obj1, obj2]); - _expectClientCallArgs([obj1, obj2], { getId }); - }); - - it(`normalizes options.namespace from 'default' to undefined`, async () => { - const getId = (type: string, id: string) => `${type}:${id}`; // test that the raw document ID equals this (e.g., does not have a namespace prefix) - await checkConflictsSuccess(client, repository, registry, [obj1, obj2], { - namespace: 'default', - }); - _expectClientCallArgs([obj1, obj2], { getId }); - }); - - it(`doesn't prepend namespace to the id when not using single-namespace type`, async () => { - const getId = (type: string, id: string) => `${type}:${id}`; // test that the raw document ID equals this (e.g., does not have a namespace prefix) - // obj3 is multi-namespace, and obj6 is namespace-agnostic - await checkConflictsSuccess(client, repository, registry, [obj3, obj6], { namespace }); - _expectClientCallArgs([obj3, obj6], { getId }); - }); - }); - - describe('errors', () => { - it(`throws when options.namespace is '*'`, async () => { - await expect( - repository.checkConflicts([obj1], { namespace: ALL_NAMESPACES_STRING }) - ).rejects.toThrowError(createBadRequestErrorPayload('"options.namespace" cannot be "*"')); - }); - }); - - describe('returns', () => { - it(`expected results`, async () => { - const unknownTypeObj = { type: 'unknownType', id: 'three' }; - const hiddenTypeObj = { type: HIDDEN_TYPE, id: 'three' }; - const objects = [unknownTypeObj, hiddenTypeObj, obj1, obj2, obj3, obj4, obj5, obj6, obj7]; - const response = { - docs: [ - getMockGetResponse(registry, obj1), - { found: false }, - getMockGetResponse(registry, obj3), - getMockGetResponse(registry, { ...obj4, namespace: 'bar-namespace' }), - { found: false }, - getMockGetResponse(registry, obj6), - { found: false }, - ], - } as estypes.MgetResponse; - client.mget.mockResolvedValue( - elasticsearchClientMock.createSuccessTransportRequestPromise(response) - ); - - const result = await checkConflicts(repository, objects); - expect(client.mget).toHaveBeenCalledTimes(1); - expect(result).toEqual({ - errors: [ - { ...unknownTypeObj, error: createUnsupportedTypeErrorPayload(unknownTypeObj.type) }, - { ...hiddenTypeObj, error: createUnsupportedTypeErrorPayload(hiddenTypeObj.type) }, - { ...obj1, error: createConflictErrorPayload(obj1.type, obj1.id) }, - // obj2 was not found so it does not result in a conflict error - { ...obj3, error: createConflictErrorPayload(obj3.type, obj3.id) }, - { - ...obj4, - error: { - ...createConflictErrorPayload(obj4.type, obj4.id), - metadata: { isNotOverwritable: true }, - }, - }, - // obj5 was not found so it does not result in a conflict error - { ...obj6, error: createConflictErrorPayload(obj6.type, obj6.id) }, - // obj7 was not found so it does not result in a conflict error - ], - }); - }); - }); - }); - - describe('#create', () => { - beforeEach(() => { - mockPreflightCheckForCreate.mockReset(); - mockPreflightCheckForCreate.mockImplementation(({ objects }) => { - return Promise.resolve(objects.map(({ type, id }) => ({ type, id }))); // respond with no errors by default - }); - client.create.mockResponseImplementation((params) => { - return { - body: { - _id: params.id, - ...mockVersionProps, - } as estypes.CreateResponse, - }; - }); - }); - - const type = 'index-pattern'; - const attributes = { title: 'Logstash' }; - const id = 'logstash-*'; - const namespace = 'foo-namespace'; - const references = [ - { - name: 'ref_0', - type: 'test', - id: '123', - }, - ]; - - const createSuccess = async ( - type: string, - attributes: T, - options?: SavedObjectsCreateOptions - ) => { - return await repository.create(type, attributes, options); - }; - - describe('client calls', () => { - it(`should use the ES index action if ID is not defined`, async () => { - await createSuccess(type, attributes, { overwrite: true }); - expect(mockPreflightCheckForCreate).not.toHaveBeenCalled(); - expect(client.index).toHaveBeenCalled(); - }); - - it(`should use the ES index action if ID is not defined and a doc has managed=true`, async () => { - await createSuccess(type, attributes, { overwrite: true, managed: true }); - expect(mockPreflightCheckForCreate).not.toHaveBeenCalled(); - expect(client.index).toHaveBeenCalled(); - }); - - it(`should use the ES index action if ID is not defined and a doc has managed=false`, async () => { - await createSuccess(type, attributes, { overwrite: true, managed: false }); - expect(mockPreflightCheckForCreate).not.toHaveBeenCalled(); - expect(client.index).toHaveBeenCalled(); - }); - - it(`should use the ES create action if ID is not defined and overwrite=false`, async () => { - await createSuccess(type, attributes); - expect(mockPreflightCheckForCreate).not.toHaveBeenCalled(); - expect(client.create).toHaveBeenCalled(); - }); - - it(`should use the ES create action if ID is not defined, overwrite=false and a doc has managed=true`, async () => { - await createSuccess(type, attributes, { managed: true }); - expect(mockPreflightCheckForCreate).not.toHaveBeenCalled(); - expect(client.create).toHaveBeenCalled(); - }); - - it(`should use the ES create action if ID is not defined, overwrite=false and a doc has managed=false`, async () => { - await createSuccess(type, attributes, { managed: false }); - expect(mockPreflightCheckForCreate).not.toHaveBeenCalled(); - expect(client.create).toHaveBeenCalled(); - }); - - it(`should use the ES index with version if ID and version are defined and overwrite=true`, async () => { - await createSuccess(type, attributes, { id, overwrite: true, version: mockVersion }); - expect(mockPreflightCheckForCreate).not.toHaveBeenCalled(); - expect(client.index).toHaveBeenCalled(); - expect(client.index.mock.calls[0][0]).toMatchObject({ - if_seq_no: mockVersionProps._seq_no, - if_primary_term: mockVersionProps._primary_term, - }); - }); - - it(`should use the ES create action if ID is defined and overwrite=false`, async () => { - await createSuccess(type, attributes, { id }); - expect(mockPreflightCheckForCreate).not.toHaveBeenCalled(); - expect(client.create).toHaveBeenCalled(); - }); - - it(`should use the preflightCheckForCreate action then create action if type is multi-namespace, ID is defined, and overwrite=false`, async () => { - await createSuccess(MULTI_NAMESPACE_TYPE, attributes, { id }); - expect(mockPreflightCheckForCreate).toHaveBeenCalled(); - expect(mockPreflightCheckForCreate).toHaveBeenCalledWith( - expect.objectContaining({ - objects: [ - { type: MULTI_NAMESPACE_TYPE, id, overwrite: false, namespaces: ['default'] }, - ], - }) - ); - expect(client.create).toHaveBeenCalled(); - }); - - it(`should use the preflightCheckForCreate action then index action if type is multi-namespace, ID is defined, and overwrite=true`, async () => { - await createSuccess(MULTI_NAMESPACE_ISOLATED_TYPE, attributes, { id, overwrite: true }); - expect(mockPreflightCheckForCreate).toHaveBeenCalled(); - expect(mockPreflightCheckForCreate).toHaveBeenCalledWith( - expect.objectContaining({ - objects: [ - { type: MULTI_NAMESPACE_ISOLATED_TYPE, id, overwrite: true, namespaces: ['default'] }, - ], - }) - ); - expect(client.index).toHaveBeenCalled(); - }); - - it(`defaults to empty references array`, async () => { - await createSuccess(type, attributes, { id }); - expect( - (client.create.mock.calls[0][0] as estypes.CreateRequest).body! - .references - ).toEqual([]); - }); - - it(`accepts custom references array`, async () => { - const test = async (references: SavedObjectReference[]) => { - await createSuccess(type, attributes, { id, references }); - expect( - (client.create.mock.calls[0][0] as estypes.CreateRequest) - .body!.references - ).toEqual(references); - client.create.mockClear(); - }; - await test(references); - await test([{ type: 'type', id: 'id', name: 'some ref' }]); - await test([]); - }); - - it(`doesn't accept custom references if not an array`, async () => { - const test = async (references: unknown) => { - // @ts-expect-error references is unknown - await createSuccess(type, attributes, { id, references }); - expect( - (client.create.mock.calls[0][0] as estypes.CreateRequest) - .body!.references - ).not.toBeDefined(); - client.create.mockClear(); - }; - await test('string'); - await test(123); - await test(true); - await test(null); - }); - - describe('originId', () => { - for (const objType of [type, NAMESPACE_AGNOSTIC_TYPE]) { - it(`throws an error if originId is set for non-multi-namespace type`, async () => { - await expect( - repository.create(objType, attributes, { originId: 'some-originId' }) - ).rejects.toThrowError( - createBadRequestErrorPayload( - '"originId" can only be set for multi-namespace object types' - ) - ); - }); - } - - for (const objType of [MULTI_NAMESPACE_TYPE, MULTI_NAMESPACE_ISOLATED_TYPE]) { - it(`${objType} defaults to no originId`, async () => { - await createSuccess(objType, attributes, { id }); - expect(client.create).toHaveBeenCalledWith( - expect.objectContaining({ - body: expect.not.objectContaining({ originId: expect.anything() }), - }), - expect.anything() - ); - }); - - describe(`${objType} with existing originId`, () => { - beforeEach(() => { - mockPreflightCheckForCreate.mockImplementation(({ objects }) => { - const existingDocument = { - _source: { originId: 'existing-originId' }, - } as SavedObjectsRawDoc; - return Promise.resolve( - objects.map(({ type, id }) => ({ type, id, existingDocument })) - ); - }); - }); - - it(`accepts custom originId for multi-namespace type`, async () => { - // The preflight result has `existing-originId`, but that is discarded - await createSuccess(objType, attributes, { id, originId: 'some-originId' }); - expect(client.create).toHaveBeenCalledWith( - expect.objectContaining({ - body: expect.objectContaining({ originId: 'some-originId' }), - }), - expect.anything() - ); - }); - - it(`accepts undefined originId`, async () => { - // The preflight result has `existing-originId`, but that is discarded - await createSuccess(objType, attributes, { id, originId: undefined }); - expect(client.create).toHaveBeenCalledWith( - expect.objectContaining({ - body: expect.not.objectContaining({ originId: expect.anything() }), - }), - expect.anything() - ); - }); - - it(`preserves existing originId if originId option is not set`, async () => { - await createSuccess(objType, attributes, { id }); - expect(client.create).toHaveBeenCalledWith( - expect.objectContaining({ - body: expect.objectContaining({ originId: 'existing-originId' }), - }), - expect.anything() - ); - }); - }); - } - }); - - it(`defaults to a refresh setting of wait_for`, async () => { - await createSuccess(type, attributes); - expect(client.create).toHaveBeenCalledWith( - expect.objectContaining({ refresh: 'wait_for' }), - expect.anything() - ); - }); - - it(`should use default index`, async () => { - await createSuccess(type, attributes, { id }); - expect(client.create).toHaveBeenCalledWith( - expect.objectContaining({ index: '.kibana-test_8.0.0-testing' }), - expect.anything() - ); - }); - - it(`should use custom index`, async () => { - await createSuccess(CUSTOM_INDEX_TYPE, attributes, { id }); - expect(client.create).toHaveBeenCalledWith( - expect.objectContaining({ index: 'custom_8.0.0-testing' }), - expect.anything() - ); - }); - - it(`self-generates an id if none is provided`, async () => { - await createSuccess(type, attributes); - expect(client.create).toHaveBeenNthCalledWith( - 1, - expect.objectContaining({ - id: expect.objectContaining(/index-pattern:[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}/), - }), - expect.anything() - ); - await createSuccess(type, attributes, { id: '' }); - expect(client.create).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - id: expect.objectContaining(/index-pattern:[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}/), - }), - expect.anything() - ); - }); - - it(`prepends namespace to the id and adds namespace to the body when providing namespace for single-namespace type`, async () => { - await createSuccess(type, attributes, { id, namespace }); - expect(client.create).toHaveBeenCalledWith( - expect.objectContaining({ - id: `${namespace}:${type}:${id}`, - body: expect.objectContaining({ namespace }), - }), - expect.anything() - ); - }); - - it(`doesn't prepend namespace to the id or add namespace to the body when providing no namespace for single-namespace type`, async () => { - await createSuccess(type, attributes, { id }); - expect(client.create).toHaveBeenCalledWith( - expect.objectContaining({ - id: `${type}:${id}`, - body: expect.not.objectContaining({ namespace: expect.anything() }), - }), - expect.anything() - ); - }); - - it(`normalizes options.namespace from 'default' to undefined`, async () => { - await createSuccess(type, attributes, { id, namespace: 'default' }); - expect(client.create).toHaveBeenCalledWith( - expect.objectContaining({ - id: `${type}:${id}`, - body: expect.not.objectContaining({ namespace: expect.anything() }), - }), - expect.anything() - ); - }); - - it(`doesn't prepend namespace to the id and adds namespaces to body when using multi-namespace type`, async () => { - // first object does not have an existing document to overwrite - await createSuccess(MULTI_NAMESPACE_TYPE, attributes, { id, namespace }); - mockPreflightCheckForCreate.mockResolvedValueOnce([ - { - type: MULTI_NAMESPACE_TYPE, - id, - existingDocument: { - _id: id, - _source: { type: MULTI_NAMESPACE_TYPE, namespaces: ['*'] }, - }, // second object does have an existing document to overwrite - }, - ]); - await createSuccess(MULTI_NAMESPACE_ISOLATED_TYPE, attributes, { - id, - namespace, - overwrite: true, - }); - - expect(mockPreflightCheckForCreate).toHaveBeenCalledTimes(2); - expect(mockPreflightCheckForCreate).toHaveBeenNthCalledWith( - 1, - expect.objectContaining({ - objects: [ - { type: MULTI_NAMESPACE_TYPE, id, overwrite: false, namespaces: [namespace] }, - ], - }) - ); - expect(mockPreflightCheckForCreate).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - objects: [ - { type: MULTI_NAMESPACE_ISOLATED_TYPE, id, overwrite: true, namespaces: [namespace] }, - ], - }) - ); - - expect(client.create).toHaveBeenCalledTimes(1); - expect(client.create).toHaveBeenCalledWith( - expect.objectContaining({ - id: `${MULTI_NAMESPACE_TYPE}:${id}`, - body: expect.objectContaining({ namespaces: [namespace] }), - }), - expect.anything() - ); - expect(client.index).toHaveBeenCalledTimes(1); - expect(client.index).toHaveBeenCalledWith( - expect.objectContaining({ - id: `${MULTI_NAMESPACE_ISOLATED_TYPE}:${id}`, - body: expect.objectContaining({ namespaces: ['*'] }), - }), - expect.anything() - ); - }); - - it(`adds initialNamespaces instead of namespace`, async () => { - const ns2 = 'bar-namespace'; - const ns3 = 'baz-namespace'; - // first object does not get passed in to preflightCheckForCreate at all - await repository.create('dashboard', attributes, { - id, - namespace, - initialNamespaces: [ns2], - }); - // second object does not have an existing document to overwrite - await repository.create(MULTI_NAMESPACE_TYPE, attributes, { - id, - namespace, - initialNamespaces: [ns2, ns3], - }); - mockPreflightCheckForCreate.mockResolvedValueOnce([ - { - type: MULTI_NAMESPACE_ISOLATED_TYPE, - id, - existingDocument: { - _id: id, - _source: { type: MULTI_NAMESPACE_ISOLATED_TYPE, namespaces: ['something-else'] }, - }, // third object does have an existing document to overwrite - }, - ]); - await repository.create(MULTI_NAMESPACE_ISOLATED_TYPE, attributes, { - id, - namespace, - initialNamespaces: [ns2], - overwrite: true, - }); - - expect(mockPreflightCheckForCreate).toHaveBeenCalledTimes(2); - expect(mockPreflightCheckForCreate).toHaveBeenNthCalledWith( - 1, - expect.objectContaining({ - objects: [{ type: MULTI_NAMESPACE_TYPE, id, overwrite: false, namespaces: [ns2, ns3] }], - }) - ); - expect(mockPreflightCheckForCreate).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - objects: [ - { type: MULTI_NAMESPACE_ISOLATED_TYPE, id, overwrite: true, namespaces: [ns2] }, - ], - }) - ); - - expect(client.create).toHaveBeenCalledTimes(2); - expect(client.create).toHaveBeenNthCalledWith( - 1, - expect.objectContaining({ - id: `${ns2}:dashboard:${id}`, - body: expect.objectContaining({ namespace: ns2 }), - }), - expect.anything() - ); - expect(client.create).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - id: `${MULTI_NAMESPACE_TYPE}:${id}`, - body: expect.objectContaining({ namespaces: [ns2, ns3] }), - }), - expect.anything() - ); - expect(client.index).toHaveBeenCalledTimes(1); - expect(client.index).toHaveBeenCalledWith( - expect.objectContaining({ - id: `${MULTI_NAMESPACE_ISOLATED_TYPE}:${id}`, - body: expect.objectContaining({ namespaces: [ns2] }), - }), - expect.anything() - ); - }); - - it(`normalizes initialNamespaces from 'default' to undefined`, async () => { - await repository.create('dashboard', attributes, { - id, - namespace, - initialNamespaces: ['default'], - }); - - expect(client.create).toHaveBeenCalledTimes(1); - expect(client.create).toHaveBeenNthCalledWith( - 1, - expect.objectContaining({ - id: `dashboard:${id}`, - body: expect.not.objectContaining({ namespace: 'default' }), - }), - expect.anything() - ); - }); - - it(`doesn't prepend namespace to the id or add namespace or namespaces fields when using namespace-agnostic type`, async () => { - await createSuccess(NAMESPACE_AGNOSTIC_TYPE, attributes, { id, namespace }); - expect(client.create).toHaveBeenCalledWith( - expect.objectContaining({ - id: `${NAMESPACE_AGNOSTIC_TYPE}:${id}`, - body: expect.not.objectContaining({ - namespace: expect.anything(), - namespaces: expect.anything(), - }), - }), - expect.anything() - ); - }); - }); - - describe('errors', () => { - it(`throws when options.initialNamespaces is used with a space-agnostic object`, async () => { - await expect( - repository.create(NAMESPACE_AGNOSTIC_TYPE, attributes, { - initialNamespaces: [namespace], - }) - ).rejects.toThrowError( - createBadRequestErrorPayload('"initialNamespaces" cannot be used on space-agnostic types') - ); - }); - - it(`throws when options.initialNamespaces is empty`, async () => { - await expect( - repository.create(MULTI_NAMESPACE_TYPE, attributes, { initialNamespaces: [] }) - ).rejects.toThrowError( - createBadRequestErrorPayload('"initialNamespaces" must be a non-empty array of strings') - ); - }); - - it(`throws when options.initialNamespaces is used with a space-isolated object and does not specify a single space`, async () => { - const doTest = async (objType: string, initialNamespaces?: string[]) => { - await expect( - repository.create(objType, attributes, { initialNamespaces }) - ).rejects.toThrowError( - createBadRequestErrorPayload( - '"initialNamespaces" can only specify a single space when used with space-isolated types' - ) - ); - }; - await doTest('dashboard', ['spacex', 'spacey']); - await doTest('dashboard', ['*']); - await doTest(MULTI_NAMESPACE_ISOLATED_TYPE, ['spacex', 'spacey']); - await doTest(MULTI_NAMESPACE_ISOLATED_TYPE, ['*']); - }); - - it(`throws when options.namespace is '*'`, async () => { - await expect( - repository.create(type, attributes, { namespace: ALL_NAMESPACES_STRING }) - ).rejects.toThrowError(createBadRequestErrorPayload('"options.namespace" cannot be "*"')); - }); - - it(`throws when type is invalid`, async () => { - await expect(repository.create('unknownType', attributes)).rejects.toThrowError( - createUnsupportedTypeErrorPayload('unknownType') - ); - expect(client.create).not.toHaveBeenCalled(); - }); - - it(`throws when type is hidden`, async () => { - await expect(repository.create(HIDDEN_TYPE, attributes)).rejects.toThrowError( - createUnsupportedTypeErrorPayload(HIDDEN_TYPE) - ); - expect(client.create).not.toHaveBeenCalled(); - }); - - it(`throws when schema validation fails`, async () => { - await expect( - repository.create('dashboard', { title: 123 }) - ).rejects.toThrowErrorMatchingInlineSnapshot( - `"[attributes.title]: expected value of type [string] but got [number]: Bad Request"` - ); - expect(client.create).not.toHaveBeenCalled(); - }); - - it(`throws when there is a conflict from preflightCheckForCreate`, async () => { - mockPreflightCheckForCreate.mockResolvedValueOnce([ - { type: MULTI_NAMESPACE_ISOLATED_TYPE, id, error: { type: 'unresolvableConflict' } }, // error type and metadata dont matter - ]); - await expect( - repository.create(MULTI_NAMESPACE_ISOLATED_TYPE, attributes, { - id, - overwrite: true, - namespace, - }) - ).rejects.toThrowError(createConflictErrorPayload(MULTI_NAMESPACE_ISOLATED_TYPE, id)); - expect(mockPreflightCheckForCreate).toHaveBeenCalled(); - }); - - it.todo(`throws when automatic index creation fails`); - - it.todo(`throws when an unexpected failure occurs`); - }); - - describe('migration', () => { - beforeEach(() => { - migrator.migrateDocument.mockImplementation(mockMigrateDocument); - }); - - it(`migrates a document and serializes the migrated doc`, async () => { - const migrationVersion = mockMigrationVersion; - const coreMigrationVersion = '8.0.0'; - const managed = false; - await createSuccess(type, attributes, { - id, - references, - migrationVersion, - coreMigrationVersion, - managed, - }); - const doc = { - type, - id, - attributes, - references, - managed, - migrationVersion, - coreMigrationVersion, - ...mockTimestampFieldsWithCreated, - }; - expectMigrationArgs(doc); - - const migratedDoc = migrator.migrateDocument(doc); - expect(serializer.savedObjectToRaw).toHaveBeenLastCalledWith(migratedDoc); - }); - - it(`migrates a document, adds managed=false and serializes the migrated doc`, async () => { - const migrationVersion = mockMigrationVersion; - const coreMigrationVersion = '8.0.0'; - await createSuccess(type, attributes, { - id, - references, - migrationVersion, - coreMigrationVersion, - managed: undefined, - }); - const doc = { - type, - id, - attributes, - references, - managed: undefined, - migrationVersion, - coreMigrationVersion, - ...mockTimestampFieldsWithCreated, - }; - expectMigrationArgs({ ...doc, managed: false }); - - const migratedDoc = migrator.migrateDocument(doc); - expect(migratedDoc.managed).toBe(false); - expect(serializer.savedObjectToRaw).toHaveBeenLastCalledWith(migratedDoc); - }); - - it(`migrates a document, does not change managed=true to managed=false and serializes the migrated doc`, async () => { - const migrationVersion = mockMigrationVersion; - const coreMigrationVersion = '8.0.0'; - await createSuccess(type, attributes, { - id, - references, - migrationVersion, - coreMigrationVersion, - managed: true, - }); - const doc = { - type, - id, - attributes, - references, - managed: true, - migrationVersion, - coreMigrationVersion, - ...mockTimestampFieldsWithCreated, - }; - expectMigrationArgs(doc); - - const migratedDoc = migrator.migrateDocument(doc); - expect(migratedDoc.managed).toBe(true); - expect(serializer.savedObjectToRaw).toHaveBeenLastCalledWith(migratedDoc); - }); - - it(`adds namespace to body when providing namespace for single-namespace type`, async () => { - await createSuccess(type, attributes, { id, namespace }); - expectMigrationArgs({ namespace }); - }); - - it(`doesn't add namespace to body when providing no namespace for single-namespace type`, async () => { - await createSuccess(type, attributes, { id }); - expectMigrationArgs({ namespace: expect.anything() }, false); - }); - - it(`doesn't add namespace to body when not using single-namespace type`, async () => { - await createSuccess(NAMESPACE_AGNOSTIC_TYPE, attributes, { id, namespace }); - expectMigrationArgs({ namespace: expect.anything() }, false, 1); - - client.create.mockClear(); - await createSuccess(MULTI_NAMESPACE_ISOLATED_TYPE, attributes, { id }); - expectMigrationArgs({ namespace: expect.anything() }, false, 2); - }); - - it(`adds namespaces to body when providing namespace for multi-namespace type`, async () => { - await createSuccess(MULTI_NAMESPACE_ISOLATED_TYPE, attributes, { id, namespace }); - expectMigrationArgs({ namespaces: [namespace] }); - }); - - it(`adds default namespaces to body when providing no namespace for multi-namespace type`, async () => { - await createSuccess(MULTI_NAMESPACE_ISOLATED_TYPE, attributes, { id }); - expectMigrationArgs({ namespaces: ['default'] }); - }); - - it(`doesn't add namespaces to body when not using multi-namespace type`, async () => { - await createSuccess(type, attributes, { id }); - expectMigrationArgs({ namespaces: expect.anything() }, false, 1); - - client.create.mockClear(); - await createSuccess(NAMESPACE_AGNOSTIC_TYPE, attributes, { id }); - expectMigrationArgs({ namespaces: expect.anything() }, false, 2); - }); - }); - - describe('returns', () => { - it(`formats the ES response`, async () => { - const result = await createSuccess(MULTI_NAMESPACE_TYPE, attributes, { - id, - namespace, - references, - }); - expect(result).toEqual({ - type: MULTI_NAMESPACE_TYPE, - id, - ...mockTimestampFieldsWithCreated, - version: mockVersion, - attributes, - references, - namespaces: [namespace ?? 'default'], - coreMigrationVersion: expect.any(String), - typeMigrationVersion: '1.1.1', - managed: false, - }); - }); - it(`allows setting 'managed' to true`, async () => { - const result = await createSuccess(MULTI_NAMESPACE_TYPE, attributes, { - id, - namespace, - references, - managed: true, - }); - expect(result).toEqual({ - type: MULTI_NAMESPACE_TYPE, - id, - ...mockTimestampFieldsWithCreated, - version: mockVersion, - attributes, - references, - namespaces: [namespace ?? 'default'], - coreMigrationVersion: expect.any(String), - typeMigrationVersion: '1.1.1', - managed: true, - }); - }); - }); - }); - - describe('#delete', () => { - const type = 'index-pattern'; - const id = 'logstash-*'; - const namespace = 'foo-namespace'; - - beforeEach(() => { - mockDeleteLegacyUrlAliases.mockClear(); - mockDeleteLegacyUrlAliases.mockResolvedValue(); - }); - - describe('client calls', () => { - it(`should use the ES delete action when not using a multi-namespace type`, async () => { - await deleteSuccess(client, repository, registry, type, id); - expect(client.get).not.toHaveBeenCalled(); - expect(client.delete).toHaveBeenCalledTimes(1); - }); - - it(`should use ES get action then delete action when using a multi-namespace type`, async () => { - await deleteSuccess(client, repository, registry, MULTI_NAMESPACE_ISOLATED_TYPE, id); - expect(client.get).toHaveBeenCalledTimes(1); - expect(client.delete).toHaveBeenCalledTimes(1); - }); - - it(`does not includes the version of the existing document when using a multi-namespace type`, async () => { - await deleteSuccess(client, repository, registry, MULTI_NAMESPACE_ISOLATED_TYPE, id); - const versionProperties = { - if_seq_no: mockVersionProps._seq_no, - if_primary_term: mockVersionProps._primary_term, - }; - expect(client.delete).toHaveBeenCalledWith( - expect.not.objectContaining(versionProperties), - expect.anything() - ); - }); - - it(`defaults to a refresh setting of wait_for`, async () => { - await deleteSuccess(client, repository, registry, type, id); - expect(client.delete).toHaveBeenCalledWith( - expect.objectContaining({ refresh: 'wait_for' }), - expect.anything() - ); - }); - - it(`prepends namespace to the id when providing namespace for single-namespace type`, async () => { - await deleteSuccess(client, repository, registry, type, id, { namespace }); - expect(client.delete).toHaveBeenCalledWith( - expect.objectContaining({ id: `${namespace}:${type}:${id}` }), - expect.anything() - ); - }); - - it(`doesn't prepend namespace to the id when providing no namespace for single-namespace type`, async () => { - await deleteSuccess(client, repository, registry, type, id); - expect(client.delete).toHaveBeenCalledWith( - expect.objectContaining({ id: `${type}:${id}` }), - expect.anything() - ); - }); - - it(`normalizes options.namespace from 'default' to undefined`, async () => { - await deleteSuccess(client, repository, registry, type, id, { namespace: 'default' }); - expect(client.delete).toHaveBeenCalledWith( - expect.objectContaining({ id: `${type}:${id}` }), - expect.anything() - ); - }); - - it(`doesn't prepend namespace to the id when not using single-namespace type`, async () => { - await deleteSuccess(client, repository, registry, NAMESPACE_AGNOSTIC_TYPE, id, { - namespace, - }); - expect(client.delete).toHaveBeenCalledWith( - expect.objectContaining({ id: `${NAMESPACE_AGNOSTIC_TYPE}:${id}` }), - expect.anything() - ); - - client.delete.mockClear(); - await deleteSuccess(client, repository, registry, MULTI_NAMESPACE_ISOLATED_TYPE, id, { - namespace, - }); - expect(client.delete).toHaveBeenCalledWith( - expect.objectContaining({ id: `${MULTI_NAMESPACE_ISOLATED_TYPE}:${id}` }), - expect.anything() - ); - }); - }); - - describe('legacy URL aliases', () => { - it(`doesn't delete legacy URL aliases for single-namespace object types`, async () => { - await deleteSuccess(client, repository, registry, type, id, { namespace }); - expect(mockDeleteLegacyUrlAliases).not.toHaveBeenCalled(); - }); - - // We intentionally do not include a test case for a multi-namespace object with a "not found" preflight result, because that throws - // an error (without deleting aliases) and we already have a test case for that - - it(`deletes legacy URL aliases for multi-namespace object types (all spaces)`, async () => { - const internalOptions = { - mockGetResponseValue: getMockGetResponse( - registry, - { type: MULTI_NAMESPACE_TYPE, id }, - ALL_NAMESPACES_STRING - ), - }; - await deleteSuccess( - client, - repository, - registry, - MULTI_NAMESPACE_TYPE, - id, - { namespace, force: true }, - internalOptions - ); - expect(mockDeleteLegacyUrlAliases).toHaveBeenCalledWith( - expect.objectContaining({ - type: MULTI_NAMESPACE_TYPE, - id, - namespaces: [], - deleteBehavior: 'exclusive', - }) - ); - }); - - it(`deletes legacy URL aliases for multi-namespace object types (specific spaces)`, async () => { - await deleteSuccess(client, repository, registry, MULTI_NAMESPACE_TYPE, id, { namespace }); // this function mocks a preflight response with the given namespace by default - expect(mockDeleteLegacyUrlAliases).toHaveBeenCalledWith( - expect.objectContaining({ - type: MULTI_NAMESPACE_TYPE, - id, - namespaces: [namespace], - deleteBehavior: 'inclusive', - }) - ); - }); - - it(`logs a message when deleteLegacyUrlAliases returns an error`, async () => { - client.get.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise( - getMockGetResponse(registry, { type: MULTI_NAMESPACE_ISOLATED_TYPE, id, namespace }) - ) - ); - client.delete.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise({ - result: 'deleted', - } as estypes.DeleteResponse) - ); - mockDeleteLegacyUrlAliases.mockRejectedValueOnce(new Error('Oh no!')); - await repository.delete(MULTI_NAMESPACE_ISOLATED_TYPE, id, { namespace }); - expect(client.get).toHaveBeenCalledTimes(1); - expect(logger.error).toHaveBeenCalledTimes(1); - expect(logger.error).toHaveBeenCalledWith( - 'Unable to delete aliases when deleting an object: Oh no!' - ); - }); - }); - - describe('errors', () => { - const expectNotFoundError = async ( - type: string, - id: string, - options?: SavedObjectsDeleteOptions - ) => { - await expect(repository.delete(type, id, options)).rejects.toThrowError( - createGenericNotFoundErrorPayload(type, id) - ); - }; - - it(`throws when options.namespace is '*'`, async () => { - await expect( - repository.delete(type, id, { namespace: ALL_NAMESPACES_STRING }) - ).rejects.toThrowError(createBadRequestErrorPayload('"options.namespace" cannot be "*"')); - }); - - it(`throws when type is invalid`, async () => { - await expectNotFoundError('unknownType', id); - expect(client.delete).not.toHaveBeenCalled(); - }); - - it(`throws when type is hidden`, async () => { - await expectNotFoundError(HIDDEN_TYPE, id); - expect(client.delete).not.toHaveBeenCalled(); - }); - - it(`throws when ES is unable to find the document during get`, async () => { - client.get.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise({ - found: false, - } as estypes.GetResponse) - ); - await expectNotFoundError(MULTI_NAMESPACE_ISOLATED_TYPE, id); - expect(client.get).toHaveBeenCalledTimes(1); - }); - - it(`throws when ES is unable to find the index during get`, async () => { - client.get.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise({} as estypes.GetResponse, { - statusCode: 404, - }) - ); - await expectNotFoundError(MULTI_NAMESPACE_ISOLATED_TYPE, id); - expect(client.get).toHaveBeenCalledTimes(1); - }); - - it(`throws when the type is multi-namespace and the document exists, but not in this namespace`, async () => { - const response = getMockGetResponse( - registry, - { type: MULTI_NAMESPACE_ISOLATED_TYPE, id }, - namespace - ); - client.get.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise(response) - ); - await expectNotFoundError(MULTI_NAMESPACE_ISOLATED_TYPE, id, { - namespace: 'bar-namespace', - }); - expect(client.get).toHaveBeenCalledTimes(1); - }); - - it(`throws when the type is multi-namespace and the document has multiple namespaces and the force option is not enabled`, async () => { - const response = getMockGetResponse(registry, { - type: MULTI_NAMESPACE_ISOLATED_TYPE, - id, - namespace, - }); - response._source!.namespaces = [namespace, 'bar-namespace']; - client.get.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise(response) - ); - await expect( - repository.delete(MULTI_NAMESPACE_ISOLATED_TYPE, id, { namespace }) - ).rejects.toThrowError( - 'Unable to delete saved object that exists in multiple namespaces, use the `force` option to delete it anyway' - ); - expect(client.get).toHaveBeenCalledTimes(1); - }); - - it(`throws when the type is multi-namespace and the document has all namespaces and the force option is not enabled`, async () => { - const response = getMockGetResponse(registry, { - type: MULTI_NAMESPACE_ISOLATED_TYPE, - id, - namespace, - }); - response._source!.namespaces = ['*']; - client.get.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise(response) - ); - await expect( - repository.delete(MULTI_NAMESPACE_ISOLATED_TYPE, id, { namespace }) - ).rejects.toThrowError( - 'Unable to delete saved object that exists in multiple namespaces, use the `force` option to delete it anyway' - ); - expect(client.get).toHaveBeenCalledTimes(1); - }); - - it(`throws when ES is unable to find the document during delete`, async () => { - client.delete.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise({ - result: 'not_found', - } as estypes.DeleteResponse) - ); - await expectNotFoundError(type, id); - expect(client.delete).toHaveBeenCalledTimes(1); - }); - - it(`throws when ES is unable to find the index during delete`, async () => { - client.delete.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise({ - // @elastic/elasticsearch doesn't declare error on DeleteResponse - error: { type: 'index_not_found_exception' }, - } as unknown as estypes.DeleteResponse) - ); - await expectNotFoundError(type, id); - expect(client.delete).toHaveBeenCalledTimes(1); - }); - - it(`throws when ES returns an unexpected response`, async () => { - client.delete.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise({ - result: 'something unexpected' as estypes.Result, - } as estypes.DeleteResponse) - ); - await expect(repository.delete(type, id)).rejects.toThrowError( - 'Unexpected Elasticsearch DELETE response' - ); - expect(client.delete).toHaveBeenCalledTimes(1); - }); - }); - - describe('returns', () => { - it(`returns an empty object on success`, async () => { - const result = await deleteSuccess(client, repository, registry, type, id); - expect(result).toEqual({}); - }); - }); - }); - - describe('#deleteByNamespace', () => { - const namespace = 'foo-namespace'; - const mockUpdateResults = { - took: 15, - timed_out: false, - total: 3, - updated: 2, - deleted: 1, - batches: 1, - version_conflicts: 0, - noops: 0, - retries: { bulk: 0, search: 0 }, - throttled_millis: 0, - requests_per_second: -1.0, - throttled_until_millis: 0, - failures: [], - }; - - const deleteByNamespaceSuccess = async ( - namespace: string, - options?: SavedObjectsDeleteByNamespaceOptions - ) => { - client.updateByQuery.mockResponseOnce(mockUpdateResults); - const result = await repository.deleteByNamespace(namespace, options); - expect(mockGetSearchDsl).toHaveBeenCalledTimes(1); - expect(client.updateByQuery).toHaveBeenCalledTimes(1); - return result; - }; - - describe('client calls', () => { - it(`should use the ES updateByQuery action`, async () => { - await deleteByNamespaceSuccess(namespace); - expect(client.updateByQuery).toHaveBeenCalledTimes(1); - }); - - it(`should use all indices for types that are not namespace-agnostic`, async () => { - await deleteByNamespaceSuccess(namespace); - expect(client.updateByQuery).toHaveBeenCalledWith( - expect.objectContaining({ - index: ['.kibana-test_8.0.0-testing', 'custom_8.0.0-testing'], - }), - expect.anything() - ); - }); - }); - - describe('errors', () => { - it(`throws when namespace is not a string or is '*'`, async () => { - const test = async (namespace: unknown) => { - // @ts-expect-error namespace is unknown - await expect(repository.deleteByNamespace(namespace)).rejects.toThrowError( - `namespace is required, and must be a string` - ); - expect(client.updateByQuery).not.toHaveBeenCalled(); - }; - await test(undefined); - await test(['namespace']); - await test(123); - await test(true); - await test(ALL_NAMESPACES_STRING); - }); - }); - - describe('returns', () => { - it(`returns the query results on success`, async () => { - const result = await deleteByNamespaceSuccess(namespace); - expect(result).toEqual(mockUpdateResults); - }); - }); - - describe('search dsl', () => { - it(`constructs a query using all multi-namespace types, and another using all single-namespace types`, async () => { - await deleteByNamespaceSuccess(namespace); - const allTypes = registry.getAllTypes().map((type) => type.name); - expect(mockGetSearchDsl).toHaveBeenCalledWith(mappings, registry, { - namespaces: [namespace], - type: [ - ...allTypes.filter((type) => !registry.isNamespaceAgnostic(type)), - LEGACY_URL_ALIAS_TYPE, - ], - kueryNode: expect.anything(), - }); - }); - }); - }); - - describe('#removeReferencesTo', () => { - const type = 'type'; - const id = 'id'; - const defaultOptions = {}; - const updatedCount = 42; - - describe('client calls', () => { - it('should use the ES updateByQuery action', async () => { - await removeReferencesToSuccess(client, repository, type, id); - expect(client.updateByQuery).toHaveBeenCalledTimes(1); - }); - - it('uses the correct default `refresh` value', async () => { - await removeReferencesToSuccess(client, repository, type, id); - expect(client.updateByQuery).toHaveBeenCalledWith( - expect.objectContaining({ - refresh: true, - }), - expect.any(Object) - ); - }); - - it('merges output of getSearchDsl into es request body', async () => { - const query = { query: 1, aggregations: 2 }; - mockGetSearchDsl.mockReturnValue(query); - await removeReferencesToSuccess(client, repository, type, id, { type }); - - expect(client.updateByQuery).toHaveBeenCalledWith( - expect.objectContaining({ - body: expect.objectContaining({ ...query }), - }), - expect.anything() - ); - }); - - it('should set index to all known SO indices on the request', async () => { - await removeReferencesToSuccess(client, repository, type, id); - expect(client.updateByQuery).toHaveBeenCalledWith( - expect.objectContaining({ - index: ['.kibana-test_8.0.0-testing', 'custom_8.0.0-testing'], - }), - expect.anything() - ); - }); - - it('should use the `refresh` option in the request', async () => { - const refresh = Symbol(); - - await removeReferencesToSuccess(client, repository, type, id, { refresh }); - expect(client.updateByQuery).toHaveBeenCalledWith( - expect.objectContaining({ - refresh, - }), - expect.anything() - ); - }); - - it('should pass the correct parameters to the update script', async () => { - await removeReferencesToSuccess(client, repository, type, id); - expect(client.updateByQuery).toHaveBeenCalledWith( - expect.objectContaining({ - body: expect.objectContaining({ - script: expect.objectContaining({ - params: { - type, - id, - }, - }), - }), - }), - expect.anything() - ); - }); - }); - - describe('search dsl', () => { - it(`passes mappings and registry to getSearchDsl`, async () => { - await removeReferencesToSuccess(client, repository, type, id); - expect(mockGetSearchDsl).toHaveBeenCalledWith(mappings, registry, expect.anything()); - }); - - it('passes namespace to getSearchDsl', async () => { - await removeReferencesToSuccess(client, repository, type, id, { namespace: 'some-ns' }); - expect(mockGetSearchDsl).toHaveBeenCalledWith( - mappings, - registry, - expect.objectContaining({ - namespaces: ['some-ns'], - }) - ); - }); - - it('passes hasReference to getSearchDsl', async () => { - await removeReferencesToSuccess(client, repository, type, id); - expect(mockGetSearchDsl).toHaveBeenCalledWith( - mappings, - registry, - expect.objectContaining({ - hasReference: { - type, - id, - }, - }) - ); - }); - - it('passes all known types to getSearchDsl', async () => { - await removeReferencesToSuccess(client, repository, type, id); - expect(mockGetSearchDsl).toHaveBeenCalledWith( - mappings, - registry, - expect.objectContaining({ - type: registry.getAllTypes().map((type) => type.name), - }) - ); - }); - }); - - describe('returns', () => { - it('returns the updated count from the ES response', async () => { - const response = await removeReferencesToSuccess(client, repository, type, id); - expect(response.updated).toBe(updatedCount); - }); - }); - - describe('errors', () => { - it(`throws when ES returns failures`, async () => { - client.updateByQuery.mockResponseOnce({ - updated: 7, - failures: [ - { id: 'failure' } as estypes.BulkIndexByScrollFailure, - { id: 'another-failure' } as estypes.BulkIndexByScrollFailure, - ], - }); - - await expect(repository.removeReferencesTo(type, id, defaultOptions)).rejects.toThrowError( - createConflictErrorPayload(type, id) - ); - }); - }); - }); - - describe('#find', () => { - const type = 'index-pattern'; - const namespace = 'foo-namespace'; - - describe('client calls', () => { - it(`should use the ES search action`, async () => { - await findSuccess(client, repository, { type }); - expect(client.search).toHaveBeenCalledTimes(1); - }); - - it(`merges output of getSearchDsl into es request body`, async () => { - const query = { query: 1, aggregations: 2 }; - mockGetSearchDsl.mockReturnValue(query); - await findSuccess(client, repository, { type }); - - expect(client.search).toHaveBeenCalledWith( - expect.objectContaining({ - body: expect.objectContaining({ ...query }), - }), - expect.anything() - ); - }); - - it(`accepts per_page/page`, async () => { - await findSuccess(client, repository, { type, perPage: 10, page: 6 }); - expect(client.search).toHaveBeenCalledWith( - expect.objectContaining({ - size: 10, - from: 50, - }), - expect.anything() - ); - }); - - it(`accepts preference`, async () => { - await findSuccess(client, repository, { type, preference: 'pref' }); - expect(client.search).toHaveBeenCalledWith( - expect.objectContaining({ - preference: 'pref', - }), - expect.anything() - ); - }); - - it(`can filter by fields`, async () => { - await findSuccess(client, repository, { type, fields: ['title'] }); - expect(client.search).toHaveBeenCalledWith( - expect.objectContaining({ - body: expect.objectContaining({ - _source: [ - `${type}.title`, - 'namespace', - 'namespaces', - 'type', - 'references', - 'migrationVersion', - 'coreMigrationVersion', - 'typeMigrationVersion', - 'managed', - 'updated_at', - 'created_at', - 'originId', - ], - }), - }), - expect.anything() - ); - }); - - it(`should set rest_total_hits_as_int to true on a request`, async () => { - await findSuccess(client, repository, { type }); - expect(client.search).toHaveBeenCalledWith( - expect.objectContaining({ - rest_total_hits_as_int: true, - }), - expect.anything() - ); - }); - - it(`should not make a client call when attempting to find only invalid or hidden types`, async () => { - const test = async (types: string | string[]) => { - await repository.find({ type: types }); - expect(client.search).not.toHaveBeenCalled(); - }; - - await test('unknownType'); - await test(HIDDEN_TYPE); - await test(['unknownType', HIDDEN_TYPE]); - }); - }); - - describe('errors', () => { - it(`throws when type is not defined`, async () => { - // @ts-expect-error type should be defined - await expect(repository.find({})).rejects.toThrowError( - 'options.type must be a string or an array of strings' - ); - expect(client.search).not.toHaveBeenCalled(); - }); - - it(`throws when namespaces is an empty array`, async () => { - await expect(repository.find({ type: 'foo', namespaces: [] })).rejects.toThrowError( - 'options.namespaces cannot be an empty array' - ); - expect(client.search).not.toHaveBeenCalled(); - }); - - it(`throws when searchFields is defined but not an array`, async () => { - await expect( - // @ts-expect-error searchFields is an array - repository.find({ type, searchFields: 'string' }) - ).rejects.toThrowError('options.searchFields must be an array'); - expect(client.search).not.toHaveBeenCalled(); - }); - - it(`throws when fields is defined but not an array`, async () => { - // @ts-expect-error fields is an array - await expect(repository.find({ type, fields: 'string' })).rejects.toThrowError( - 'options.fields must be an array' - ); - expect(client.search).not.toHaveBeenCalled(); - }); - - it(`throws when a preference is provided with pit`, async () => { - await expect( - repository.find({ type: 'foo', pit: { id: 'abc123' }, preference: 'hi' }) - ).rejects.toThrowError('options.preference must be excluded when options.pit is used'); - expect(client.search).not.toHaveBeenCalled(); - }); - - it(`throws when KQL filter syntax is invalid`, async () => { - const findOpts: SavedObjectsFindOptions = { - namespaces: [namespace], - search: 'foo*', - searchFields: ['foo'], - type: ['dashboard'], - sortField: 'name', - sortOrder: 'desc', - defaultSearchOperator: 'AND', - hasReference: { - type: 'foo', - id: '1', - }, - filter: 'dashboard.attributes.otherField:<', - }; - - await expect(repository.find(findOpts)).rejects.toMatchInlineSnapshot(` - [Error: KQLSyntaxError: Expected "(", "{", value, whitespace but "<" found. - dashboard.attributes.otherField:< - --------------------------------^: Bad Request] - `); - expect(mockGetSearchDsl).not.toHaveBeenCalled(); - expect(client.search).not.toHaveBeenCalled(); - }); - }); - - describe('returns', () => { - it(`formats the ES response when there is no namespace`, async () => { - const noNamespaceSearchResults = generateIndexPatternSearchResults(); - client.search.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise(noNamespaceSearchResults) - ); - const count = noNamespaceSearchResults.hits.hits.length; - - const response = await repository.find({ type }); - - expect(response.total).toBe(count); - expect(response.saved_objects).toHaveLength(count); - - noNamespaceSearchResults.hits.hits.forEach((doc, i) => { - expect(response.saved_objects[i]).toEqual({ - id: doc._id.replace(/(index-pattern|config|globalType)\:/, ''), - type: doc._source!.type, - originId: doc._source!.originId, - ...mockTimestampFields, - version: mockVersion, - score: doc._score, - attributes: doc._source![doc._source!.type], - references: [], - namespaces: doc._source!.type === NAMESPACE_AGNOSTIC_TYPE ? undefined : ['default'], - coreMigrationVersion: expect.any(String), - typeMigrationVersion: expect.any(String), - managed: expect.any(Boolean), - }); - }); - }); - - it(`formats the ES response when there is a namespace`, async () => { - const namespacedSearchResults = generateIndexPatternSearchResults(namespace); - client.search.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise(namespacedSearchResults) - ); - const count = namespacedSearchResults.hits.hits.length; - - const response = await repository.find({ type, namespaces: [namespace] }); - - expect(response.total).toBe(count); - expect(response.saved_objects).toHaveLength(count); - - namespacedSearchResults.hits.hits.forEach((doc, i) => { - expect(response.saved_objects[i]).toEqual({ - id: doc._id.replace(/(foo-namespace\:)?(index-pattern|config|globalType)\:/, ''), - type: doc._source!.type, - originId: doc._source!.originId, - ...mockTimestampFields, - version: mockVersion, - score: doc._score, - attributes: doc._source![doc._source!.type], - references: [], - namespaces: doc._source!.type === NAMESPACE_AGNOSTIC_TYPE ? undefined : [namespace], - coreMigrationVersion: expect.any(String), - typeMigrationVersion: expect.any(String), - managed: expect.any(Boolean), - }); - }); - }); - - it(`should return empty results when attempting to find only invalid or hidden types`, async () => { - const test = async (types: string | string[]) => { - const result = await repository.find({ type: types }); - expect(result).toEqual(expect.objectContaining({ saved_objects: [] })); - expect(client.search).not.toHaveBeenCalled(); - }; - - await test('unknownType'); - await test(HIDDEN_TYPE); - await test(['unknownType', HIDDEN_TYPE]); - }); - - it('migrates the found document', async () => { - const noNamespaceSearchResults = generateIndexPatternSearchResults(); - client.search.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise(noNamespaceSearchResults) - ); - migrator.migrateDocument.mockImplementationOnce((doc) => ({ ...doc, migrated: true })); - await expect(repository.find({ type })).resolves.toHaveProperty( - 'saved_objects.0.migrated', - true - ); - expect(migrator.migrateDocument).toHaveBeenCalledTimes( - noNamespaceSearchResults.hits.hits.length - ); - expectMigrationArgs({ - type, - id: noNamespaceSearchResults.hits.hits[0]._id.replace( - /(index-pattern|config|globalType)\:/, - '' - ), - }); - }); - }); - - describe('search dsl', () => { - const commonOptions: SavedObjectsFindOptions = { - type: [type], - namespaces: [namespace], - search: 'foo*', - searchFields: ['foo'], - sortField: 'name', - sortOrder: 'desc', - defaultSearchOperator: 'AND', - hasReference: { - type: 'foo', - id: '1', - }, - hasNoReference: { - type: 'bar', - id: '1', - }, - }; - - it(`passes mappings, registry, and search options to getSearchDsl`, async () => { - await findSuccess(client, repository, commonOptions, namespace); - expect(mockGetSearchDsl).toHaveBeenCalledWith(mappings, registry, commonOptions); - }); - - it(`accepts hasReferenceOperator`, async () => { - const relevantOpts: SavedObjectsFindOptions = { - ...commonOptions, - hasReferenceOperator: 'AND', - }; - - await findSuccess(client, repository, relevantOpts, namespace); - expect(mockGetSearchDsl).toHaveBeenCalledWith(mappings, registry, { - ...relevantOpts, - hasReferenceOperator: 'AND', - }); - }); - - it(`accepts searchAfter`, async () => { - const relevantOpts: SavedObjectsFindOptions = { - ...commonOptions, - searchAfter: ['1', 'a'], - }; - - await findSuccess(client, repository, relevantOpts, namespace); - expect(mockGetSearchDsl).toHaveBeenCalledWith(mappings, registry, { - ...relevantOpts, - searchAfter: ['1', 'a'], - }); - }); - - it(`accepts pit`, async () => { - const relevantOpts: SavedObjectsFindOptions = { - ...commonOptions, - pit: { id: 'abc123', keepAlive: '2m' }, - }; - - await findSuccess(client, repository, relevantOpts, namespace); - expect(mockGetSearchDsl).toHaveBeenCalledWith(mappings, registry, { - ...relevantOpts, - pit: { id: 'abc123', keepAlive: '2m' }, - }); - }); - - it(`accepts KQL expression filter and passes KueryNode to getSearchDsl`, async () => { - const findOpts: SavedObjectsFindOptions = { - namespaces: [namespace], - search: 'foo*', - searchFields: ['foo'], - type: ['dashboard'], - sortField: 'name', - sortOrder: 'desc', - defaultSearchOperator: 'AND', - hasReference: { - type: 'foo', - id: '1', - }, - filter: 'dashboard.attributes.otherField: *', - }; - - await findSuccess(client, repository, findOpts, namespace); - const { kueryNode } = mockGetSearchDsl.mock.calls[0][2]; - expect(kueryNode).toMatchInlineSnapshot(` - Object { - "arguments": Array [ - Object { - "isQuoted": false, - "type": "literal", - "value": "dashboard.otherField", - }, - Object { - "type": "wildcard", - "value": "@kuery-wildcard@", - }, - ], - "function": "is", - "type": "function", - } - `); - }); - - it(`accepts KQL KueryNode filter and passes KueryNode to getSearchDsl`, async () => { - const findOpts: SavedObjectsFindOptions = { - namespaces: [namespace], - search: 'foo*', - searchFields: ['foo'], - type: ['dashboard'], - sortField: 'name', - sortOrder: 'desc', - defaultSearchOperator: 'AND', - hasReference: { - type: 'foo', - id: '1', - }, - filter: nodeTypes.function.buildNode('is', `dashboard.attributes.otherField`, '*'), - }; - - await findSuccess(client, repository, findOpts, namespace); - const { kueryNode } = mockGetSearchDsl.mock.calls[0][2]; - expect(kueryNode).toMatchInlineSnapshot(` - Object { - "arguments": Array [ - Object { - "isQuoted": false, - "type": "literal", - "value": "dashboard.otherField", - }, - Object { - "type": "wildcard", - "value": "@kuery-wildcard@", - }, - ], - "function": "is", - "type": "function", - } - `); - }); - - it(`supports multiple types`, async () => { - const types = ['config', 'index-pattern']; - await findSuccess(client, repository, { type: types }); - - expect(mockGetSearchDsl).toHaveBeenCalledWith( - mappings, - registry, - expect.objectContaining({ - type: types, - }) - ); - }); - - it(`filters out invalid types`, async () => { - const types = ['config', 'unknownType', 'index-pattern']; - await findSuccess(client, repository, { type: types }); - - expect(mockGetSearchDsl).toHaveBeenCalledWith( - mappings, - registry, - expect.objectContaining({ - type: ['config', 'index-pattern'], - }) - ); - }); - - it(`filters out hidden types`, async () => { - const types = ['config', HIDDEN_TYPE, 'index-pattern']; - await findSuccess(client, repository, { type: types }); - - expect(mockGetSearchDsl).toHaveBeenCalledWith( - mappings, - registry, - expect.objectContaining({ - type: ['config', 'index-pattern'], - }) - ); - }); - }); - }); - - describe('#get', () => { - const type = 'index-pattern'; - const id = 'logstash-*'; - const namespace = 'foo-namespace'; - const originId = 'some-origin-id'; - - describe('client calls', () => { - it(`should use the ES get action`, async () => { - await getSuccess(client, repository, registry, type, id); - expect(client.get).toHaveBeenCalledTimes(1); - }); - - it(`prepends namespace to the id when providing namespace for single-namespace type`, async () => { - await getSuccess(client, repository, registry, type, id, { namespace }); - expect(client.get).toHaveBeenCalledWith( - expect.objectContaining({ - id: `${namespace}:${type}:${id}`, - }), - expect.anything() - ); - }); - - it(`doesn't prepend namespace to the id when providing no namespace for single-namespace type`, async () => { - await getSuccess(client, repository, registry, type, id); - expect(client.get).toHaveBeenCalledWith( - expect.objectContaining({ - id: `${type}:${id}`, - }), - expect.anything() - ); - }); - - it(`normalizes options.namespace from 'default' to undefined`, async () => { - await getSuccess(client, repository, registry, type, id, { namespace: 'default' }); - expect(client.get).toHaveBeenCalledWith( - expect.objectContaining({ - id: `${type}:${id}`, - }), - expect.anything() - ); - }); - - it(`doesn't prepend namespace to the id when not using single-namespace type`, async () => { - await getSuccess(client, repository, registry, NAMESPACE_AGNOSTIC_TYPE, id, { namespace }); - expect(client.get).toHaveBeenCalledWith( - expect.objectContaining({ - id: `${NAMESPACE_AGNOSTIC_TYPE}:${id}`, - }), - expect.anything() - ); - - client.get.mockClear(); - await getSuccess(client, repository, registry, MULTI_NAMESPACE_ISOLATED_TYPE, id, { - namespace, - }); - expect(client.get).toHaveBeenCalledWith( - expect.objectContaining({ - id: `${MULTI_NAMESPACE_ISOLATED_TYPE}:${id}`, - }), - expect.anything() - ); - }); - }); - - describe('errors', () => { - const expectNotFoundError = async ( - type: string, - id: string, - options?: SavedObjectsBaseOptions - ) => { - await expect(repository.get(type, id, options)).rejects.toThrowError( - createGenericNotFoundErrorPayload(type, id) - ); - }; - - it(`throws when options.namespace is '*'`, async () => { - await expect( - repository.get(type, id, { namespace: ALL_NAMESPACES_STRING }) - ).rejects.toThrowError(createBadRequestErrorPayload('"options.namespace" cannot be "*"')); - }); - - it(`throws when type is invalid`, async () => { - await expectNotFoundError('unknownType', id); - expect(client.get).not.toHaveBeenCalled(); - }); - - it(`throws when type is hidden`, async () => { - await expectNotFoundError(HIDDEN_TYPE, id); - expect(client.get).not.toHaveBeenCalled(); - }); - - it(`throws when ES is unable to find the document during get`, async () => { - client.get.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise({ - found: false, - } as estypes.GetResponse) - ); - await expectNotFoundError(type, id); - expect(client.get).toHaveBeenCalledTimes(1); - }); - - it(`throws when ES is unable to find the index during get`, async () => { - client.get.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise({} as estypes.GetResponse, { - statusCode: 404, - }) - ); - await expectNotFoundError(type, id); - expect(client.get).toHaveBeenCalledTimes(1); - }); - - it(`throws when type is multi-namespace and the document exists, but not in this namespace`, async () => { - const response = getMockGetResponse( - registry, - { type: MULTI_NAMESPACE_ISOLATED_TYPE, id }, - namespace - ); - client.get.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise(response) - ); - await expectNotFoundError(MULTI_NAMESPACE_ISOLATED_TYPE, id, { - namespace: 'bar-namespace', - }); - expect(client.get).toHaveBeenCalledTimes(1); - }); - }); - - describe('returns', () => { - it(`formats the ES response`, async () => { - const result = await getSuccess(client, repository, registry, type, id); - expect(result).toEqual({ - id, - type, - updated_at: mockTimestamp, - version: mockVersion, - attributes: { - title: 'Testing', - }, - references: [], - namespaces: ['default'], - coreMigrationVersion: expect.any(String), - typeMigrationVersion: expect.any(String), - managed: expect.any(Boolean), - }); - }); - - it(`includes namespaces if type is multi-namespace`, async () => { - const result = await getSuccess( - client, - repository, - registry, - MULTI_NAMESPACE_ISOLATED_TYPE, - id - ); - expect(result).toMatchObject({ - namespaces: expect.any(Array), - }); - }); - - it(`include namespaces if type is not multi-namespace`, async () => { - const result = await getSuccess(client, repository, registry, type, id); - expect(result).toMatchObject({ - namespaces: ['default'], - }); - }); - - it(`includes originId property if present in cluster call response`, async () => { - const result = await getSuccess(client, repository, registry, type, id, {}, originId); - expect(result).toMatchObject({ originId }); - }); - }); - - it('migrates the fetched document', async () => { - migrator.migrateDocument.mockReturnValueOnce( - 'migrated' as unknown as ReturnType - ); - await expect(getSuccess(client, repository, registry, type, id)).resolves.toBe('migrated'); - expect(migrator.migrateDocument).toHaveBeenCalledTimes(1); - expectMigrationArgs({ - id, - type, - }); - }); - }); - - describe('#resolve', () => { - afterEach(() => { - mockInternalBulkResolve.mockReset(); - }); - - it('passes arguments to the internalBulkResolve module and returns the result', async () => { - const expectedResult: SavedObjectsResolveResponse = { - saved_object: { type: 'type', id: 'id', attributes: {}, references: [] }, - outcome: 'exactMatch', - }; - mockInternalBulkResolve.mockResolvedValue({ resolved_objects: [expectedResult] }); - - await expect(repository.resolve('obj-type', 'obj-id')).resolves.toEqual(expectedResult); - expect(mockInternalBulkResolve).toHaveBeenCalledTimes(1); - expect(mockInternalBulkResolve).toHaveBeenCalledWith( - expect.objectContaining({ objects: [{ type: 'obj-type', id: 'obj-id' }] }) - ); - }); - - it('throws when internalBulkResolve result is an error', async () => { - const error = SavedObjectsErrorHelpers.decorateBadRequestError(new Error('Oh no!')); - const expectedResult: BulkResolveError = { type: 'obj-type', id: 'obj-id', error }; - mockInternalBulkResolve.mockResolvedValue({ resolved_objects: [expectedResult] }); - - await expect(repository.resolve('foo', '2')).rejects.toEqual(error); - }); - - it('throws when internalBulkResolve throws', async () => { - const error = new Error('Oh no!'); - mockInternalBulkResolve.mockRejectedValue(error); - - await expect(repository.resolve('foo', '2')).rejects.toEqual(error); - }); - }); - - describe('#incrementCounter', () => { - const type = 'config'; - const id = 'one'; - const counterFields = ['buildNum', 'apiCallsCount']; - const namespace = 'foo-namespace'; - const originId = 'some-origin-id'; - - const incrementCounterSuccess = async ( - type: string, - id: string, - fields: Array, - options?: SavedObjectsIncrementCounterOptions, - internalOptions: { mockGetResponseValue?: estypes.GetResponse } = {} - ) => { - const { mockGetResponseValue } = internalOptions; - const isMultiNamespace = registry.isMultiNamespace(type); - if (isMultiNamespace) { - const response = - mockGetResponseValue ?? getMockGetResponse(registry, { type, id }, options?.namespace); - client.get.mockResponseOnce(response); - } - - client.update.mockResponseImplementation((params) => { - return { - body: { - _id: params.id, - ...mockVersionProps, - _index: MAIN_SAVED_OBJECT_INDEX, - get: { - found: true, - _source: { - type, - ...mockTimestampFields, - [type]: { - ...fields.reduce((acc, field) => { - acc[typeof field === 'string' ? field : field.fieldName] = 8468; - return acc; - }, {} as Record), - defaultIndex: 'logstash-*', - }, - }, - }, - } as estypes.UpdateResponse, - }; - }); - - const result = await repository.incrementCounter(type, id, fields, options); - expect(client.get).toHaveBeenCalledTimes(isMultiNamespace ? 1 : 0); - return result; - }; - - beforeEach(() => { - mockPreflightCheckForCreate.mockReset(); - mockPreflightCheckForCreate.mockImplementation(({ objects }) => { - return Promise.resolve(objects.map(({ type, id }) => ({ type, id }))); // respond with no errors by default - }); - }); - - describe('client calls', () => { - it(`should use the ES update action if type is not multi-namespace`, async () => { - await incrementCounterSuccess(type, id, counterFields, { namespace }); - expect(client.get).not.toHaveBeenCalled(); - expect(mockPreflightCheckForCreate).not.toHaveBeenCalled(); - expect(client.update).toHaveBeenCalledTimes(1); - }); - - it(`should use the ES get action then update action if type is multi-namespace, ID is defined, and overwrite=true`, async () => { - await incrementCounterSuccess(MULTI_NAMESPACE_ISOLATED_TYPE, id, counterFields, { - namespace, - }); - expect(client.get).toHaveBeenCalledTimes(1); - expect(mockPreflightCheckForCreate).not.toHaveBeenCalled(); - expect(client.update).toHaveBeenCalledTimes(1); - }); - - it(`should check for alias conflicts if a new multi-namespace object would be created`, async () => { - await incrementCounterSuccess( - MULTI_NAMESPACE_ISOLATED_TYPE, - id, - counterFields, - { namespace }, - { mockGetResponseValue: { found: false } as estypes.GetResponse } - ); - expect(client.get).toHaveBeenCalledTimes(1); - expect(mockPreflightCheckForCreate).toHaveBeenCalledTimes(1); - expect(client.update).toHaveBeenCalledTimes(1); - }); - - it(`defaults to a refresh setting of wait_for`, async () => { - await incrementCounterSuccess(type, id, counterFields, { namespace }); - expect(client.update).toHaveBeenCalledWith( - expect.objectContaining({ - refresh: 'wait_for', - }), - expect.anything() - ); - }); - - it(`uses the 'upsertAttributes' option when specified`, async () => { - const upsertAttributes = { - foo: 'bar', - hello: 'dolly', - }; - await incrementCounterSuccess(type, id, counterFields, { namespace, upsertAttributes }); - expect(client.update).toHaveBeenCalledWith( - expect.objectContaining({ - body: expect.objectContaining({ - upsert: expect.objectContaining({ - [type]: { - foo: 'bar', - hello: 'dolly', - ...counterFields.reduce((aggs, field) => { - return { - ...aggs, - [field]: 1, - }; - }, {}), - }, - }), - }), - }), - expect.anything() - ); - }); - - it(`prepends namespace to the id when providing namespace for single-namespace type`, async () => { - await incrementCounterSuccess(type, id, counterFields, { namespace }); - expect(client.update).toHaveBeenCalledWith( - expect.objectContaining({ - id: `${namespace}:${type}:${id}`, - }), - expect.anything() - ); - }); - - it(`doesn't prepend namespace to the id when providing no namespace for single-namespace type`, async () => { - await incrementCounterSuccess(type, id, counterFields); - expect(client.update).toHaveBeenCalledWith( - expect.objectContaining({ - id: `${type}:${id}`, - }), - expect.anything() - ); - }); - - it(`normalizes options.namespace from 'default' to undefined`, async () => { - await incrementCounterSuccess(type, id, counterFields, { namespace: 'default' }); - expect(client.update).toHaveBeenCalledWith( - expect.objectContaining({ - id: `${type}:${id}`, - }), - expect.anything() - ); - }); - - it(`doesn't prepend namespace to the id when not using single-namespace type`, async () => { - await incrementCounterSuccess(NAMESPACE_AGNOSTIC_TYPE, id, counterFields, { namespace }); - expect(client.update).toHaveBeenCalledWith( - expect.objectContaining({ - id: `${NAMESPACE_AGNOSTIC_TYPE}:${id}`, - }), - expect.anything() - ); - - client.update.mockClear(); - await incrementCounterSuccess(MULTI_NAMESPACE_ISOLATED_TYPE, id, counterFields, { - namespace, - }); - expect(client.update).toHaveBeenCalledWith( - expect.objectContaining({ - id: `${MULTI_NAMESPACE_ISOLATED_TYPE}:${id}`, - }), - expect.anything() - ); - }); - }); - - describe('errors', () => { - const expectUnsupportedTypeError = async ( - type: string, - id: string, - field: Array - ) => { - await expect(repository.incrementCounter(type, id, field)).rejects.toThrowError( - createUnsupportedTypeErrorPayload(type) - ); - }; - - it(`throws when options.namespace is '*'`, async () => { - await expect( - repository.incrementCounter(type, id, counterFields, { - namespace: ALL_NAMESPACES_STRING, - }) - ).rejects.toThrowError(createBadRequestErrorPayload('"options.namespace" cannot be "*"')); - }); - - it(`throws when type is not a string`, async () => { - const test = async (type: unknown) => { - await expect( - // @ts-expect-error type is supposed to be a string - repository.incrementCounter(type, id, counterFields) - ).rejects.toThrowError(`"type" argument must be a string`); - expect(client.update).not.toHaveBeenCalled(); - }; - - await test(null); - await test(42); - await test(false); - await test({}); - }); - - it(`throws when id is empty`, async () => { - await expect(repository.incrementCounter(type, '', counterFields)).rejects.toThrowError( - createBadRequestErrorPayload('id cannot be empty') - ); - expect(client.update).not.toHaveBeenCalled(); - }); - - it(`throws when counterField is not CounterField type`, async () => { - const test = async (field: unknown[]) => { - await expect( - // @ts-expect-error field is of wrong type - repository.incrementCounter(type, id, field) - ).rejects.toThrowError( - `"counterFields" argument must be of type Array` - ); - expect(client.update).not.toHaveBeenCalled(); - }; - - await test([null]); - await test([42]); - await test([false]); - await test([{}]); - await test([{}, false, 42, null, 'string']); - await test([{ fieldName: 'string' }, false, null, 'string']); - }); - - it(`throws when type is invalid`, async () => { - await expectUnsupportedTypeError('unknownType', id, counterFields); - expect(client.update).not.toHaveBeenCalled(); - }); - - it(`throws when type is hidden`, async () => { - await expectUnsupportedTypeError(HIDDEN_TYPE, id, counterFields); - expect(client.update).not.toHaveBeenCalled(); - }); - - it(`throws when there is a conflict with an existing multi-namespace saved object (get)`, async () => { - const response = getMockGetResponse( - registry, - { type: MULTI_NAMESPACE_ISOLATED_TYPE, id }, - 'bar-namespace' - ); - client.get.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise(response) - ); - await expect( - repository.incrementCounter(MULTI_NAMESPACE_ISOLATED_TYPE, id, counterFields, { - namespace, - }) - ).rejects.toThrowError(createConflictErrorPayload(MULTI_NAMESPACE_ISOLATED_TYPE, id)); - expect(client.get).toHaveBeenCalledTimes(1); - expect(mockPreflightCheckForCreate).not.toHaveBeenCalled(); - expect(client.update).not.toHaveBeenCalled(); - }); - - it(`throws when there is an alias conflict from preflightCheckForCreate`, async () => { - client.get.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise({ - found: false, - } as estypes.GetResponse) - ); - mockPreflightCheckForCreate.mockResolvedValue([ - { type: 'foo', id: 'bar', error: { type: 'aliasConflict' } }, - ]); - await expect( - repository.incrementCounter(MULTI_NAMESPACE_ISOLATED_TYPE, id, counterFields, { - namespace, - }) - ).rejects.toThrowError(createConflictErrorPayload(MULTI_NAMESPACE_ISOLATED_TYPE, id)); - expect(client.get).toHaveBeenCalledTimes(1); - expect(mockPreflightCheckForCreate).toHaveBeenCalledTimes(1); - expect(client.update).not.toHaveBeenCalled(); - }); - - it(`does not throw when there is a different error from preflightCheckForCreate`, async () => { - client.get.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise({ - found: false, - } as estypes.GetResponse) - ); - mockPreflightCheckForCreate.mockResolvedValue([ - { type: 'foo', id: 'bar', error: { type: 'conflict' } }, - ]); - await incrementCounterSuccess( - MULTI_NAMESPACE_ISOLATED_TYPE, - id, - counterFields, - { namespace }, - { mockGetResponseValue: { found: false } as estypes.GetResponse } - ); - expect(client.get).toHaveBeenCalledTimes(1); - expect(mockPreflightCheckForCreate).toHaveBeenCalledTimes(1); - expect(client.update).toHaveBeenCalledTimes(1); - }); - }); - - describe('migration', () => { - beforeEach(() => { - migrator.migrateDocument.mockImplementation(mockMigrateDocument); - }); - - it(`migrates a document and serializes the migrated doc`, async () => { - const migrationVersion = mockMigrationVersion; - await incrementCounterSuccess(type, id, counterFields, { migrationVersion }); - const attributes = { buildNum: 1, apiCallsCount: 1 }; // this is added by the incrementCounter function - const doc = { type, id, attributes, migrationVersion, ...mockTimestampFields }; - expectMigrationArgs(doc); - - const migratedDoc = migrator.migrateDocument(doc); - expect(serializer.savedObjectToRaw).toHaveBeenLastCalledWith(migratedDoc); - }); - }); - - describe('returns', () => { - it(`formats the ES response`, async () => { - client.update.mockResponseImplementation((params) => { - return { - body: { - _id: params.id, - ...mockVersionProps, - _index: MAIN_SAVED_OBJECT_INDEX, - get: { - found: true, - _source: { - type: 'config', - ...mockTimestampFields, - config: { - buildNum: 8468, - apiCallsCount: 100, - defaultIndex: 'logstash-*', - }, - originId, - }, - }, - } as estypes.UpdateResponse, - }; - }); - - const response = await repository.incrementCounter( - 'config', - '6.0.0-alpha1', - ['buildNum', 'apiCallsCount'], - { - namespace: 'foo-namespace', - } - ); - - expect(response).toEqual({ - type: 'config', - id: '6.0.0-alpha1', - ...mockTimestampFields, - version: mockVersion, - references: [], - attributes: { - buildNum: 8468, - apiCallsCount: 100, - defaultIndex: 'logstash-*', - }, - originId, - }); - }); - - it('increments counter by incrementBy config', async () => { - await incrementCounterSuccess(type, id, [{ fieldName: counterFields[0], incrementBy: 3 }]); - - expect(client.update).toBeCalledTimes(1); - expect(client.update).toBeCalledWith( - expect.objectContaining({ - body: expect.objectContaining({ - script: expect.objectContaining({ - params: expect.objectContaining({ - counterFieldNames: [counterFields[0]], - counts: [3], - }), - }), - }), - }), - expect.anything() - ); - }); - - it('does not increment counter when incrementBy is 0', async () => { - await incrementCounterSuccess(type, id, [{ fieldName: counterFields[0], incrementBy: 0 }]); - - expect(client.update).toBeCalledTimes(1); - expect(client.update).toBeCalledWith( - expect.objectContaining({ - body: expect.objectContaining({ - script: expect.objectContaining({ - params: expect.objectContaining({ - counterFieldNames: [counterFields[0]], - counts: [0], - }), - }), - }), - }), - expect.anything() - ); - }); - }); - }); - - describe('#openPointInTimeForType', () => { - const type = 'index-pattern'; - - const generateResults = (id?: string) => ({ id: id || 'id' }); - const successResponse = async (type: string, options?: SavedObjectsOpenPointInTimeOptions) => { - client.openPointInTime.mockResponseOnce(generateResults()); - const result = await repository.openPointInTimeForType(type, options); - expect(client.openPointInTime).toHaveBeenCalledTimes(1); - return result; - }; - - describe('client calls', () => { - it(`should use the ES PIT API`, async () => { - await successResponse(type); - expect(client.openPointInTime).toHaveBeenCalledTimes(1); - }); - - it(`accepts preference`, async () => { - await successResponse(type, { preference: 'pref' }); - expect(client.openPointInTime).toHaveBeenCalledWith( - expect.objectContaining({ - preference: 'pref', - }), - expect.anything() - ); - }); - - it(`accepts keepAlive`, async () => { - await successResponse(type, { keepAlive: '2m' }); - expect(client.openPointInTime).toHaveBeenCalledWith( - expect.objectContaining({ - keep_alive: '2m', - }), - expect.anything() - ); - }); - - it(`defaults keepAlive to 5m`, async () => { - await successResponse(type); - expect(client.openPointInTime).toHaveBeenCalledWith( - expect.objectContaining({ - keep_alive: '5m', - }), - expect.anything() - ); - }); - }); - - describe('errors', () => { - const expectNotFoundError = async (types: string | string[]) => { - await expect(repository.openPointInTimeForType(types)).rejects.toThrowError( - createGenericNotFoundErrorPayload() - ); - }; - - it(`throws when ES is unable to find the index`, async () => { - client.openPointInTime.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise( - { id: 'error' }, - { statusCode: 404 } - ) - ); - await expectNotFoundError(type); - expect(client.openPointInTime).toHaveBeenCalledTimes(1); - }); - - it(`should return generic not found error when attempting to find only invalid or hidden types`, async () => { - const test = async (types: string | string[]) => { - await expectNotFoundError(types); - expect(client.openPointInTime).not.toHaveBeenCalled(); - }; - - await test('unknownType'); - await test(HIDDEN_TYPE); - await test(['unknownType', HIDDEN_TYPE]); - }); - }); - - describe('returns', () => { - it(`returns id in the expected format`, async () => { - const id = 'abc123'; - const results = generateResults(id); - client.openPointInTime.mockResolvedValueOnce( - elasticsearchClientMock.createSuccessTransportRequestPromise(results) - ); - const response = await repository.openPointInTimeForType(type); - expect(response).toEqual({ id }); - }); - }); - }); - - describe('#closePointInTime', () => { - const generateResults = () => ({ succeeded: true, num_freed: 3 }); - const successResponse = async (id: string) => { - client.closePointInTime.mockResponseOnce(generateResults()); - const result = await repository.closePointInTime(id); - expect(client.closePointInTime).toHaveBeenCalledTimes(1); - return result; - }; - - describe('client calls', () => { - it(`should use the ES PIT API`, async () => { - await successResponse('abc123'); - expect(client.closePointInTime).toHaveBeenCalledTimes(1); - }); - - it(`accepts id`, async () => { - await successResponse('abc123'); - expect(client.closePointInTime).toHaveBeenCalledWith( - expect.objectContaining({ - body: expect.objectContaining({ - id: 'abc123', - }), - }), - expect.anything() - ); - }); - }); - - describe('returns', () => { - it(`returns response body from ES`, async () => { - const results = generateResults(); - client.closePointInTime.mockResponseOnce(results); - const response = await repository.closePointInTime('abc123'); - expect(response).toEqual(results); - }); - }); - }); - - describe('#createPointInTimeFinder', () => { - it('returns a new PointInTimeFinder instance', async () => { - const result = await repository.createPointInTimeFinder({ type: 'PIT' }); - expect(result).toBeInstanceOf(PointInTimeFinder); - }); - - it('calls PointInTimeFinder with the provided options and dependencies', async () => { - const options: SavedObjectsCreatePointInTimeFinderOptions = { - type: 'my-type', - }; - const dependencies: SavedObjectsCreatePointInTimeFinderDependencies = { - client: { - find: jest.fn(), - openPointInTimeForType: jest.fn(), - closePointInTime: jest.fn(), - }, - }; - - await repository.createPointInTimeFinder(options, dependencies); - expect(pointInTimeFinderMock).toHaveBeenCalledWith( - options, - expect.objectContaining({ - ...dependencies, - logger, - }) - ); - }); - }); - - describe('#collectMultiNamespaceReferences', () => { - afterEach(() => { - mockCollectMultiNamespaceReferences.mockReset(); - }); - - it('passes arguments to the collectMultiNamespaceReferences module and returns the result', async () => { - const objects: SavedObjectsCollectMultiNamespaceReferencesObject[] = [ - { type: 'foo', id: 'bar' }, - ]; - const expectedResult: SavedObjectsCollectMultiNamespaceReferencesResponse = { - objects: [{ type: 'foo', id: 'bar', spaces: ['ns-1'], inboundReferences: [] }], - }; - mockCollectMultiNamespaceReferences.mockResolvedValue(expectedResult); - - await expect(repository.collectMultiNamespaceReferences(objects)).resolves.toEqual( - expectedResult - ); - expect(mockCollectMultiNamespaceReferences).toHaveBeenCalledTimes(1); - expect(mockCollectMultiNamespaceReferences).toHaveBeenCalledWith( - expect.objectContaining({ objects }) - ); - }); - - it('returns an error from the collectMultiNamespaceReferences module', async () => { - const expectedResult = new Error('Oh no!'); - mockCollectMultiNamespaceReferences.mockRejectedValue(expectedResult); - - await expect(repository.collectMultiNamespaceReferences([])).rejects.toEqual(expectedResult); - }); - }); - - describe('#updateObjectsSpaces', () => { - afterEach(() => { - mockUpdateObjectsSpaces.mockReset(); - }); - - it('passes arguments to the updateObjectsSpaces module and returns the result', async () => { - const objects: SavedObjectsUpdateObjectsSpacesObject[] = [{ type: 'type', id: 'id' }]; - const spacesToAdd = ['to-add', 'also-to-add']; - const spacesToRemove = ['to-remove']; - const options: SavedObjectsUpdateObjectsSpacesOptions = { namespace: 'ns-1' }; - const expectedResult: SavedObjectsUpdateObjectsSpacesResponse = { - objects: [ - { - type: 'type', - id: 'id', - spaces: ['foo', 'bar'], - }, - ], - }; - mockUpdateObjectsSpaces.mockResolvedValue(expectedResult); - - await expect( - repository.updateObjectsSpaces(objects, spacesToAdd, spacesToRemove, options) - ).resolves.toEqual(expectedResult); - expect(mockUpdateObjectsSpaces).toHaveBeenCalledTimes(1); - expect(mockUpdateObjectsSpaces).toHaveBeenCalledWith( - expect.objectContaining({ objects, spacesToAdd, spacesToRemove, options }) - ); - }); - - it('returns an error from the updateObjectsSpaces module', async () => { - const expectedResult = new Error('Oh no!'); - mockUpdateObjectsSpaces.mockRejectedValue(expectedResult); - - await expect(repository.updateObjectsSpaces([], [], [])).rejects.toEqual(expectedResult); - }); - }); - describe('#getCurrentNamespace', () => { it('returns `undefined` for `undefined` namespace argument', async () => { expect(repository.getCurrentNamespace()).toBeUndefined(); diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/__snapshots__/migrations_state_action_machine.test.ts.snap b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/__snapshots__/migrations_state_action_machine.test.ts.snap index d513177d685bc..f709e0137730b 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/__snapshots__/migrations_state_action_machine.test.ts.snap +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/__snapshots__/migrations_state_action_machine.test.ts.snap @@ -5,10 +5,6 @@ Object { "debug": Array [ Array [ "[.my-so-index] INIT RESPONSE", - Object { - "_tag": "Right", - "right": "response", - }, ], Array [ "[.my-so-index] INIT -> LEGACY_REINDEX. took: 0ms.", @@ -233,10 +229,6 @@ Object { ], Array [ "[.my-so-index] LEGACY_REINDEX RESPONSE", - Object { - "_tag": "Right", - "right": "response", - }, ], Array [ "[.my-so-index] LEGACY_REINDEX -> LEGACY_DELETE. took: 0ms.", @@ -465,10 +457,6 @@ Object { ], Array [ "[.my-so-index] LEGACY_DELETE RESPONSE", - Object { - "_tag": "Right", - "right": "response", - }, ], Array [ "[.my-so-index] LEGACY_DELETE -> LEGACY_DELETE. took: 0ms.", @@ -701,10 +689,6 @@ Object { ], Array [ "[.my-so-index] LEGACY_DELETE RESPONSE", - Object { - "_tag": "Right", - "right": "response", - }, ], Array [ "[.my-so-index] LEGACY_DELETE -> DONE. took: 0ms.", @@ -967,10 +951,6 @@ Object { "debug": Array [ Array [ "[.my-so-index] INIT RESPONSE", - Object { - "_tag": "Right", - "right": "response", - }, ], Array [ "[.my-so-index] INIT -> LEGACY_DELETE. took: 0ms.", @@ -1206,10 +1186,6 @@ Object { ], Array [ "[.my-so-index] LEGACY_DELETE RESPONSE", - Object { - "_tag": "Right", - "right": "response", - }, ], Array [ "[.my-so-index] LEGACY_DELETE -> FATAL. took: 0ms.", diff --git a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/common/utils/logs.ts b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/common/utils/logs.ts index 1cf531ef51cc4..04f6242632bd3 100644 --- a/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/common/utils/logs.ts +++ b/packages/core/saved-objects/core-saved-objects-migration-server-internal/src/common/utils/logs.ts @@ -66,5 +66,5 @@ export const logActionResponse = ( state: LogAwareState, res: unknown ) => { - logger.debug(logMessagePrefix + `${state.controlState} RESPONSE`, res as LogMeta); + logger.debug(logMessagePrefix + `${state.controlState} RESPONSE`); }; diff --git a/packages/core/saved-objects/core-saved-objects-server/index.ts b/packages/core/saved-objects/core-saved-objects-server/index.ts index ce042d392d6f4..1ff74905b0b0f 100644 --- a/packages/core/saved-objects/core-saved-objects-server/index.ts +++ b/packages/core/saved-objects/core-saved-objects-server/index.ts @@ -73,6 +73,7 @@ export type { SavedObjectsRawDoc, SavedObjectSanitizedDoc, SavedObjectsRawDocParseOptions, + SavedObjectDoc, SavedObjectUnsanitizedDoc, } from './src/serialization'; export type { ISavedObjectTypeRegistry } from './src/type_registry'; diff --git a/packages/core/saved-objects/core-saved-objects-server/src/serialization.ts b/packages/core/saved-objects/core-saved-objects-server/src/serialization.ts index a5fbf87145d8d..e6a7c0553f55a 100644 --- a/packages/core/saved-objects/core-saved-objects-server/src/serialization.ts +++ b/packages/core/saved-objects/core-saved-objects-server/src/serialization.ts @@ -99,7 +99,7 @@ export interface SavedObjectsRawDocSource { * * @public */ -interface SavedObjectDoc { +export interface SavedObjectDoc { attributes: T; id: string; type: string; diff --git a/packages/core/status/core-status-server-internal/src/routes/status.ts b/packages/core/status/core-status-server-internal/src/routes/status.ts index e06d667b4c78b..59c7aa23c51d4 100644 --- a/packages/core/status/core-status-server-internal/src/routes/status.ts +++ b/packages/core/status/core-status-server-internal/src/routes/status.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { Observable, combineLatest, ReplaySubject, firstValueFrom } from 'rxjs'; +import { type Observable, combineLatest, ReplaySubject, firstValueFrom, startWith } from 'rxjs'; import { schema } from '@kbn/config-schema'; import type { PackageInfo } from '@kbn/config'; import type { PluginName } from '@kbn/core-base-common'; @@ -15,12 +15,12 @@ import type { MetricsServiceSetup } from '@kbn/core-metrics-server'; import type { CoreIncrementUsageCounter } from '@kbn/core-usage-data-server'; import type { StatusResponse } from '@kbn/core-status-common-internal'; import { - ServiceStatus, - ServiceStatusLevel, - CoreStatus, + type ServiceStatus, + type ServiceStatusLevel, + type CoreStatus, ServiceStatusLevels, } from '@kbn/core-status-common'; -import { calculateLegacyStatus, LegacyStatusInfo } from '../legacy_status'; +import { calculateLegacyStatus, type LegacyStatusInfo } from '../legacy_status'; const SNAPSHOT_POSTFIX = /-SNAPSHOT$/; @@ -61,6 +61,11 @@ export interface RedactedStatusHttpBody { }; } +const SERVICE_UNAVAILABLE_NOT_REPORTED: ServiceStatus = { + level: ServiceStatusLevels.unavailable, + summary: 'Status not yet reported', +}; + export const registerStatusRoute = ({ router, config, @@ -73,9 +78,17 @@ export const registerStatusRoute = ({ const combinedStatus$ = new ReplaySubject< [ServiceStatus, ServiceStatus, CoreStatus, Record>] >(1); - combineLatest([status.overall$, status.coreOverall$, status.core$, status.plugins$]).subscribe( - combinedStatus$ - ); + combineLatest([ + status.overall$.pipe(startWith(SERVICE_UNAVAILABLE_NOT_REPORTED)), + status.coreOverall$.pipe(startWith(SERVICE_UNAVAILABLE_NOT_REPORTED)), + status.core$.pipe( + startWith({ + elasticsearch: SERVICE_UNAVAILABLE_NOT_REPORTED, + savedObjects: SERVICE_UNAVAILABLE_NOT_REPORTED, + }) + ), + status.plugins$.pipe(startWith({})), + ]).subscribe(combinedStatus$); router.get( { diff --git a/packages/core/test-helpers/core-test-helpers-model-versions/src/test_bed/test_kit.ts b/packages/core/test-helpers/core-test-helpers-model-versions/src/test_bed/test_kit.ts index 93517ea3b33a6..ee33208d793c2 100644 --- a/packages/core/test-helpers/core-test-helpers-model-versions/src/test_bed/test_kit.ts +++ b/packages/core/test-helpers/core-test-helpers-model-versions/src/test_bed/test_kit.ts @@ -9,7 +9,7 @@ import fs from 'fs/promises'; import { defaultsDeep } from 'lodash'; import { BehaviorSubject, firstValueFrom, map } from 'rxjs'; -import { ConfigService, Env } from '@kbn/config'; +import { ConfigService, Env, BuildFlavor } from '@kbn/config'; import { getEnvOptions } from '@kbn/config-mocks'; import { REPO_ROOT } from '@kbn/repo-info'; import { KibanaMigrator } from '@kbn/core-saved-objects-migration-server-internal'; @@ -216,6 +216,7 @@ const getMigrator = async ({ loggerFactory, kibanaVersion, kibanaBranch, + buildFlavor = 'traditional', nodeRoles, }: { configService: ConfigService; @@ -226,6 +227,7 @@ const getMigrator = async ({ loggerFactory: LoggerFactory; kibanaVersion: string; kibanaBranch: string; + buildFlavor?: BuildFlavor; nodeRoles: NodeRoles; }) => { const savedObjectsConf = await firstValueFrom( @@ -237,8 +239,8 @@ const getMigrator = async ({ const soConfig = new SavedObjectConfig(savedObjectsConf, savedObjectsMigrationConf); const docLinks: DocLinksServiceStart = { - ...getDocLinksMeta({ kibanaBranch }), - links: getDocLinks({ kibanaBranch }), + ...getDocLinksMeta({ kibanaBranch, buildFlavor }), + links: getDocLinks({ kibanaBranch, buildFlavor }), }; const esCapabilities = await getCapabilitiesFromClient(client); diff --git a/packages/core/theme/core-theme-browser-internal/src/theme_service.test.ts b/packages/core/theme/core-theme-browser-internal/src/theme_service.test.ts index 9bc01dc3af26e..e9b58ca12ceea 100644 --- a/packages/core/theme/core-theme-browser-internal/src/theme_service.test.ts +++ b/packages/core/theme/core-theme-browser-internal/src/theme_service.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { take } from 'rxjs/operators'; +import { firstValueFrom } from 'rxjs'; import { injectedMetadataServiceMock } from '@kbn/core-injected-metadata-browser-mocks'; import { ThemeService } from './theme_service'; @@ -23,7 +23,16 @@ describe('ThemeService', () => { it('exposes a `theme$` observable with the values provided by the injected metadata', async () => { injectedMetadata.getTheme.mockReturnValue({ version: 'v8', darkMode: true }); const { theme$ } = themeService.setup({ injectedMetadata }); - const theme = await theme$.pipe(take(1)).toPromise(); + const theme = await firstValueFrom(theme$); + expect(theme).toEqual({ + darkMode: true, + }); + }); + + it('#getTheme() returns the current theme', async () => { + injectedMetadata.getTheme.mockReturnValue({ version: 'v8', darkMode: true }); + const setup = themeService.setup({ injectedMetadata }); + const theme = setup.getTheme(); expect(theme).toEqual({ darkMode: true, }); @@ -41,7 +50,17 @@ describe('ThemeService', () => { injectedMetadata.getTheme.mockReturnValue({ version: 'v8', darkMode: true }); themeService.setup({ injectedMetadata }); const { theme$ } = themeService.start(); - const theme = await theme$.pipe(take(1)).toPromise(); + const theme = await firstValueFrom(theme$); + expect(theme).toEqual({ + darkMode: true, + }); + }); + + it('#getTheme() returns the current theme', async () => { + injectedMetadata.getTheme.mockReturnValue({ version: 'v8', darkMode: true }); + themeService.setup({ injectedMetadata }); + const start = themeService.start(); + const theme = start.getTheme(); expect(theme).toEqual({ darkMode: true, }); diff --git a/packages/core/theme/core-theme-browser-internal/src/theme_service.ts b/packages/core/theme/core-theme-browser-internal/src/theme_service.ts index 58ff963c387f9..b4768ec05e550 100644 --- a/packages/core/theme/core-theme-browser-internal/src/theme_service.ts +++ b/packages/core/theme/core-theme-browser-internal/src/theme_service.ts @@ -6,8 +6,7 @@ * Side Public License, v 1. */ -import { Subject, Observable, of } from 'rxjs'; -import { shareReplay, takeUntil } from 'rxjs/operators'; +import { Subject, of } from 'rxjs'; import type { InternalInjectedMetadataSetup } from '@kbn/core-injected-metadata-browser-internal'; import type { CoreTheme, ThemeServiceSetup, ThemeServiceStart } from '@kbn/core-theme-browser'; @@ -18,26 +17,27 @@ export interface ThemeServiceSetupDeps { /** @internal */ export class ThemeService { - private theme$?: Observable; + private contract?: ThemeServiceSetup; private stop$ = new Subject(); public setup({ injectedMetadata }: ThemeServiceSetupDeps): ThemeServiceSetup { - const theme = injectedMetadata.getTheme(); - this.theme$ = of({ darkMode: theme.darkMode }); + const themeMeta = injectedMetadata.getTheme(); + const theme: CoreTheme = { darkMode: themeMeta.darkMode }; - return { - theme$: this.theme$.pipe(takeUntil(this.stop$), shareReplay(1)), + this.contract = { + theme$: of(theme), + getTheme: () => theme, }; + + return this.contract; } public start(): ThemeServiceStart { - if (!this.theme$) { + if (!this.contract) { throw new Error('setup must be called before start'); } - return { - theme$: this.theme$.pipe(takeUntil(this.stop$), shareReplay(1)), - }; + return this.contract; } public stop() { diff --git a/packages/core/theme/core-theme-browser-mocks/src/theme_service.mock.ts b/packages/core/theme/core-theme-browser-mocks/src/theme_service.mock.ts index a4f2321e874a1..3c1ef5fb67479 100644 --- a/packages/core/theme/core-theme-browser-mocks/src/theme_service.mock.ts +++ b/packages/core/theme/core-theme-browser-mocks/src/theme_service.mock.ts @@ -8,7 +8,7 @@ import { of } from 'rxjs'; import type { PublicMethodsOf } from '@kbn/utility-types'; -import type { ThemeServiceSetup, ThemeServiceStart, CoreTheme } from '@kbn/core-theme-browser'; +import type { ThemeServiceSetup, CoreTheme } from '@kbn/core-theme-browser'; import type { ThemeService } from '@kbn/core-theme-browser-internal'; const mockTheme: CoreTheme = { @@ -19,22 +19,16 @@ const createThemeMock = (): CoreTheme => { return { ...mockTheme }; }; -const createTheme$Mock = () => { - return of(createThemeMock()); +const createTheme$Mock = (theme: CoreTheme = createThemeMock()) => { + return of(theme); }; -const createThemeSetupMock = () => { - const setupMock: jest.Mocked = { - theme$: createTheme$Mock(), +const createThemeContractMock = (theme: CoreTheme = createThemeMock()) => { + const themeMock: jest.Mocked = { + theme$: createTheme$Mock(theme), + getTheme: jest.fn().mockReturnValue(theme), }; - return setupMock; -}; - -const createThemeStartMock = () => { - const startMock: jest.Mocked = { - theme$: createTheme$Mock(), - }; - return startMock; + return themeMock; }; type ThemeServiceContract = PublicMethodsOf; @@ -46,16 +40,16 @@ const createServiceMock = () => { stop: jest.fn(), }; - mocked.setup.mockReturnValue(createThemeSetupMock()); - mocked.start.mockReturnValue(createThemeStartMock()); + mocked.setup.mockReturnValue(createThemeContractMock()); + mocked.start.mockReturnValue(createThemeContractMock()); return mocked; }; export const themeServiceMock = { create: createServiceMock, - createSetupContract: createThemeSetupMock, - createStartContract: createThemeStartMock, + createSetupContract: createThemeContractMock, + createStartContract: createThemeContractMock, createTheme: createThemeMock, createTheme$: createTheme$Mock, }; diff --git a/packages/core/theme/core-theme-browser/src/types.ts b/packages/core/theme/core-theme-browser/src/types.ts index 5f8672f3c902c..c3299dbd7b910 100644 --- a/packages/core/theme/core-theme-browser/src/types.ts +++ b/packages/core/theme/core-theme-browser/src/types.ts @@ -22,12 +22,21 @@ export interface CoreTheme { * @public */ export interface ThemeServiceSetup { + /** + * An observable of the currently active theme. + * Note that the observable has a replay effect, so it will emit once during subscriptions. + */ theme$: Observable; + + /** + * Returns the theme currently in use. + * Note that when possible, using the `theme$` observable instead is strongly encouraged, as + * it will allow to react to dynamic theme switch (even if those are not implemented at the moment) + */ + getTheme(): CoreTheme; } /** * @public */ -export interface ThemeServiceStart { - theme$: Observable; -} +export type ThemeServiceStart = ThemeServiceSetup; diff --git a/packages/deeplinks/observability/constants.ts b/packages/deeplinks/observability/constants.ts index a28c9e4aaff21..9f2a28fcef971 100644 --- a/packages/deeplinks/observability/constants.ts +++ b/packages/deeplinks/observability/constants.ts @@ -8,7 +8,7 @@ export const LOGS_APP_ID = 'logs'; -export const OBSERVABILITY_LOG_EXPLORER = 'observability-log-explorer'; +export const OBSERVABILITY_LOG_EXPLORER_APP_ID = 'observability-log-explorer'; export const OBSERVABILITY_OVERVIEW_APP_ID = 'observability-overview'; diff --git a/packages/deeplinks/observability/deep_links.ts b/packages/deeplinks/observability/deep_links.ts index 844d68fdb27cc..831976bcc37ea 100644 --- a/packages/deeplinks/observability/deep_links.ts +++ b/packages/deeplinks/observability/deep_links.ts @@ -7,16 +7,16 @@ */ import { + APM_APP_ID, LOGS_APP_ID, - OBSERVABILITY_LOG_EXPLORER, - OBSERVABILITY_OVERVIEW_APP_ID, METRICS_APP_ID, - APM_APP_ID, + OBSERVABILITY_LOG_EXPLORER_APP_ID, OBSERVABILITY_ONBOARDING_APP_ID, + OBSERVABILITY_OVERVIEW_APP_ID, } from './constants'; type LogsApp = typeof LOGS_APP_ID; -type ObservabilityLogExplorerApp = typeof OBSERVABILITY_LOG_EXPLORER; +type ObservabilityLogExplorerApp = typeof OBSERVABILITY_LOG_EXPLORER_APP_ID; type ObservabilityOverviewApp = typeof OBSERVABILITY_OVERVIEW_APP_ID; type MetricsApp = typeof METRICS_APP_ID; type ApmApp = typeof APM_APP_ID; diff --git a/packages/deeplinks/observability/index.ts b/packages/deeplinks/observability/index.ts index 39608b4b7f7a9..3d816db884765 100644 --- a/packages/deeplinks/observability/index.ts +++ b/packages/deeplinks/observability/index.ts @@ -6,8 +6,11 @@ * Side Public License, v 1. */ -export { OBSERVABILITY_ONBOARDING_APP_ID } from './constants'; - +export { + LOGS_APP_ID, + OBSERVABILITY_LOG_EXPLORER_APP_ID, + OBSERVABILITY_ONBOARDING_APP_ID, + OBSERVABILITY_OVERVIEW_APP_ID, +} from './constants'; export type { AppId, DeepLinkId } from './deep_links'; - export * from './locators'; diff --git a/packages/deeplinks/observability/locators/log_explorer.ts b/packages/deeplinks/observability/locators/log_explorer.ts index 752ae3d79bee8..d20c36e73917d 100644 --- a/packages/deeplinks/observability/locators/log_explorer.ts +++ b/packages/deeplinks/observability/locators/log_explorer.ts @@ -14,6 +14,17 @@ export type RefreshInterval = { value: number; }; +// eslint-disable-next-line @typescript-eslint/consistent-type-definitions +export type FilterControls = { + namespace?: ListFilterControl; +}; + +// eslint-disable-next-line @typescript-eslint/consistent-type-definitions +export type ListFilterControl = { + mode: 'include'; + values: string[]; +}; + export const LOG_EXPLORER_LOCATOR_ID = 'LOG_EXPLORER_LOCATOR'; export interface LogExplorerNavigationParams extends SerializableRecord { @@ -34,13 +45,13 @@ export interface LogExplorerNavigationParams extends SerializableRecord { */ columns?: string[]; /** - * Array of the used sorting [[field,direction],...] + * Optionally apply free-form filters. */ - sort?: string[][]; + filters?: Filter[]; /** - * Optionally apply filters. + * Optionally apply curated filter controls */ - filters?: Filter[]; + filterControls?: FilterControls; } export interface LogExplorerLocatorParams extends LogExplorerNavigationParams { diff --git a/packages/home/sample_data_card/src/__snapshots__/sample_data_card.test.tsx.snap b/packages/home/sample_data_card/src/__snapshots__/sample_data_card.test.tsx.snap index 9f397dc7ccbd7..269499f65547a 100644 --- a/packages/home/sample_data_card/src/__snapshots__/sample_data_card.test.tsx.snap +++ b/packages/home/sample_data_card/src/__snapshots__/sample_data_card.test.tsx.snap @@ -83,33 +83,29 @@ exports[`SampleDataCard installed renders with app links 1`] = ` class="euiFlexItem emotion-euiFlexItem-growZero" >
-
- -
+ + +
diff --git a/packages/home/sample_data_card/src/footer/__snapshots__/view_button.test.tsx.snap b/packages/home/sample_data_card/src/footer/__snapshots__/view_button.test.tsx.snap index ca5d1ccce0739..472852006ca26 100644 --- a/packages/home/sample_data_card/src/footer/__snapshots__/view_button.test.tsx.snap +++ b/packages/home/sample_data_card/src/footer/__snapshots__/view_button.test.tsx.snap @@ -2,65 +2,57 @@ exports[`should render popover when appLinks is not empty 1`] = `
-
- -
+ + +
`; exports[`should render popover with ordered appLinks 1`] = `
-
- -
+ + +
`; diff --git a/packages/kbn-alerts-as-data-utils/src/schemas/generated/stack_schema.ts b/packages/kbn-alerts-as-data-utils/src/schemas/generated/stack_schema.ts index a051eda6519c1..1588963385834 100644 --- a/packages/kbn-alerts-as-data-utils/src/schemas/generated/stack_schema.ts +++ b/packages/kbn-alerts-as-data-utils/src/schemas/generated/stack_schema.ts @@ -70,6 +70,7 @@ const StackAlertRequired = rt.type({ }); const StackAlertOptional = rt.partial({ 'kibana.alert.evaluation.conditions': schemaString, + 'kibana.alert.evaluation.threshold': schemaStringOrNumber, 'kibana.alert.evaluation.value': schemaString, 'kibana.alert.title': schemaString, }); diff --git a/packages/kbn-analytics/package.json b/packages/kbn-analytics/package.json index 128870ecff50f..840ae04e9c324 100644 --- a/packages/kbn-analytics/package.json +++ b/packages/kbn-analytics/package.json @@ -4,5 +4,6 @@ "version": "1.0.0", "description": "Kibana Analytics tool", "author": "Kibana Core", - "license": "SSPL-1.0 OR Elastic License 2.0" + "license": "SSPL-1.0 OR Elastic License 2.0", + "sideEffects": false } \ No newline at end of file diff --git a/packages/kbn-apm-synthtrace-client/src/lib/apm/apm_fields.ts b/packages/kbn-apm-synthtrace-client/src/lib/apm/apm_fields.ts index 7c3ab2c2b1e3a..3ee43dc63f04f 100644 --- a/packages/kbn-apm-synthtrace-client/src/lib/apm/apm_fields.ts +++ b/packages/kbn-apm-synthtrace-client/src/lib/apm/apm_fields.ts @@ -50,6 +50,35 @@ export interface GeoLocation { type: string; } +export interface APMStacktrace { + abs_path?: string; + classname?: string; + context?: { + post?: string[]; + pre?: string[]; + }; + exclude_from_grouping?: boolean; + filename?: string; + function?: string; + module?: string; + library_frame?: boolean; + line?: + | { + column?: number; + number: number; + } + | { + context?: string; + }; + sourcemap?: { + error?: string; + updated?: boolean; + }; + vars?: { + [key: string]: unknown; + }; +} + type ExperimentalFields = Partial<{ 'metricset.interval': string; 'transaction.duration.summary': string; @@ -80,6 +109,8 @@ export type ApmFields = Fields<{ 'cloud.provider': string; 'cloud.region': string; 'cloud.service.name': string; + // otel + 'code.stacktrace': string; 'container.id': string; 'destination.address': string; 'destination.port': number; @@ -169,6 +200,7 @@ export type ApmFields = Fields<{ 'span.duration.us': number; 'span.id': string; 'span.name': string; + 'span.stacktrace': APMStacktrace[]; 'span.self_time.count': number; 'span.self_time.sum.us': number; 'span.subtype': string; diff --git a/packages/kbn-apm-synthtrace-client/src/types/agent_names.ts b/packages/kbn-apm-synthtrace-client/src/types/agent_names.ts index d9e3a371e0e87..c57b15e3dace0 100644 --- a/packages/kbn-apm-synthtrace-client/src/types/agent_names.ts +++ b/packages/kbn-apm-synthtrace-client/src/types/agent_names.ts @@ -31,6 +31,7 @@ type OpenTelemetryAgentName = | 'opentelemetry/python' | 'opentelemetry/ruby' | 'opentelemetry/swift' + | 'opentelemetry/android' | 'opentelemetry/webjs'; // Unable to reference AgentName from '@kbn/apm-plugin/typings/es_schemas/ui/fields/agent' due to circular reference diff --git a/packages/kbn-apm-synthtrace/src/cli/run_synthtrace.ts b/packages/kbn-apm-synthtrace/src/cli/run_synthtrace.ts index 08fab85b04c0d..61b2995a03bc7 100644 --- a/packages/kbn-apm-synthtrace/src/cli/run_synthtrace.ts +++ b/packages/kbn-apm-synthtrace/src/cli/run_synthtrace.ts @@ -8,17 +8,25 @@ import datemath from '@kbn/datemath'; import { Argv } from 'yargs'; import yargs from 'yargs/yargs'; +import { readdirSync } from 'fs'; +import path from 'path'; import { intervalToMs } from './utils/interval_to_ms'; import { parseRunCliFlags } from './utils/parse_run_cli_flags'; import { startHistoricalDataUpload } from './utils/start_historical_data_upload'; import { startLiveDataUpload } from './utils/start_live_data_upload'; +function getBuiltinScenarios() { + return readdirSync(path.resolve(__dirname, '../scenarios')).map((s) => s.replace(/\.ts$/, '')); +} + function options(y: Argv) { return y + .usage('$0 ') .positional('file', { - describe: 'File that contains the trace scenario', + describe: 'Name of scenario', demandOption: true, string: true, + choices: getBuiltinScenarios(), }) .option('target', { describe: 'Elasticsearch target', @@ -54,6 +62,7 @@ function options(y: Argv) { }) .option('logLevel', { describe: 'Log level', + choices: ['trace', 'debug', 'info', 'error'], default: 'info', }) .option('scenarioOpts', { @@ -66,7 +75,14 @@ function options(y: Argv) { describe: 'Assumes passed package version to avoid calling Fleet API to install', string: true, }) - .showHelpOnFail(false); + .example( + '$0 simple_logs --target=http://admin:changeme@localhost:9200', + 'Ingest data to specific Elasticsearch cluster' + ) + .example('$0 simple_logs --live', 'Continuously ingest data to local development cluster') + .example('$0 simple_logs --from=now-24h --to=now', 'Ingest data for a fixed time window') + .showHelpOnFail(false) + .wrap(null); } async function run(argv: RunCliFlags) { diff --git a/packages/kbn-apm-synthtrace/src/scenarios/malformed_logs.ts b/packages/kbn-apm-synthtrace/src/scenarios/degraded_logs.ts similarity index 94% rename from packages/kbn-apm-synthtrace/src/scenarios/malformed_logs.ts rename to packages/kbn-apm-synthtrace/src/scenarios/degraded_logs.ts index 55b32e592d62c..cb5313ac70795 100644 --- a/packages/kbn-apm-synthtrace/src/scenarios/malformed_logs.ts +++ b/packages/kbn-apm-synthtrace/src/scenarios/degraded_logs.ts @@ -29,12 +29,6 @@ const scenario: Scenario = async (runOptions) => { const CLOUD_PROVIDERS = ['gcp', 'aws', 'azure']; const CLOUD_REGION = ['eu-central-1', 'us-east-1', 'area-51']; - // "ignore_above": 1024 in mapping - const MALFORMED_LOG_LEVEL = MORE_THAN_1024_CHARS; - - // "ignore_above": 1024 in mapping - const MALFORMED_CLOUD_REGION = MORE_THAN_1024_CHARS; - const CLUSTER = [ { clusterId: generateShortId(), clusterName: 'synth-cluster-1' }, { clusterId: generateShortId(), clusterName: 'synth-cluster-2' }, @@ -76,7 +70,7 @@ const scenario: Scenario = async (runOptions) => { .create() .dataset('synth.2') .message(MESSAGE_LOG_LEVELS[index].message as string) - .logLevel(isMalformed ? MALFORMED_LOG_LEVEL : MESSAGE_LOG_LEVELS[index].level) + .logLevel(isMalformed ? MORE_THAN_1024_CHARS : MESSAGE_LOG_LEVELS[index].level) // "ignore_above": 1024 in mapping .service(SERVICE_NAMES[index]) .defaults({ 'trace.id': generateShortId(), @@ -101,7 +95,7 @@ const scenario: Scenario = async (runOptions) => { .create() .dataset('synth.3') .message(MESSAGE_LOG_LEVELS[index].message as string) - .logLevel(isMalformed ? MALFORMED_LOG_LEVEL : MESSAGE_LOG_LEVELS[index].level) + .logLevel(isMalformed ? MORE_THAN_1024_CHARS : MESSAGE_LOG_LEVELS[index].level) // "ignore_above": 1024 in mapping .service(SERVICE_NAMES[index]) .defaults({ 'trace.id': generateShortId(), @@ -112,7 +106,7 @@ const scenario: Scenario = async (runOptions) => { 'cloud.provider': CLOUD_PROVIDERS[Math.floor(Math.random() * 3)], 'cloud.region': CLOUD_REGION[index], 'cloud.availability_zone': isMalformed - ? MALFORMED_CLOUD_REGION + ? MORE_THAN_1024_CHARS // "ignore_above": 1024 in mapping : `${CLOUD_REGION[index]}a`, 'cloud.project.id': generateShortId(), 'cloud.instance.id': generateShortId(), diff --git a/packages/kbn-apm-synthtrace/src/scenarios/simple_trace.ts b/packages/kbn-apm-synthtrace/src/scenarios/simple_trace.ts index 26b0c1282d514..c93e37b4f99b3 100644 --- a/packages/kbn-apm-synthtrace/src/scenarios/simple_trace.ts +++ b/packages/kbn-apm-synthtrace/src/scenarios/simple_trace.ts @@ -25,7 +25,7 @@ const scenario: Scenario = async (runOptions) => { const instances = [...Array(numServices).keys()].map((index) => apm - .service({ name: `synth-go-${index}`, environment: ENVIRONMENT, agentName: 'go' }) + .service({ name: `synth-node-${index}`, environment: ENVIRONMENT, agentName: 'nodejs' }) .instance('instance') ); const instanceSpans = (instance: Instance) => { diff --git a/packages/kbn-babel-preset/node_preset.js b/packages/kbn-babel-preset/node_preset.js index 6cd89bdcdecbe..a0c5a0114873c 100644 --- a/packages/kbn-babel-preset/node_preset.js +++ b/packages/kbn-babel-preset/node_preset.js @@ -31,7 +31,7 @@ module.exports = (_, options = {}) => { // Because of that we should use for that value the same version we install // in the package.json in order to have the same polyfills between the environment // and the tests - corejs: '3.31.0', + corejs: '3.34.0', bugfixes: true, ...(options['@babel/preset-env'] || {}), diff --git a/packages/kbn-babel-preset/webpack_preset.js b/packages/kbn-babel-preset/webpack_preset.js index 75dd1b642c29f..84f3771816dfa 100644 --- a/packages/kbn-babel-preset/webpack_preset.js +++ b/packages/kbn-babel-preset/webpack_preset.js @@ -19,7 +19,7 @@ module.exports = (api, options = {}) => { modules: false, // Please read the explanation for this // in node_preset.js - corejs: '3.31.0', + corejs: '3.34.0', bugfixes: true, browserslistEnv: api.env('production') ? 'production' : 'dev', }, diff --git a/packages/kbn-bfetch-error/BUILD.bazel b/packages/kbn-bfetch-error/BUILD.bazel new file mode 100644 index 0000000000000..88cb5bbe5b9e8 --- /dev/null +++ b/packages/kbn-bfetch-error/BUILD.bazel @@ -0,0 +1,35 @@ +load("@build_bazel_rules_nodejs//:index.bzl", "js_library") + +SRCS = glob( + [ + "**/*.ts", + "**/*.tsx", + ], + exclude = [ + "**/test_helpers.ts", + "**/*.config.js", + "**/*.mock.*", + "**/*.test.*", + "**/*.stories.*", + "**/__snapshots__/**", + "**/integration_tests/**", + "**/mocks/**", + "**/scripts/**", + "**/storybook/**", + "**/test_fixtures/**", + "**/test_helpers/**", + ], +) + +BUNDLER_DEPS = [ + "//packages/kbn-i18n", + "@npm//tslib", +] + +js_library( + name = "kbn-bfetch-error", + package_name = "@kbn/bfetch-error", + srcs = ["package.json"] + SRCS, + deps = BUNDLER_DEPS, + visibility = ["//visibility:public"], +) diff --git a/packages/kbn-bfetch-error/README.md b/packages/kbn-bfetch-error/README.md new file mode 100644 index 0000000000000..c44118eef53a6 --- /dev/null +++ b/packages/kbn-bfetch-error/README.md @@ -0,0 +1,3 @@ +# @kbn/bfetch-error + +package isolating befetch error logic diff --git a/packages/kbn-bfetch-error/index.ts b/packages/kbn-bfetch-error/index.ts new file mode 100644 index 0000000000000..9af059ac9d855 --- /dev/null +++ b/packages/kbn-bfetch-error/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 { BfetchRequestError } from './src/bfetch_error'; diff --git a/packages/kbn-bfetch-error/jest.config.js b/packages/kbn-bfetch-error/jest.config.js new file mode 100644 index 0000000000000..fb6554b8e0808 --- /dev/null +++ b/packages/kbn-bfetch-error/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-bfetch-error'], +}; diff --git a/packages/kbn-bfetch-error/kibana.jsonc b/packages/kbn-bfetch-error/kibana.jsonc new file mode 100644 index 0000000000000..2cde90d13d99c --- /dev/null +++ b/packages/kbn-bfetch-error/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/bfetch-error", + "owner": "@elastic/appex-sharedux" +} diff --git a/packages/kbn-bfetch-error/package.json b/packages/kbn-bfetch-error/package.json new file mode 100644 index 0000000000000..539210ff96b26 --- /dev/null +++ b/packages/kbn-bfetch-error/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/bfetch-error", + "private": true, + "version": "1.0.0", + "license": "SSPL-1.0 OR Elastic License 2.0" +} \ No newline at end of file diff --git a/src/plugins/bfetch/common/bfetch_error.ts b/packages/kbn-bfetch-error/src/bfetch_error.ts similarity index 88% rename from src/plugins/bfetch/common/bfetch_error.ts rename to packages/kbn-bfetch-error/src/bfetch_error.ts index 582178f645b2b..be4f80e0dc7e2 100644 --- a/src/plugins/bfetch/common/bfetch_error.ts +++ b/packages/kbn-bfetch-error/src/bfetch_error.ts @@ -20,10 +20,10 @@ export class BfetchRequestError extends Error { constructor(code: number) { const message = code === 0 - ? i18n.translate('bfetch.networkError', { + ? i18n.translate('bfetchError.networkError', { defaultMessage: 'Check your network connection and try again.', }) - : i18n.translate('bfetch.networkErrorWithStatus', { + : i18n.translate('bfetchError.networkErrorWithStatus', { defaultMessage: 'Check your network connection and try again. Code {code}', values: { code }, }); diff --git a/packages/kbn-bfetch-error/tsconfig.json b/packages/kbn-bfetch-error/tsconfig.json new file mode 100644 index 0000000000000..c4703bc51cf6c --- /dev/null +++ b/packages/kbn-bfetch-error/tsconfig.json @@ -0,0 +1,21 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node", + "react" + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx", + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/i18n", + ] +} diff --git a/packages/kbn-check-mappings-update-cli/current_fields.json b/packages/kbn-check-mappings-update-cli/current_fields.json index 7e658e4ec414d..2a8795d77842a 100644 --- a/packages/kbn-check-mappings-update-cli/current_fields.json +++ b/packages/kbn-check-mappings-update-cli/current_fields.json @@ -8,20 +8,10 @@ "targetNamespace", "targetType" ], - "config": [ - "buildNum" - ], - "config-global": [ - "buildNum" - ], - "url": [ - "accessDate", - "createDate", - "slug" - ], - "usage-counters": [ - "domainId" - ], + "config": ["buildNum"], + "config-global": ["buildNum"], + "url": ["accessDate", "createDate", "slug"], + "usage-counters": ["domainId"], "task": [ "attempts", "enabled", @@ -35,33 +25,15 @@ "status", "taskType" ], - "guided-onboarding-guide-state": [ - "guideId", - "isActive" - ], + "guided-onboarding-guide-state": ["guideId", "isActive"], "guided-onboarding-plugin-state": [], - "ui-metric": [ - "count" - ], + "ui-metric": ["count"], "application_usage_totals": [], - "application_usage_daily": [ - "timestamp" - ], - "event_loop_delays_daily": [ - "lastUpdatedAt" - ], - "index-pattern": [ - "name", - "title", - "type" - ], - "sample-data-telemetry": [ - "installCount", - "unInstallCount" - ], - "space": [ - "name" - ], + "application_usage_daily": ["timestamp"], + "event_loop_delays_daily": ["lastUpdatedAt"], + "index-pattern": ["name", "title", "type"], + "sample-data-telemetry": ["installCount", "unInstallCount"], + "space": ["name"], "spaces-usage-stats": [], "exception-list-agnostic": [ "_tags", @@ -155,44 +127,17 @@ "size", "user" ], - "fileShare": [ - "created", - "name", - "token", - "valid_until" - ], - "action": [ - "actionTypeId", - "name" - ], + "fileShare": ["created", "name", "token", "valid_until"], + "action": ["actionTypeId", "name"], "action_task_params": [], - "connector_token": [ - "connectorId", - "tokenType" - ], - "query": [ - "description", - "title" - ], + "connector_token": ["connectorId", "tokenType"], + "query": ["description", "title"], "kql-telemetry": [], - "search-session": [ - "created", - "realmName", - "realmType", - "sessionId", - "username" - ], + "search-session": ["created", "realmName", "realmType", "sessionId", "username"], "search-telemetry": [], - "file-upload-usage-collection-telemetry": [ - "file_upload", - "file_upload.index_creation_count" - ], + "file-upload-usage-collection-telemetry": ["file_upload", "file_upload.index_creation_count"], "apm-indices": [], - "tag": [ - "color", - "description", - "name" - ], + "tag": ["color", "description", "name"], "alert": [ "actions", "actions.actionRef", @@ -261,17 +206,9 @@ "updatedAt", "updatedBy" ], - "api_key_pending_invalidation": [ - "apiKeyId", - "createdAt" - ], - "rules-settings": [ - "flapping" - ], - "maintenance-window": [ - "enabled", - "events" - ], + "api_key_pending_invalidation": ["apiKeyId", "createdAt"], + "rules-settings": ["flapping"], + "maintenance-window": ["enabled", "events"], "graph-workspace": [ "description", "kibanaSavedObjectMeta", @@ -283,39 +220,12 @@ "version", "wsState" ], - "search": [ - "description", - "title" - ], - "visualization": [ - "description", - "kibanaSavedObjectMeta", - "title", - "version" - ], - "canvas-element": [ - "@created", - "@timestamp", - "content", - "help", - "image", - "name" - ], - "canvas-workpad": [ - "@created", - "@timestamp", - "name" - ], - "canvas-workpad-template": [ - "help", - "name", - "tags", - "template_key" - ], - "event-annotation-group": [ - "description", - "title" - ], + "search": ["description", "title"], + "visualization": ["description", "kibanaSavedObjectMeta", "title", "version"], + "canvas-element": ["@created", "@timestamp", "content", "help", "image", "name"], + "canvas-workpad": ["@created", "@timestamp", "name"], + "canvas-workpad-template": ["help", "name", "tags", "template_key"], + "event-annotation-group": ["description", "title"], "dashboard": [ "controlGroupInput", "controlGroupInput.chainingSystem", @@ -339,23 +249,9 @@ "title", "version" ], - "links": [ - "description", - "links", - "title" - ], - "lens": [ - "description", - "state", - "title", - "visualizationType" - ], - "lens-ui-telemetry": [ - "count", - "date", - "name", - "type" - ], + "links": ["description", "links", "title"], + "lens": ["description", "state", "title", "visualizationType"], + "lens-ui-telemetry": ["count", "date", "name", "type"], "map": [ "bounds", "description", @@ -380,14 +276,8 @@ "type", "updated_at" ], - "cases-configure": [ - "closure_type", - "created_at", - "owner" - ], - "cases-connector-mappings": [ - "owner" - ], + "cases-configure": ["closure_type", "created_at", "owner"], + "cases-connector-mappings": ["owner"], "cases": [ "assignees", "assignees.uid", @@ -461,9 +351,7 @@ "type" ], "cases-telemetry": [], - "infrastructure-monitoring-log-view": [ - "name" - ], + "infrastructure-monitoring-log-view": ["name"], "metrics-data-source": [], "ingest_manager_settings": [ "fleet_server_hosts", @@ -528,6 +416,7 @@ "output_id", "partition", "password", + "preset", "proxy_id", "random", "random.group_events", @@ -616,23 +505,9 @@ "package_name", "package_version" ], - "fleet-preconfiguration-deletion-record": [ - "id" - ], - "ingest-download-sources": [ - "host", - "is_default", - "name", - "proxy_id", - "source_id" - ], - "fleet-fleet-server-host": [ - "host_urls", - "is_default", - "is_preconfigured", - "name", - "proxy_id" - ], + "fleet-preconfiguration-deletion-record": ["id"], + "ingest-download-sources": ["host", "is_default", "name", "proxy_id", "source_id"], + "fleet-fleet-server-host": ["host_urls", "is_default", "is_preconfigured", "name", "proxy_id"], "fleet-proxy": [ "certificate", "certificate_authorities", @@ -643,14 +518,8 @@ "url" ], "fleet-message-signing-keys": [], - "fleet-uninstall-tokens": [ - "policy_id", - "token_plain" - ], - "osquery-manager-usage-metric": [ - "count", - "errors" - ], + "fleet-uninstall-tokens": ["policy_id", "token_plain"], + "osquery-manager-usage-metric": ["count", "errors"], "osquery-saved-query": [ "created_at", "created_by", @@ -720,25 +589,13 @@ "indicator.params", "indicator.type", "name", - "tags" + "tags", + "version" ], "threshold-explorer-view": [], - "observability-onboarding-state": [ - "progress", - "state", - "type" - ], - "ml-job": [ - "datafeed_id", - "job_id", - "type" - ], - "ml-trained-model": [ - "job", - "job.create_time", - "job.job_id", - "model_id" - ], + "observability-onboarding-state": ["progress", "state", "type"], + "ml-job": ["datafeed_id", "job_id", "type"], + "ml-trained-model": ["job", "job.create_time", "job.job_id", "model_id"], "ml-module": [ "datafeeds", "defaultIndexPattern", @@ -779,41 +636,19 @@ "type", "urls" ], - "uptime-synthetics-api-key": [ - "apiKey" - ], + "uptime-synthetics-api-key": ["apiKey"], "synthetics-param": [], "infrastructure-ui-source": [], "inventory-view": [], "metrics-explorer-view": [], - "upgrade-assistant-reindex-operation": [ - "indexName", - "status" - ], - "upgrade-assistant-ml-upgrade-operation": [ - "snapshotId" - ], - "monitoring-telemetry": [ - "reportedClusterUuids" - ], + "upgrade-assistant-reindex-operation": ["indexName", "status"], + "upgrade-assistant-ml-upgrade-operation": ["snapshotId"], + "monitoring-telemetry": ["reportedClusterUuids"], "enterprise_search_telemetry": [], "app_search_telemetry": [], "workplace_search_telemetry": [], - "siem-ui-timeline-note": [ - "created", - "createdBy", - "eventId", - "note", - "updated", - "updatedBy" - ], - "siem-ui-timeline-pinned-event": [ - "created", - "createdBy", - "eventId", - "updated", - "updatedBy" - ], + "siem-ui-timeline-note": ["created", "createdBy", "eventId", "note", "updated", "updatedBy"], + "siem-ui-timeline-pinned-event": ["created", "createdBy", "eventId", "updated", "updatedBy"], "siem-detection-engine-rule-actions": [ "actions", "actions.actionRef", @@ -825,10 +660,7 @@ "ruleAlertId", "ruleThrottle" ], - "security-rule": [ - "rule_id", - "version" - ], + "security-rule": ["rule_id", "version"], "siem-ui-timeline": [ "columns", "columns.aggregatable", @@ -928,15 +760,8 @@ "updated", "updatedBy" ], - "endpoint:user-artifact-manifest": [ - "artifacts", - "schemaVersion" - ], - "security-solution-signals-migration": [ - "sourceIndex", - "updated", - "version" - ], + "endpoint:user-artifact-manifest": ["artifacts", "schemaVersion"], + "security-solution-signals-migration": ["sourceIndex", "updated", "version"], "risk-engine-configuration": [ "dataViewId", "enabled", @@ -948,32 +773,16 @@ "range.end", "range.start" ], - "policy-settings-protection-updates-note": [ - "note" - ], + "policy-settings-protection-updates-note": ["note"], "apm-telemetry": [], - "apm-server-schema": [ - "schemaJson" - ], - "apm-service-group": [ - "color", - "description", - "groupName", - "kuery" - ], + "apm-server-schema": ["schemaJson"], + "apm-service-group": ["color", "description", "groupName", "kuery"], "apm-custom-dashboards": [ "dashboardSavedObjectId", "kuery", "serviceEnvironmentFilterEnabled", "serviceNameFilterEnabled" ], - "cases-oracle": [ - "cases", - "cases.id", - "counter", - "createdAt", - "rules", - "rules.id", - "updatedAt" - ] + "cloud-security-posture-settings": ["rules"], + "cases-oracle": ["cases", "cases.id", "counter", "createdAt", "rules", "rules.id", "updatedAt"] } diff --git a/packages/kbn-check-mappings-update-cli/current_mappings.json b/packages/kbn-check-mappings-update-cli/current_mappings.json index c4258d6c86cd1..94fcd545a88ac 100644 --- a/packages/kbn-check-mappings-update-cli/current_mappings.json +++ b/packages/kbn-check-mappings-update-cli/current_mappings.json @@ -1882,6 +1882,10 @@ } } } + }, + "preset": { + "type": "keyword", + "index": false } } }, @@ -2373,6 +2377,10 @@ } } }, + "cloud-security-posture-settings": { + "dynamic": false, + "properties": {} + }, "slo": { "dynamic": false, "properties": { @@ -2403,6 +2411,9 @@ }, "tags": { "type": "keyword" + }, + "version": { + "type": "long" } } }, diff --git a/packages/kbn-code-owners/README.md b/packages/kbn-code-owners/README.md new file mode 100644 index 0000000000000..b25944e2efbe6 --- /dev/null +++ b/packages/kbn-code-owners/README.md @@ -0,0 +1,3 @@ +# @kbn/code-owners + +This package contains utility methods to determine GitHub code ownership for files in the repository. diff --git a/packages/kbn-code-owners/index.ts b/packages/kbn-code-owners/index.ts new file mode 100644 index 0000000000000..0b2cd53a0b3a9 --- /dev/null +++ b/packages/kbn-code-owners/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export type { PathWithOwners } from './src/file_code_owner'; +export { getPathsWithOwnersReversed, getCodeOwnersForFile } from './src/file_code_owner'; diff --git a/packages/kbn-code-owners/jest.config.js b/packages/kbn-code-owners/jest.config.js new file mode 100644 index 0000000000000..471e87b73b161 --- /dev/null +++ b/packages/kbn-code-owners/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../..', + roots: ['/packages/kbn-code-owners'], +}; diff --git a/packages/kbn-code-owners/kibana.jsonc b/packages/kbn-code-owners/kibana.jsonc new file mode 100644 index 0000000000000..66d2e57ca15c1 --- /dev/null +++ b/packages/kbn-code-owners/kibana.jsonc @@ -0,0 +1,6 @@ +{ + "type": "shared-common", + "id": "@kbn/code-owners", + "owner": "@elastic/appex-qa", + "devOnly": true +} diff --git a/packages/kbn-code-owners/package.json b/packages/kbn-code-owners/package.json new file mode 100644 index 0000000000000..38a7c77800d0e --- /dev/null +++ b/packages/kbn-code-owners/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/code-owners", + "private": true, + "version": "1.0.0", + "license": "SSPL-1.0 OR Elastic License 2.0" +} \ No newline at end of file diff --git a/packages/kbn-code-owners/src/file_code_owner.ts b/packages/kbn-code-owners/src/file_code_owner.ts new file mode 100644 index 0000000000000..56f3c54ef1603 --- /dev/null +++ b/packages/kbn-code-owners/src/file_code_owner.ts @@ -0,0 +1,67 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { REPO_ROOT } from '@kbn/repo-info'; +import { createFailError } from '@kbn/dev-cli-errors'; +import { join as joinPath } from 'path'; +import { existsSync, readFileSync } from 'fs'; + +import type { Ignore } from 'ignore'; +import ignore from 'ignore'; + +export interface PathWithOwners { + path: string; + teams: string; + ignorePattern: Ignore; +} + +/** + * Get the .github/CODEOWNERS entries, prepared for path matching. + * The last matching CODEOWNERS entry has highest precedence: + * https://help.github.com/articles/about-codeowners/ + * so entries are returned in reversed order to later search for the first match. + */ +export function getPathsWithOwnersReversed(): PathWithOwners[] { + const codeownersPath = joinPath(REPO_ROOT, '.github', 'CODEOWNERS'); + if (existsSync(codeownersPath) === false) { + throw createFailError(`Unable to determine code owners: file ${codeownersPath} not found`); + } + const codeownersContent = readFileSync(codeownersPath, { encoding: 'utf8', flag: 'r' }); + const codeownersLines = codeownersContent.split(/\r?\n/); + const codeowners = codeownersLines + .map((line) => line.trim()) + .filter((line) => line && line[0] !== '#'); + + const pathsWithOwners: PathWithOwners[] = codeowners.map((c) => { + const [path, ...ghTeams] = c.split(/\s+/); + return { + path, + teams: ghTeams.map((t) => t.replace('@', '')).join(), + // register CODEOWNERS entries with the `ignores` lib for later path matching + ignorePattern: ignore().add([path]), + }; + }); + + return pathsWithOwners.reverse(); +} + +/** + * Get the GitHub CODEOWNERS for a file in the repository + * @param filePath the file to get code owners for + * @param reversedCodeowners a cached reversed code owners list, use to speed up multiple requests + */ +export function getCodeOwnersForFile( + filePath: string, + reversedCodeowners?: PathWithOwners[] +): string | undefined { + const pathsWithOwners = reversedCodeowners ?? getPathsWithOwnersReversed(); + + const match = pathsWithOwners.find((p) => p.ignorePattern.test(filePath).ignored); + + return match?.teams; +} diff --git a/packages/kbn-code-owners/tsconfig.json b/packages/kbn-code-owners/tsconfig.json new file mode 100644 index 0000000000000..e97f927147d73 --- /dev/null +++ b/packages/kbn-code-owners/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts", + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/repo-info", + "@kbn/dev-cli-errors" + ] +} diff --git a/packages/kbn-config-schema/src/internals/index.ts b/packages/kbn-config-schema/src/internals/index.ts index 5ab5bd6a9ef02..4de64ab7fdd82 100644 --- a/packages/kbn-config-schema/src/internals/index.ts +++ b/packages/kbn-config-schema/src/internals/index.ts @@ -153,6 +153,46 @@ export const internals: JoiRoot = Joi.extend( } return { value }; }, + rules: { + min: { + args: [ + { + name: 'limit', + assert: Joi.alternatives([Joi.number(), Joi.string()]).required(), + }, + ], + method(limit) { + return this.$_addRule({ name: 'min', args: { limit } }); + }, + validate(value, { error }, args) { + const limit = ensureDuration(args.limit); + if (value.asMilliseconds() < limit.asMilliseconds()) { + return error('duration.min', { value, limit }); + } + + return value; + }, + }, + max: { + args: [ + { + name: 'limit', + assert: Joi.alternatives([Joi.number(), Joi.string()]).required(), + }, + ], + method(limit) { + return this.$_addRule({ name: 'max', args: { limit } }); + }, + validate(value, { error }, args) { + const limit = ensureDuration(args.limit); + if (value.asMilliseconds() > limit.asMilliseconds()) { + return error('duration.max', { value, limit }); + } + + return value; + }, + }, + }, }, { type: 'number', diff --git a/packages/kbn-config-schema/src/types/duration_type.test.ts b/packages/kbn-config-schema/src/types/duration_type.test.ts index ef52e36c6799a..18327b65cd1f6 100644 --- a/packages/kbn-config-schema/src/types/duration_type.test.ts +++ b/packages/kbn-config-schema/src/types/duration_type.test.ts @@ -8,6 +8,7 @@ import { duration as momentDuration } from 'moment'; import { schema } from '../..'; +import { ensureDuration } from '../duration'; const { duration, object, contextRef, siblingRef } = schema; @@ -135,6 +136,28 @@ describe('#defaultValue', () => { }); }); +describe('#min', () => { + it('returns the value when larger', () => { + expect(duration({ min: '5m' }).validate('7m')).toEqual(ensureDuration('7m')); + }); + it('throws error when value is smaller', () => { + expect(() => duration({ min: '5m' }).validate('3m')).toThrowErrorMatchingInlineSnapshot( + `"Value must be equal to or greater than [PT5M]"` + ); + }); +}); + +describe('#max', () => { + it('returns the value when smaller', () => { + expect(duration({ max: '10d' }).validate('7d')).toEqual(ensureDuration('7d')); + }); + it('throws error when value is greater', () => { + expect(() => duration({ max: '10h' }).validate('17h')).toThrowErrorMatchingInlineSnapshot( + `"Value must be equal to or less than [PT10H]"` + ); + }); +}); + test('returns error when not valid string or non-safe positive integer', () => { expect(() => duration().validate(-123)).toThrowErrorMatchingInlineSnapshot( `"Value in milliseconds is expected to be a safe positive integer."` diff --git a/packages/kbn-config-schema/src/types/duration_type.ts b/packages/kbn-config-schema/src/types/duration_type.ts index 2d469f0ca6ac0..561ac9cfa90a5 100644 --- a/packages/kbn-config-schema/src/types/duration_type.ts +++ b/packages/kbn-config-schema/src/types/duration_type.ts @@ -13,12 +13,14 @@ import { internals } from '../internals'; import { Reference } from '../references'; import { Type } from './type'; -type DurationValueType = Duration | string | number; +export type DurationValueType = Duration | string | number; export interface DurationOptions { // we need to special-case defaultValue as we want to handle string inputs too defaultValue?: DurationValueType | Reference | (() => DurationValueType); validate?: (value: Duration) => string | void; + min?: DurationValueType; + max?: DurationValueType; } export class DurationType extends Type { @@ -36,16 +38,32 @@ export class DurationType extends Type { defaultValue = options.defaultValue; } - super(internals.duration(), { ...options, defaultValue }); + let schema = internals.duration(); + if (options.min) { + schema = schema.min(options.min); + } + if (options.max) { + schema = schema.max(options.max); + } + + super(schema, { validate: options.validate, defaultValue }); } - protected handleError(type: string, { message, value }: Record, path: string[]) { + protected handleError( + type: string, + { message, value, limit }: Record, + path: string[] + ) { switch (type) { case 'any.required': case 'duration.base': return `expected value of type [moment.Duration] but got [${typeDetect(value)}]`; case 'duration.parse': return new SchemaTypeError(message, path); + case 'duration.min': + return `Value must be equal to or greater than [${limit.toString()}]`; + case 'duration.max': + return `Value must be equal to or less than [${limit.toString()}]`; } } } diff --git a/packages/kbn-config-schema/types/joi.d.ts b/packages/kbn-config-schema/types/joi.d.ts index 5dd695cb05e88..f6e14fdea5dca 100644 --- a/packages/kbn-config-schema/types/joi.d.ts +++ b/packages/kbn-config-schema/types/joi.d.ts @@ -7,7 +7,8 @@ */ import * as Joi from 'joi'; -import { ByteSizeValue } from '../src/byte_size_value'; +import type { ByteSizeValue } from '../src/byte_size_value'; +import type { DurationValueType } from '../src/types/duration_type'; declare module 'joi' { interface BytesSchema extends AnySchema { @@ -16,6 +17,12 @@ declare module 'joi' { max(limit: number | string | ByteSizeValue): this; } + interface DurationSchema extends AnySchema { + min(limit: DurationValueType): this; + + max(limit: DurationValueType): this; + } + interface MapSchema extends AnySchema { entries(key: AnySchema, value: AnySchema): this; } @@ -34,7 +41,7 @@ declare module 'joi' { export type JoiRoot = Joi.Root & { bytes: () => BytesSchema; - duration: () => AnySchema; + duration: () => DurationSchema; map: () => MapSchema; record: () => RecordSchema; stream: () => AnySchema; diff --git a/packages/kbn-config/index.ts b/packages/kbn-config/index.ts index 4950e3f68beed..1b4c66702d9e8 100644 --- a/packages/kbn-config/index.ts +++ b/packages/kbn-config/index.ts @@ -30,4 +30,4 @@ export { isConfigPath, hasConfigPathIntersection } from './src/config'; export { ObjectToConfigAdapter } from './src/object_to_config_adapter'; export type { CliArgs, RawPackageInfo, EnvOptions } from './src/env'; export { Env } from './src/env'; -export type { EnvironmentMode, PackageInfo } from './src/types'; +export type { EnvironmentMode, PackageInfo, BuildFlavor } from './src/types'; diff --git a/packages/kbn-config/src/config_service.ts b/packages/kbn-config/src/config_service.ts index e1c4ccfb55fbe..56b6f1b7887cf 100644 --- a/packages/kbn-config/src/config_service.ts +++ b/packages/kbn-config/src/config_service.ts @@ -73,7 +73,10 @@ export class ConfigService { ) { this.log = logger.get('config'); this.deprecationLog = logger.get('config', 'deprecation'); - this.docLinks = getDocLinks({ kibanaBranch: env.packageInfo.branch }); + this.docLinks = getDocLinks({ + kibanaBranch: env.packageInfo.branch, + buildFlavor: env.packageInfo.buildFlavor, + }); this.config$ = combineLatest([ this.rawConfigProvider.getConfig$(), diff --git a/packages/kbn-crypto-browser/package.json b/packages/kbn-crypto-browser/package.json index 6838d31a7a6ba..682e2f99a2b91 100644 --- a/packages/kbn-crypto-browser/package.json +++ b/packages/kbn-crypto-browser/package.json @@ -2,5 +2,6 @@ "name": "@kbn/crypto-browser", "private": true, "version": "1.0.0", - "license": "SSPL-1.0 OR Elastic License 2.0" + "license": "SSPL-1.0 OR Elastic License 2.0", + "sideEffects": false } \ No newline at end of file diff --git a/packages/kbn-custom-integrations/package.json b/packages/kbn-custom-integrations/package.json index 80b3bc267e5a6..bc653a7c3de01 100644 --- a/packages/kbn-custom-integrations/package.json +++ b/packages/kbn-custom-integrations/package.json @@ -2,5 +2,6 @@ "name": "@kbn/custom-integrations", "private": true, "version": "1.0.0", - "license": "SSPL-1.0 OR Elastic License 2.0" + "license": "SSPL-1.0 OR Elastic License 2.0", + "sideEffects": false } \ No newline at end of file diff --git a/packages/kbn-doc-links/src/get_doc_links.test.ts b/packages/kbn-doc-links/src/get_doc_links.test.ts index 0fff33ebd3fe4..60ce89266afc4 100644 --- a/packages/kbn-doc-links/src/get_doc_links.test.ts +++ b/packages/kbn-doc-links/src/get_doc_links.test.ts @@ -10,7 +10,7 @@ import { getDocLinks } from './get_doc_links'; describe('getDocLinks', () => { it('returns an immutable object', () => { - const links = getDocLinks({ kibanaBranch: 'test.branch' }); + const links = getDocLinks({ kibanaBranch: 'test.branch', buildFlavor: 'traditional' }); expect(() => { (links as unknown as Record).settings = 'override'; diff --git a/packages/kbn-doc-links/src/get_doc_links.ts b/packages/kbn-doc-links/src/get_doc_links.ts index b22e091b5206a..a259d76c6affb 100644 --- a/packages/kbn-doc-links/src/get_doc_links.ts +++ b/packages/kbn-doc-links/src/get_doc_links.ts @@ -7,15 +7,16 @@ */ import { deepFreeze } from '@kbn/std'; -import type { DocLinks } from './types'; +import type { DocLinks, BuildFlavor } from './types'; import { getDocLinksMeta } from './get_doc_meta'; export interface GetDocLinkOptions { kibanaBranch: string; + buildFlavor: BuildFlavor; } -export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { - const meta = getDocLinksMeta({ kibanaBranch }); +export const getDocLinks = ({ kibanaBranch, buildFlavor }: GetDocLinkOptions): DocLinks => { + const meta = getDocLinksMeta({ kibanaBranch, buildFlavor }); const DOC_LINK_VERSION = meta.version; const ELASTIC_WEBSITE_URL = meta.elasticWebsiteUrl; @@ -38,6 +39,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { const SERVERLESS_DOCS = `${DOCS_WEBSITE_URL}serverless/`; const SERVERLESS_ELASTICSEARCH_DOCS = `${SERVERLESS_DOCS}elasticsearch/`; const SEARCH_LABS_REPO = `${ELASTIC_GITHUB}elasticsearch-labs/`; + const isServerless = buildFlavor === 'serverless'; return deepFreeze({ settings: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/settings.html`, @@ -315,7 +317,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { luceneExpressions: `${ELASTICSEARCH_DOCS}modules-scripting-expression.html`, }, indexPatterns: { - introduction: `${KIBANA_DOCS}data-views.html`, + introduction: isServerless ? `${SERVERLESS_DOCS}data-views` : `${KIBANA_DOCS}data-views.html`, fieldFormattersNumber: `${KIBANA_DOCS}numeral.html`, fieldFormattersString: `${KIBANA_DOCS}managing-data-views.html#string-field-formatters`, runtimeFields: `${KIBANA_DOCS}managing-data-views.html#runtime-fields`, @@ -462,7 +464,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { riskScorePrerequisites: `${SECURITY_SOLUTION_DOCS}ers-requirements.html`, hostRiskScore: `${SECURITY_SOLUTION_DOCS}host-risk-score.html`, userRiskScore: `${SECURITY_SOLUTION_DOCS}user-risk-score.html`, - entityRiskScoring: `${SECURITY_SOLUTION_DOCS}advanced-entity-analytics-overview.html`, + entityRiskScoring: `${SECURITY_SOLUTION_DOCS}entity-risk-scoring.html`, }, detectionEngineOverview: `${SECURITY_SOLUTION_DOCS}detection-engine-overview.html`, }, @@ -485,7 +487,9 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { }, management: { dashboardSettings: `${KIBANA_DOCS}advanced-options.html#kibana-dashboard-settings`, - indexManagement: `${ELASTICSEARCH_DOCS}index-mgmt.html`, + indexManagement: isServerless + ? `${SERVERLESS_DOCS}index-management` + : `${ELASTICSEARCH_DOCS}index-mgmt.html`, kibanaSearchSettings: `${KIBANA_DOCS}advanced-options.html#kibana-search-settings`, discoverSettings: `${KIBANA_DOCS}advanced-options.html#kibana-discover-settings`, visualizationSettings: `${KIBANA_DOCS}advanced-options.html#kibana-visualization-settings`, @@ -528,7 +532,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { nlpImportModel: `${MACHINE_LEARNING_DOCS}ml-nlp-import-model.html`, }, transforms: { - guide: `${ELASTICSEARCH_DOCS}transforms.html`, + guide: isServerless ? `${SERVERLESS_DOCS}transforms` : `${ELASTICSEARCH_DOCS}transforms.html`, alertingRules: `${ELASTICSEARCH_DOCS}transform-alerts.html`, }, visualize: { @@ -561,8 +565,12 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { sloBurnRateRule: `${ELASTIC_WEBSITE_URL}guide/en/observability/${DOC_LINK_VERSION}/slo-burn-rate-alert.html`, }, alerting: { - guide: `${KIBANA_DOCS}create-and-manage-rules.html`, - actionTypes: `${KIBANA_DOCS}action-types.html`, + guide: isServerless + ? `${SERVERLESS_DOCS}rules` + : `${KIBANA_DOCS}create-and-manage-rules.html`, + actionTypes: isServerless + ? `${SERVERLESS_DOCS}action-connectors` + : `${KIBANA_DOCS}action-types.html`, apmRules: `${KIBANA_DOCS}apm-alerts.html`, emailAction: `${KIBANA_DOCS}email-action-type.html`, emailActionConfig: `${KIBANA_DOCS}email-action-type.html`, @@ -572,7 +580,9 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { indexAction: `${KIBANA_DOCS}index-action-type.html`, esQuery: `${KIBANA_DOCS}rule-type-es-query.html`, indexThreshold: `${KIBANA_DOCS}rule-type-index-threshold.html`, - maintenanceWindows: `${KIBANA_DOCS}maintenance-windows.html`, + maintenanceWindows: isServerless + ? `${SERVERLESS_DOCS}maintenance-windows` + : `${KIBANA_DOCS}maintenance-windows.html`, pagerDutyAction: `${KIBANA_DOCS}pagerduty-action-type.html`, preconfiguredConnectors: `${KIBANA_DOCS}pre-configured-connectors.html`, preconfiguredAlertHistoryConnector: `${KIBANA_DOCS}pre-configured-connectors.html#preconfigured-connector-alert-history`, @@ -589,7 +599,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { }, maps: { connectToEms: `${KIBANA_DOCS}maps-connect-to-ems.html`, - guide: `${KIBANA_DOCS}maps.html`, + guide: isServerless ? `${SERVERLESS_DOCS}maps` : `${KIBANA_DOCS}maps.html`, importGeospatialPrivileges: `${KIBANA_DOCS}import-geospatial-data.html#import-geospatial-privileges`, gdalTutorial: `${ELASTIC_WEBSITE_URL}blog/how-to-ingest-geospatial-data-into-elasticsearch-with-gdal`, termJoinsExample: `${KIBANA_DOCS}terms-join.html#_example_term_join`, @@ -661,6 +671,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { cronExpressions: `${ELASTICSEARCH_DOCS}cron-expressions.html`, executeWatchActionModes: `${ELASTICSEARCH_DOCS}watcher-api-execute-watch.html#watcher-api-execute-watch-action-mode`, indexExists: `${ELASTICSEARCH_DOCS}indices-exists.html`, + inferTrainedModel: `${ELASTICSEARCH_DOCS}infer-trained-model.html`, multiSearch: `${ELASTICSEARCH_DOCS}search-multi-search.html`, openIndex: `${ELASTICSEARCH_DOCS}indices-open-close.html`, putComponentTemplate: `${ELASTICSEARCH_DOCS}indices-component-template.html`, @@ -729,7 +740,9 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { kv: `${ELASTICSEARCH_DOCS}kv-processor.html`, lowercase: `${ELASTICSEARCH_DOCS}lowercase-processor.html`, pipeline: `${ELASTICSEARCH_DOCS}pipeline-processor.html`, - pipelines: `${ELASTICSEARCH_DOCS}ingest.html`, + pipelines: isServerless + ? `${SERVERLESS_DOCS}ingest-pipelines` + : `${ELASTICSEARCH_DOCS}ingest.html`, csvPipelines: `${ELASTIC_WEBSITE_URL}guide/en/ecs/${DOC_LINK_VERSION}/ecs-converting.html`, pipelineFailure: `${ELASTICSEARCH_DOCS}ingest.html#handling-pipeline-failures`, processors: `${ELASTICSEARCH_DOCS}processors.html`, @@ -769,7 +782,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { installElasticAgentStandalone: `${FLEET_DOCS}install-standalone-elastic-agent.html`, upgradeElasticAgent: `${FLEET_DOCS}upgrade-elastic-agent.html`, learnMoreBlog: `${ELASTIC_WEBSITE_URL}blog/elastic-agent-and-fleet-make-it-easier-to-integrate-your-systems-with-elastic`, - apiKeysLearnMore: `${KIBANA_DOCS}api-keys.html`, + apiKeysLearnMore: isServerless ? `${SERVERLESS_DOCS}api-keys` : `${KIBANA_DOCS}api-keys.html`, onPremRegistry: `${FLEET_DOCS}air-gapped.html`, packageSignatures: `${FLEET_DOCS}package-signatures.html`, secureLogstash: `${FLEET_DOCS}secure-logstash-connections.html`, @@ -780,6 +793,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { elasticAgentInputConfiguration: `${FLEET_DOCS}elastic-agent-input-configuration.html`, policySecrets: `${FLEET_DOCS}agent-policy.html#agent-policy-secret-values`, remoteESOoutput: `${FLEET_DOCS}monitor-elastic-agent.html#external-elasticsearch-monitoring`, + performancePresets: `${FLEET_DOCS}es-output-settings.html#es-output-settings-performance-tuning-settings`, }, ecs: { guide: `${ELASTIC_WEBSITE_URL}guide/en/ecs/current/index.html`, @@ -822,7 +836,7 @@ export const getDocLinks = ({ kibanaBranch }: GetDocLinkOptions): DocLinks => { rubyOverview: `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/client/ruby-api/${DOC_LINK_VERSION}/ruby_client.html`, rustGuide: `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/client/rust-api/${DOC_LINK_VERSION}/index.html`, rustOverview: `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/client/rust-api/${DOC_LINK_VERSION}/overview.html`, - eland: `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/client/eland/${DOC_LINK_VERSION}/index.html`, + eland: `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/client/eland/current/index.html`, }, endpoints: { troubleshooting: `${SECURITY_SOLUTION_DOCS}ts-management.html#ts-endpoints`, diff --git a/packages/kbn-doc-links/src/get_doc_meta.test.ts b/packages/kbn-doc-links/src/get_doc_meta.test.ts index ec98fc5345a06..28685ee654a18 100644 --- a/packages/kbn-doc-links/src/get_doc_meta.test.ts +++ b/packages/kbn-doc-links/src/get_doc_meta.test.ts @@ -10,16 +10,20 @@ import { getDocLinksMeta } from './get_doc_meta'; describe('getDocLinksMeta', () => { it('returns the correct version for the `main` branch', () => { - expect(getDocLinksMeta({ kibanaBranch: 'main' }).version).toEqual('master'); + expect(getDocLinksMeta({ kibanaBranch: 'main', buildFlavor: 'traditional' }).version).toEqual( + 'master' + ); }); it('returns the correct version for other branches', () => { - expect(getDocLinksMeta({ kibanaBranch: '7.x' }).version).toEqual('7.x'); + expect(getDocLinksMeta({ kibanaBranch: '7.x', buildFlavor: 'traditional' }).version).toEqual( + '7.x' + ); }); it('returns the correct website url', () => { - expect(getDocLinksMeta({ kibanaBranch: '7.x' }).elasticWebsiteUrl).toEqual( - 'https://www.elastic.co/' - ); + expect( + getDocLinksMeta({ kibanaBranch: '7.x', buildFlavor: 'traditional' }).elasticWebsiteUrl + ).toEqual('https://www.elastic.co/'); }); }); diff --git a/packages/kbn-doc-links/src/get_doc_meta.ts b/packages/kbn-doc-links/src/get_doc_meta.ts index 10e144166d39f..6e36aef20471f 100644 --- a/packages/kbn-doc-links/src/get_doc_meta.ts +++ b/packages/kbn-doc-links/src/get_doc_meta.ts @@ -6,13 +6,17 @@ * Side Public License, v 1. */ -import { DocLinksMeta } from './types'; +import { DocLinksMeta, BuildFlavor } from './types'; export interface GetDocLinksMetaOptions { kibanaBranch: string; + buildFlavor: BuildFlavor; } -export const getDocLinksMeta = ({ kibanaBranch }: GetDocLinksMetaOptions): DocLinksMeta => { +export const getDocLinksMeta = ({ + kibanaBranch, + buildFlavor, +}: GetDocLinksMetaOptions): DocLinksMeta => { return { version: kibanaBranch === 'main' ? 'master' : kibanaBranch, elasticWebsiteUrl: 'https://www.elastic.co/', diff --git a/packages/kbn-doc-links/src/types.ts b/packages/kbn-doc-links/src/types.ts index f9e8699a06375..910c0c218dcc5 100644 --- a/packages/kbn-doc-links/src/types.ts +++ b/packages/kbn-doc-links/src/types.ts @@ -391,6 +391,7 @@ export interface DocLinks { cronExpressions: string; executeWatchActionModes: string; indexExists: string; + inferTrainedModel: string; multiSearch: string; openIndex: string; putComponentTemplate: string; @@ -536,6 +537,7 @@ export interface DocLinks { elasticAgentInputConfiguration: string; policySecrets: string; remoteESOoutput: string; + performancePresets: string; }>; readonly ecs: { readonly guide: string; @@ -633,3 +635,5 @@ export interface DocLinks { readonly settings: string; }; } + +export type BuildFlavor = 'serverless' | 'traditional'; diff --git a/packages/kbn-dom-drag-drop/package.json b/packages/kbn-dom-drag-drop/package.json index 13248a11ab059..b69bca91c0c17 100644 --- a/packages/kbn-dom-drag-drop/package.json +++ b/packages/kbn-dom-drag-drop/package.json @@ -2,5 +2,8 @@ "name": "@kbn/dom-drag-drop", "private": true, "version": "1.0.0", - "license": "SSPL-1.0 OR Elastic License 2.0" + "license": "SSPL-1.0 OR Elastic License 2.0", + "sideEffects": [ + "*.scss" + ] } \ No newline at end of file diff --git a/packages/kbn-ecs/package.json b/packages/kbn-ecs/package.json index ad23a36d8f82a..07ffc9fed886a 100644 --- a/packages/kbn-ecs/package.json +++ b/packages/kbn-ecs/package.json @@ -2,5 +2,6 @@ "name": "@kbn/ecs", "version": "1.0.0", "private": true, - "license": "SSPL-1.0 OR Elastic License 2.0" + "license": "SSPL-1.0 OR Elastic License 2.0", + "sideEffects": false } diff --git a/packages/kbn-elastic-agent-utils/src/agent_names.ts b/packages/kbn-elastic-agent-utils/src/agent_names.ts index f29160699a241..ca98b64024f8a 100644 --- a/packages/kbn-elastic-agent-utils/src/agent_names.ts +++ b/packages/kbn-elastic-agent-utils/src/agent_names.ts @@ -49,6 +49,7 @@ export type OpenTelemetryAgentName = | 'opentelemetry/ruby' | 'opentelemetry/rust' | 'opentelemetry/swift' + | 'opentelemetry/android' | 'opentelemetry/webjs'; export const OPEN_TELEMETRY_AGENT_NAMES: OpenTelemetryAgentName[] = [ 'otlp', @@ -63,6 +64,7 @@ export const OPEN_TELEMETRY_AGENT_NAMES: OpenTelemetryAgentName[] = [ 'opentelemetry/ruby', 'opentelemetry/rust', 'opentelemetry/swift', + 'opentelemetry/android', 'opentelemetry/webjs', ]; diff --git a/packages/kbn-es-query/index.ts b/packages/kbn-es-query/index.ts index ac0a078e34d94..cfcf8239196df 100644 --- a/packages/kbn-es-query/index.ts +++ b/packages/kbn-es-query/index.ts @@ -128,3 +128,5 @@ export { isDataViewFieldSubtypeMulti, isDataViewFieldSubtypeNested, } from './src/utils'; + +export type { ExecutionContextSearch } from './src/expressions/types'; diff --git a/packages/kbn-es-query/package.json b/packages/kbn-es-query/package.json index 7894764af2d87..15d903f7d6057 100644 --- a/packages/kbn-es-query/package.json +++ b/packages/kbn-es-query/package.json @@ -2,5 +2,6 @@ "name": "@kbn/es-query", "version": "1.0.0", "license": "SSPL-1.0 OR Elastic License 2.0", - "private": true + "private": true, + "sideEffects": false } \ No newline at end of file diff --git a/packages/kbn-es-query/src/expressions/types.ts b/packages/kbn-es-query/src/expressions/types.ts new file mode 100644 index 0000000000000..ed367adc7973a --- /dev/null +++ b/packages/kbn-es-query/src/expressions/types.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Filter, Query, TimeRange } from '../filters'; + +export interface ExecutionContextSearch { + now?: number; + filters?: Filter[]; + query?: Query | Query[]; + timeRange?: TimeRange; + disableWarningToasts?: boolean; +} diff --git a/packages/kbn-es/src/serverless_resources/roles.yml b/packages/kbn-es/src/serverless_resources/roles.yml index a98652d5b9311..bdd135366b3a8 100644 --- a/packages/kbn-es/src/serverless_resources/roles.yml +++ b/packages/kbn-es/src/serverless_resources/roles.yml @@ -95,6 +95,7 @@ editor: run_as: [] # source: https://github.com/elastic/project-controller/blob/main/internal/project/security/config/roles.yml + t1_analyst: cluster: indices: @@ -118,6 +119,7 @@ t1_analyst: - ".fleet-agents*" - ".fleet-actions*" - risk-score.risk-score-* + - .asset-criticality.asset-criticality-* privileges: - read applications: @@ -168,6 +170,11 @@ t2_analyst: - risk-score.risk-score-* privileges: - read + - names: + - .asset-criticality.asset-criticality-* + privileges: + - read + - write applications: - application: "kibana-.kibana" privileges: @@ -201,6 +208,7 @@ t3_analyst: - logs-* - packetbeat-* - winlogbeat-* + - .asset-criticality.asset-criticality-* privileges: - read - write @@ -271,6 +279,7 @@ threat_intelligence_analyst: - names: - .lists* - .items* + - .asset-criticality.asset-criticality-* privileges: - read - write @@ -320,6 +329,7 @@ rule_author: - logs-* - packetbeat-* - winlogbeat-* + - .asset-criticality.asset-criticality-* privileges: - read - write @@ -385,6 +395,7 @@ soc_manager: - logs-* - packetbeat-* - winlogbeat-* + - .asset-criticality.asset-criticality-* privileges: - read - write @@ -474,6 +485,11 @@ detections_admin: - risk-score.risk-score-* privileges: - all + - names: + - .asset-criticality.asset-criticality-* + privileges: + - read + - write applications: - application: "kibana-.kibana" privileges: @@ -516,6 +532,11 @@ platform_engineer: - risk-score.risk-score-* privileges: - all + - names: + - .asset-criticality.asset-criticality-* + privileges: + - read + - write applications: - application: "kibana-.kibana" privileges: @@ -578,11 +599,17 @@ endpoint_operations_analyst: - read - write - maintenance + - names: + - .asset-criticality.asset-criticality-* + privileges: + - read + - write applications: - application: "kibana-.kibana" privileges: - feature_ml.read - feature_siem.all + - feature_siem.read_alerts - feature_siem.policy_management_all - feature_siem.endpoint_list_all - feature_siem.trusted_applications_all @@ -633,6 +660,7 @@ endpoint_policy_manager: - names: - .lists* - .items* + - .asset-criticality.asset-criticality-* privileges: - read - write diff --git a/packages/kbn-es/src/serverless_resources/security_roles.json b/packages/kbn-es/src/serverless_resources/security_roles.json index c4a06751e19b7..0c965328a971c 100644 --- a/packages/kbn-es/src/serverless_resources/security_roles.json +++ b/packages/kbn-es/src/serverless_resources/security_roles.json @@ -20,7 +20,8 @@ "winlogbeat-*", "metrics-endpoint.metadata_current_*", ".fleet-agents*", - ".fleet-actions*" + ".fleet-actions*", + ".asset-criticality.asset-criticality-*" ], "privileges": ["read"] } @@ -65,7 +66,8 @@ "winlogbeat-*", "metrics-endpoint.metadata_current_*", ".fleet-agents*", - ".fleet-actions*" + ".fleet-actions*", + ".asset-criticality.asset-criticality-*" ], "privileges": ["read"] } @@ -101,7 +103,8 @@ "filebeat-*", "logs-*", "packetbeat-*", - "winlogbeat-*" + "winlogbeat-*", + ".asset-criticality.asset-criticality-*" ], "privileges": ["read", "write"] }, @@ -165,7 +168,8 @@ "packetbeat-*", "winlogbeat-*", ".lists*", - ".items*" + ".items*", + ".asset-criticality.asset-criticality-*" ], "privileges": ["read", "write"] }, @@ -216,7 +220,8 @@ "packetbeat-*", "winlogbeat-*", ".lists*", - ".items*" + ".items*", + ".asset-criticality.asset-criticality-*" ], "privileges": ["read", "write"] }, @@ -271,7 +276,8 @@ "filebeat-*", "logs-*", "packetbeat-*", - "winlogbeat-*" + "winlogbeat-*", + ".asset-criticality.asset-criticality-*" ], "privileges": ["manage", "write", "read"] }, @@ -319,7 +325,8 @@ "winlogbeat-*", "metrics-endpoint.metadata_current_*", ".fleet-agents*", - ".fleet-actions*" + ".fleet-actions*", + ".asset-criticality.asset-criticality-*" ], "privileges": ["all"] }, diff --git a/packages/kbn-expandable-flyout/package.json b/packages/kbn-expandable-flyout/package.json index a2e826c042842..1dbae79ed9931 100644 --- a/packages/kbn-expandable-flyout/package.json +++ b/packages/kbn-expandable-flyout/package.json @@ -2,5 +2,6 @@ "name": "@kbn/expandable-flyout", "private": true, "version": "1.0.0", - "license": "SSPL-1.0 OR Elastic License 2.0" + "license": "SSPL-1.0 OR Elastic License 2.0", + "sideEffects": false } \ No newline at end of file diff --git a/packages/kbn-failed-test-reporter-cli/failed_tests_reporter/add_messages_to_report.test.ts b/packages/kbn-failed-test-reporter-cli/failed_tests_reporter/add_messages_to_report.test.ts index 3b71823ee6bde..02197cf54fab0 100644 --- a/packages/kbn-failed-test-reporter-cli/failed_tests_reporter/add_messages_to_report.test.ts +++ b/packages/kbn-failed-test-reporter-cli/failed_tests_reporter/add_messages_to_report.test.ts @@ -53,10 +53,10 @@ it('rewrites ftr reports with minimal changes', async () => { reportPath: Path.resolve(__dirname, './__fixtures__/ftr_report.xml'), }); - expect(createPatch('ftr.xml', FTR_REPORT, xml, { context: 0 })).toMatchInlineSnapshot(` + expect(createPatch('ftr.xml', FTR_REPORT, xml)).toMatchInlineSnapshot(` Index: ftr.xml =================================================================== - --- ftr.xml [object Object] + --- ftr.xml +++ ftr.xml @@ -1,53 +1,56 @@ ‹?xml version="1.0" encoding="utf-8"?› @@ -149,10 +149,10 @@ it('rewrites jest reports with minimal changes', async () => { reportPath: Path.resolve(__dirname, './__fixtures__/jest_report.xml'), }); - expect(createPatch('jest.xml', JEST_REPORT, xml, { context: 0 })).toMatchInlineSnapshot(` + expect(createPatch('jest.xml', JEST_REPORT, xml)).toMatchInlineSnapshot(` Index: jest.xml =================================================================== - --- jest.xml [object Object] + --- jest.xml +++ jest.xml @@ -3,13 +3,17 @@ ‹testsuite name="x-pack/legacy/plugins/code/server/lsp/abstract_launcher.test.ts" timestamp="2019-06-07T03:42:21" time="14.504" tests="5" failures="1" skipped="0" file="/var/lib/jenkins/workspace/elastic+kibana+master/JOB/x-pack-intake/node/immutable/kibana/x-pack/legacy/plugins/code/server/lsp/abstract_launcher.test.ts"› @@ -196,10 +196,10 @@ it('rewrites mocha reports with minimal changes', async () => { reportPath: Path.resolve(__dirname, './__fixtures__/mocha_report.xml'), }); - expect(createPatch('mocha.xml', MOCHA_REPORT, xml, { context: 0 })).toMatchInlineSnapshot(` + expect(createPatch('mocha.xml', MOCHA_REPORT, xml)).toMatchInlineSnapshot(` Index: mocha.xml =================================================================== - --- mocha.xml [object Object] + --- mocha.xml +++ mocha.xml @@ -1,13 +1,16 @@ ‹?xml version="1.0" encoding="utf-8"?› @@ -273,10 +273,10 @@ it('rewrites cypress reports with minimal changes', async () => { reportPath: Path.resolve(__dirname, './__fixtures__/cypress_report.xml'), }); - expect(createPatch('cypress.xml', CYPRESS_REPORT, xml, { context: 0 })).toMatchInlineSnapshot(` + expect(createPatch('cypress.xml', CYPRESS_REPORT, xml)).toMatchInlineSnapshot(` Index: cypress.xml =================================================================== - --- cypress.xml [object Object] + --- cypress.xml +++ cypress.xml @@ -1,25 +1,16 @@ -‹?xml version="1.0" encoding="UTF-8"?› diff --git a/packages/kbn-ftr-common-functional-ui-services/README.md b/packages/kbn-ftr-common-functional-ui-services/README.md new file mode 100644 index 0000000000000..b6fbe963d099b --- /dev/null +++ b/packages/kbn-ftr-common-functional-ui-services/README.md @@ -0,0 +1,3 @@ +# @kbn/ftr-common-functional-ui-services + +Common test services for ui actions. diff --git a/packages/kbn-ftr-common-functional-ui-services/index.ts b/packages/kbn-ftr-common-functional-ui-services/index.ts new file mode 100644 index 0000000000000..5fe8ae6fd0164 --- /dev/null +++ b/packages/kbn-ftr-common-functional-ui-services/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { services as commonFunctionalUIServices } from './services/all'; +export type { FtrProviderContext } from './services/ftr_provider_context'; diff --git a/packages/kbn-ftr-common-functional-ui-services/jest.config.js b/packages/kbn-ftr-common-functional-ui-services/jest.config.js new file mode 100644 index 0000000000000..afd295ad81883 --- /dev/null +++ b/packages/kbn-ftr-common-functional-ui-services/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test/jest_node', + rootDir: '../..', + roots: ['/packages/kbn-ftr-common-functional-ui-services'], +}; diff --git a/packages/kbn-ftr-common-functional-ui-services/kibana.jsonc b/packages/kbn-ftr-common-functional-ui-services/kibana.jsonc new file mode 100644 index 0000000000000..5437a12260a9c --- /dev/null +++ b/packages/kbn-ftr-common-functional-ui-services/kibana.jsonc @@ -0,0 +1,6 @@ +{ + "type": "shared-common", + "id": "@kbn/ftr-common-functional-ui-services", + "owner": "@elastic/appex-qa", + "devOnly": true +} diff --git a/packages/kbn-ftr-common-functional-ui-services/package.json b/packages/kbn-ftr-common-functional-ui-services/package.json new file mode 100644 index 0000000000000..4fad67bc28fa0 --- /dev/null +++ b/packages/kbn-ftr-common-functional-ui-services/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/ftr-common-functional-ui-services", + "private": true, + "version": "1.0.0", + "license": "SSPL-1.0 OR Elastic License 2.0" +} \ No newline at end of file diff --git a/packages/kbn-ftr-common-functional-ui-services/services/all.ts b/packages/kbn-ftr-common-functional-ui-services/services/all.ts new file mode 100644 index 0000000000000..8c8a723660117 --- /dev/null +++ b/packages/kbn-ftr-common-functional-ui-services/services/all.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { RetryOnStaleProvider } from './retry_on_stale'; + +export const services = { + retryOnStale: RetryOnStaleProvider, +}; diff --git a/packages/kbn-ftr-common-functional-ui-services/services/ftr_provider_context.ts b/packages/kbn-ftr-common-functional-ui-services/services/ftr_provider_context.ts new file mode 100644 index 0000000000000..979658fbd8edd --- /dev/null +++ b/packages/kbn-ftr-common-functional-ui-services/services/ftr_provider_context.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { GenericFtrProviderContext, GenericFtrService } from '@kbn/test'; + +import type { services } from './all'; + +type Services = typeof services; + +export type FtrProviderContext = GenericFtrProviderContext; +export class FtrService extends GenericFtrService {} diff --git a/test/functional/services/common/retry_on_stale.ts b/packages/kbn-ftr-common-functional-ui-services/services/retry_on_stale.ts similarity index 95% rename from test/functional/services/common/retry_on_stale.ts rename to packages/kbn-ftr-common-functional-ui-services/services/retry_on_stale.ts index 4a190266458ec..8f4b374a8b3cd 100644 --- a/test/functional/services/common/retry_on_stale.ts +++ b/packages/kbn-ftr-common-functional-ui-services/services/retry_on_stale.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrProviderContext } from './ftr_provider_context'; const MAX_ATTEMPTS = 10; diff --git a/packages/kbn-ftr-common-functional-ui-services/tsconfig.json b/packages/kbn-ftr-common-functional-ui-services/tsconfig.json new file mode 100644 index 0000000000000..0c77cc5d6b917 --- /dev/null +++ b/packages/kbn-ftr-common-functional-ui-services/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node" + ] + }, + "include": [ + "**/*.ts", + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/test", + ] +} diff --git a/packages/kbn-guided-onboarding/package.json b/packages/kbn-guided-onboarding/package.json index d25b7c04bc4a0..ce4f8ebcff41a 100644 --- a/packages/kbn-guided-onboarding/package.json +++ b/packages/kbn-guided-onboarding/package.json @@ -2,5 +2,6 @@ "name": "@kbn/guided-onboarding", "private": true, "version": "1.0.0", - "license": "SSPL-1.0 OR Elastic License 2.0" + "license": "SSPL-1.0 OR Elastic License 2.0", + "sideEffects": false } \ No newline at end of file diff --git a/packages/kbn-guided-onboarding/src/components/landing_page/guide/__snapshots__/guide_cards.test.tsx.snap b/packages/kbn-guided-onboarding/src/components/landing_page/guide/__snapshots__/guide_cards.test.tsx.snap deleted file mode 100644 index 9f1d86ef25477..0000000000000 --- a/packages/kbn-guided-onboarding/src/components/landing_page/guide/__snapshots__/guide_cards.test.tsx.snap +++ /dev/null @@ -1,2989 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`guide cards snapshots should render observability cards 1`] = ` - - -
- - - - , - , - ], - }, - } - } - isStringTag={true} - serialized={ - Object { - "map": undefined, - "name": "1a2wnuz-euiFlexGroup-responsive-wrap-l-center-center-row", - "next": undefined, - "styles": "display:flex;align-items:stretch;flex-grow:1;label:euiFlexGroup;;;@media only screen and (max-width: 767px){flex-wrap:wrap;&>.euiFlexItem{inline-size: 100%; flex-basis:100%;}};label:responsive;;;flex-wrap:wrap;label:wrap;;;gap:24px;;label:l;;;justify-content:center;label:center;;;align-items:center;label:center;;;flex-direction:row;label:row;;;", - "toString": [Function], - } - } - /> -
-
- - -`; - -exports[`guide cards snapshots should render search cards 1`] = ` - - -
- - - - , - , - ], - }, - } - } - isStringTag={true} - serialized={ - Object { - "map": undefined, - "name": "1a2wnuz-euiFlexGroup-responsive-wrap-l-center-center-row", - "next": undefined, - "styles": "display:flex;align-items:stretch;flex-grow:1;label:euiFlexGroup;;;@media only screen and (max-width: 767px){flex-wrap:wrap;&>.euiFlexItem{inline-size: 100%; flex-basis:100%;}};label:responsive;;;flex-wrap:wrap;label:wrap;;;gap:24px;;label:l;;;justify-content:center;label:center;;;align-items:center;label:center;;;flex-direction:row;label:row;;;", - "toString": [Function], - } - } - /> -
-
- - -`; - -exports[`guide cards snapshots should render security cards 1`] = ` - - -
- - - - , - , - ], - }, - } - } - isStringTag={true} - serialized={ - Object { - "map": undefined, - "name": "1a2wnuz-euiFlexGroup-responsive-wrap-l-center-center-row", - "next": undefined, - "styles": "display:flex;align-items:stretch;flex-grow:1;label:euiFlexGroup;;;@media only screen and (max-width: 767px){flex-wrap:wrap;&>.euiFlexItem{inline-size: 100%; flex-basis:100%;}};label:responsive;;;flex-wrap:wrap;label:wrap;;;gap:24px;;label:l;;;justify-content:center;label:center;;;align-items:center;label:center;;;flex-direction:row;label:row;;;", - "toString": [Function], - } - } - /> -
-
- - -`; diff --git a/packages/kbn-guided-onboarding/src/components/landing_page/guide/guide_cards.test.tsx b/packages/kbn-guided-onboarding/src/components/landing_page/guide/guide_cards.test.tsx deleted file mode 100644 index ea0a18c89617d..0000000000000 --- a/packages/kbn-guided-onboarding/src/components/landing_page/guide/guide_cards.test.tsx +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import React from 'react'; -import { mount } from 'enzyme'; - -import { GuideCards, GuideCardsProps } from './guide_cards'; -import { cloudMock } from '@kbn/cloud-plugin/public/mocks'; -import { sharePluginMock } from '@kbn/share-plugin/public/mocks'; -import { I18nStart } from '@kbn/core-i18n-browser'; -import { themeServiceMock } from '@kbn/core-theme-browser-mocks'; -import { docLinksServiceMock } from '@kbn/core-doc-links-browser-mocks'; -import { GuideCardSolutions } from '../classic_version/guide_cards'; - -const defaultProps: Omit = { - activateGuide: jest.fn(), - navigateToApp: jest.fn(), - guidesState: [], - openModal: jest.fn(), - theme: themeServiceMock.createStartContract(), - i18nStart: {} as unknown as I18nStart, - cloud: cloudMock.createSetup(), - docLinks: docLinksServiceMock.createStartContract(), - navigateToUrl: jest.fn(), - url: sharePluginMock.createSetupContract().url, -}; - -// FLAKY: https://github.com/elastic/kibana/issues/172595 -// FLAKY: https://github.com/elastic/kibana/issues/172596 -// FLAKY: https://github.com/elastic/kibana/issues/172597 -describe.skip('guide cards', () => { - describe('snapshots', () => { - test('should render search cards', async () => { - const component = mount( - - ); - expect(component).toMatchSnapshot(); - }); - test('should render security cards', async () => { - const component = mount( - - ); - expect(component).toMatchSnapshot(); - }); - test('should render observability cards', async () => { - const component = mount( - - ); - expect(component).toMatchSnapshot(); - }); - }); -}); diff --git a/packages/kbn-guided-onboarding/tsconfig.json b/packages/kbn-guided-onboarding/tsconfig.json index 1bfd6c2454fcb..050ae7d99e95c 100644 --- a/packages/kbn-guided-onboarding/tsconfig.json +++ b/packages/kbn-guided-onboarding/tsconfig.json @@ -23,9 +23,7 @@ "@kbn/share-plugin", "@kbn/cloud-plugin", "@kbn/core-lifecycle-browser", - "@kbn/core-theme-browser-mocks", "@kbn/analytics", - "@kbn/core-doc-links-browser-mocks", "@kbn/core-mount-utils-browser" ], "exclude": [ diff --git a/packages/kbn-io-ts-utils/package.json b/packages/kbn-io-ts-utils/package.json index d62960217f948..9e402254c368c 100644 --- a/packages/kbn-io-ts-utils/package.json +++ b/packages/kbn-io-ts-utils/package.json @@ -2,5 +2,6 @@ "name": "@kbn/io-ts-utils", "version": "1.0.0", "license": "SSPL-1.0 OR Elastic License 2.0", - "private": true + "private": true, + "sideEffects": false } \ No newline at end of file diff --git a/packages/kbn-language-documentation-popover/package.json b/packages/kbn-language-documentation-popover/package.json index dfa1e7e4fd372..63060b46c2c1e 100644 --- a/packages/kbn-language-documentation-popover/package.json +++ b/packages/kbn-language-documentation-popover/package.json @@ -2,5 +2,6 @@ "name": "@kbn/language-documentation-popover", "version": "1.0.0", "license": "SSPL-1.0 OR Elastic License 2.0", - "private": true + "private": true, + "sideEffects": false } \ No newline at end of file diff --git a/packages/kbn-logging/package.json b/packages/kbn-logging/package.json index 7ffb4903932de..0a10bfa905afb 100644 --- a/packages/kbn-logging/package.json +++ b/packages/kbn-logging/package.json @@ -3,5 +3,6 @@ "version": "1.0.0", "private": true, "author": "Kibana Core", - "license": "SSPL-1.0 OR Elastic License 2.0" + "license": "SSPL-1.0 OR Elastic License 2.0", + "sideEffects": false } \ No newline at end of file diff --git a/packages/kbn-monaco/index.ts b/packages/kbn-monaco/index.ts index d7ff4d4abc94a..2ebb05bd0e393 100644 --- a/packages/kbn-monaco/index.ts +++ b/packages/kbn-monaco/index.ts @@ -12,7 +12,7 @@ export { monaco } from './src/monaco_imports'; export { XJsonLang } from './src/xjson'; export { SQLLang } from './src/sql'; export { ESQL_LANG_ID, ESQL_THEME_ID, ESQLLang } from './src/esql'; -export type { ESQLCustomAutocompleteCallbacks } from './src/esql'; +export type { ESQLCallbacks } from './src/esql'; export * from './src/painless'; /* eslint-disable-next-line @kbn/eslint/module_migration */ diff --git a/packages/kbn-monaco/src/common/error_listener.ts b/packages/kbn-monaco/src/common/error_listener.ts index b072b132b26ac..efcacb88d2c2a 100644 --- a/packages/kbn-monaco/src/common/error_listener.ts +++ b/packages/kbn-monaco/src/common/error_listener.ts @@ -31,6 +31,7 @@ export class ANTLREErrorListener implements ANTLRErrorListener { startColumn: column, endColumn, message, + severity: 8, }); } diff --git a/packages/kbn-monaco/src/esql/antlr/esql_lexer.g4 b/packages/kbn-monaco/src/esql/antlr/esql_lexer.g4 index 815cf7f237bfb..90a892d8d1dd6 100644 --- a/packages/kbn-monaco/src/esql/antlr/esql_lexer.g4 +++ b/packages/kbn-monaco/src/esql/antlr/esql_lexer.g4 @@ -4,26 +4,27 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - + lexer grammar esql_lexer; +options { } DISSECT : D I S S E C T -> pushMode(EXPRESSION); -GROK : G R O K -> pushMode(EXPRESSION); +DROP : D R O P -> pushMode(SOURCE_IDENTIFIERS); +ENRICH : E N R I C H -> pushMode(SOURCE_IDENTIFIERS); EVAL : E V A L -> pushMode(EXPRESSION); -EXPLAIN : E X P L A I N -> pushMode(EXPLAIN_MODE); FROM : F R O M -> pushMode(SOURCE_IDENTIFIERS); +GROK : G R O K -> pushMode(EXPRESSION); +KEEP : K E E P -> pushMode(SOURCE_IDENTIFIERS); +LIMIT : L I M I T -> pushMode(EXPRESSION); +MV_EXPAND : M V UNDERSCORE E X P A N D -> pushMode(SOURCE_IDENTIFIERS); +PROJECT : P R O J E C T -> pushMode(SOURCE_IDENTIFIERS); +RENAME : R E N A M E -> pushMode(SOURCE_IDENTIFIERS); ROW : R O W -> pushMode(EXPRESSION); +SHOW : S H O W -> pushMode(EXPRESSION); +SORT : S O R T -> pushMode(EXPRESSION); STATS : S T A T S -> pushMode(EXPRESSION); WHERE : W H E R E -> pushMode(EXPRESSION); -SORT : S O R T -> pushMode(EXPRESSION); -MV_EXPAND : M V UNDERSCORE E X P A N D -> pushMode(EXPRESSION); -LIMIT : L I M I T -> pushMode(EXPRESSION); -PROJECT : P R O J E C T -> pushMode(EXPRESSION); -DROP : D R O P -> pushMode(EXPRESSION); -RENAME : R E N A M E -> pushMode(EXPRESSION); -SHOW : S H O W -> pushMode(EXPRESSION); -ENRICH : E N R I C H -> pushMode(ENRICH_IDENTIFIERS); -KEEP : K E E P -> pushMode(EXPRESSION); +UNKNOWN_CMD : ~[ \r\n\t[\]/]+ -> pushMode(EXPRESSION); LINE_COMMENT : '//' ~[\r\n]* '\r'? '\n'? -> channel(HIDDEN) @@ -36,12 +37,7 @@ MULTILINE_COMMENT WS : [ \r\n\t]+ -> channel(HIDDEN) ; -mode EXPLAIN_MODE; -EXPLAIN_OPENING_BRACKET : '[' -> type(OPENING_BRACKET), pushMode(DEFAULT_MODE); -EXPLAIN_PIPE : '|' -> type(PIPE), popMode; -EXPLAIN_WS : WS -> channel(HIDDEN); -EXPLAIN_LINE_COMMENT : LINE_COMMENT -> channel(HIDDEN); -EXPLAIN_MULTILINE_COMMENT : MULTILINE_COMMENT -> channel(HIDDEN); + mode EXPRESSION; PIPE : '|' -> popMode; @@ -82,169 +78,60 @@ DECIMAL_LITERAL | DOT DIGIT+ EXPONENT ; -BY : 'by'; - -DATE_LITERAL - : 'year' - | 'month' - | 'day' - | 'second' - | 'minute' - | 'hour' - | 'week' - | 'millisecond' - | 'years' - | 'months' - | 'days' - | 'seconds' - | 'minutes' - | 'hours' - | 'weeks' - | 'milliseconds' - ; +BY : B Y; -AND : 'and'; +AND : A N D; +ASC : A S C; ASSIGN : '='; COMMA : ','; +DESC : D E S C; DOT : '.'; +FALSE : F A L S E; +FIRST : F I R S T; +LAST : L A S T; LP : '('; -OPENING_BRACKET : '[' -> pushMode(EXPRESSION), pushMode(EXPRESSION); -CLOSING_BRACKET : ']' -> popMode, popMode; -NOT : N O T; -LIKE: L I K E; -RLIKE: R L I K E; IN: I N; IS: I S; -AS: A S; +LIKE: L I K E; +NOT : N O T; NULL : N U L L; -OR : 'or'; +NULLS : N U L L S; +OR : O R; +PARAM: '?'; +RLIKE: R L I K E; RP : ')'; +TRUE : T R U E; +INFO : I N F O; +FUNCTIONS : F U N C T I O N S; UNDERSCORE: '_'; -INFO : 'info'; -FUNCTIONS : 'functions'; - -BOOLEAN_VALUE - : 'true' - | 'false' - ; - -COMPARISON_OPERATOR - : '==' - |'!=' - | '<' - | '<=' - | '>' - | '>=' - ; + +EQ : '=='; +NEQ : '!='; +LT : '<'; +LTE : '<='; +GT : '>'; +GTE : '>='; PLUS : '+'; MINUS : '-'; ASTERISK : '*'; SLASH : '/'; PERCENT : '%'; -TEN: '10'; - -ORDERING - : 'asc' - | 'desc' - ; - -NULLS_ORDERING: 'nulls'; -NULLS_ORDERING_DIRECTION - : 'first' - | 'last' - ; -MATH_FUNCTION - : R O U N D - | A B S - | P O W - | L O G TEN - | P I - | T A U - | E - | S U B S T R I N G - | T R I M - | C O N C A T - | C O A L E S C E - | G R E A T E S T - | L E F T - | N O W - | R I G H T - | S T A R T S UNDERSCORE W I T H - | D A T E UNDERSCORE F O R M A T - | D A T E UNDERSCORE T R U N C - | D A T E UNDERSCORE P A R S E - | A U T O UNDERSCORE B U C K E T - | D A T E UNDERSCORE E X T R A C T - | I S UNDERSCORE F I N I T E - | I S UNDERSCORE I N F I N I T E - | C A S E - | L E N G T H - | M V UNDERSCORE M A X - | M V UNDERSCORE M I N - | M V UNDERSCORE A V G - | M V UNDERSCORE S U M - | M V UNDERSCORE C O U N T - | M V UNDERSCORE C O N C A T - | M V UNDERSCORE J O I N - | M V UNDERSCORE M E D I A N - | M V UNDERSCORE D E D U P E - | M E T A D A T A - | S P L I T - | T O UNDERSCORE S T R I N G - | T O UNDERSCORE S T R - | T O UNDERSCORE B O O L - | T O UNDERSCORE B O O L E A N - | T O UNDERSCORE D A T E T I M E - | T O UNDERSCORE D T - | T O UNDERSCORE D B L - | T O UNDERSCORE D O U B L E - | T O UNDERSCORE D E G R E E S - | T O UNDERSCORE I N T - | T O UNDERSCORE I N T E G E R - | T O UNDERSCORE I P - | T O UNDERSCORE L O N G - | T O UNDERSCORE R A D I A N S - | T O UNDERSCORE V E R S I O N - | T O UNDERSCORE U N S I G N E D UNDERSCORE L O N G - ; - -UNARY_FUNCTION - : A V G - | M I N - | M A X - | S U M - | C O U N T - | C O U N T UNDERSCORE D I S T I N C T - | P E R C E N T I L E - | M E D I A N - | M E D I A N UNDERSCORE A B S O L U T E UNDERSCORE D E V I A T I O N - | A C O S - | A S I N - | A T A N - | A T A N '2' - | C E I L - | C O S - | C O S H - | F L O O R - | L T R I M - | S I N - | S I N H - | S Q R T - | T A N - | T A N H - ; +// Brackets are funny. We can happen upon a CLOSING_BRACKET in two ways - one +// way is to start in an explain command which then shifts us to expression +// mode. Thus, the two popModes on CLOSING_BRACKET. The other way could as +// the start of a multivalued field constant. To line up with the double pop +// the explain mode needs, we double push when we see that. +OPENING_BRACKET : '[' -> pushMode(EXPRESSION), pushMode(EXPRESSION); +CLOSING_BRACKET : ']' -> popMode, popMode; -WHERE_FUNCTIONS - : C I D R UNDERSCORE M A T C H - ; UNQUOTED_IDENTIFIER - : LETTER (LETTER | DIGIT | '_' | ASTERISK)* + : LETTER (LETTER | DIGIT | '_')* // only allow @ at beginning of identifier to keep the option to allow @ as infix operator in the future // also, single `_` and `@` characters are not valid identifiers - | ('_' | '@') (LETTER | DIGIT | '_' | ASTERISK)+ + | ('_' | '@') (LETTER | DIGIT | '_')+ ; QUOTED_IDENTIFIER @@ -264,6 +151,7 @@ EXPR_WS ; + mode SOURCE_IDENTIFIERS; SRC_PIPE : '|' -> type(PIPE), popMode; @@ -271,7 +159,10 @@ SRC_OPENING_BRACKET : '[' -> type(OPENING_BRACKET), pushMode(SOURCE_IDENTIFIERS) SRC_CLOSING_BRACKET : ']' -> popMode, popMode, type(CLOSING_BRACKET); SRC_COMMA : ',' -> type(COMMA); SRC_ASSIGN : '=' -> type(ASSIGN); +AS : A S; METADATA: M E T A D A T A; +ON : O N; +WITH : W I T H; SRC_UNQUOTED_IDENTIFIER : SRC_UNQUOTED_IDENTIFIER_PART+ @@ -298,41 +189,6 @@ SRC_WS : WS -> channel(HIDDEN) ; -mode ENRICH_IDENTIFIERS; - -ON : O N; -WITH : W I T H; - -ENR_PIPE : '|' -> type(PIPE), popMode; -ENR_CLOSING_BRACKET : ']' -> popMode, popMode, type(CLOSING_BRACKET); -ENR_COMMA : ',' -> type(COMMA); -ENR_ASSIGN : '=' -> type(ASSIGN); - -ENR_UNQUOTED_IDENTIFIER - : ENR_UNQUOTED_IDENTIFIER_PART+ - ; - -fragment ENR_UNQUOTED_IDENTIFIER_PART - : ~[=`|,[\]/ \t\r\n]+ - | '/' ~[*/] // allow single / but not followed by another / or * which would start a comment - ; - -ENR_QUOTED_IDENTIFIER - : QUOTED_IDENTIFIER - ; - -ENR_LINE_COMMENT - : LINE_COMMENT -> channel(HIDDEN) - ; - -ENR_MULTILINE_COMMENT - : MULTILINE_COMMENT -> channel(HIDDEN) - ; - -ENR_WS - : WS -> channel(HIDDEN) - ; - fragment A : [aA]; // match either an 'a' or 'A' fragment B : [bB]; fragment C : [cC]; diff --git a/packages/kbn-monaco/src/esql/antlr/esql_lexer.interp b/packages/kbn-monaco/src/esql/antlr/esql_lexer.interp index 80f8a20f9d322..0ff1f62c47445 100644 --- a/packages/kbn-monaco/src/esql/antlr/esql_lexer.interp +++ b/packages/kbn-monaco/src/esql/antlr/esql_lexer.interp @@ -27,46 +27,41 @@ null null null null -'by' null -'and' null null '.' +null +null +null '(' null -']' null null null null null null +'?' null -'or' ')' -'_' -'info' -'functions' null null +null +'_' +'==' +'!=' +'<' +'<=' +'>' +'>=' '+' '-' '*' '/' '%' -'10' -null -'nulls' -null -null -null -null -null -null -null -null null +']' null null null @@ -85,113 +80,103 @@ null token symbolic names: null DISSECT -GROK +DROP +ENRICH EVAL -EXPLAIN FROM -ROW -STATS -WHERE -SORT -MV_EXPAND +GROK +KEEP LIMIT +MV_EXPAND PROJECT -DROP RENAME +ROW SHOW -ENRICH -KEEP +SORT +STATS +WHERE +UNKNOWN_CMD LINE_COMMENT MULTILINE_COMMENT WS -EXPLAIN_WS -EXPLAIN_LINE_COMMENT -EXPLAIN_MULTILINE_COMMENT PIPE STRING INTEGER_LITERAL DECIMAL_LITERAL BY -DATE_LITERAL AND +ASC ASSIGN COMMA +DESC DOT +FALSE +FIRST +LAST LP -OPENING_BRACKET -CLOSING_BRACKET -NOT -LIKE -RLIKE IN IS -AS +LIKE +NOT NULL +NULLS OR +PARAM +RLIKE RP -UNDERSCORE +TRUE INFO FUNCTIONS -BOOLEAN_VALUE -COMPARISON_OPERATOR +UNDERSCORE +EQ +NEQ +LT +LTE +GT +GTE PLUS MINUS ASTERISK SLASH PERCENT -TEN -ORDERING -NULLS_ORDERING -NULLS_ORDERING_DIRECTION -MATH_FUNCTION -UNARY_FUNCTION -WHERE_FUNCTIONS +OPENING_BRACKET +CLOSING_BRACKET UNQUOTED_IDENTIFIER QUOTED_IDENTIFIER EXPR_LINE_COMMENT EXPR_MULTILINE_COMMENT EXPR_WS +AS METADATA +ON +WITH SRC_UNQUOTED_IDENTIFIER SRC_QUOTED_IDENTIFIER SRC_LINE_COMMENT SRC_MULTILINE_COMMENT SRC_WS -ON -WITH -ENR_UNQUOTED_IDENTIFIER -ENR_QUOTED_IDENTIFIER -ENR_LINE_COMMENT -ENR_MULTILINE_COMMENT -ENR_WS -EXPLAIN_PIPE rule names: DISSECT -GROK +DROP +ENRICH EVAL -EXPLAIN FROM -ROW -STATS -WHERE -SORT -MV_EXPAND +GROK +KEEP LIMIT +MV_EXPAND PROJECT -DROP RENAME +ROW SHOW -ENRICH -KEEP +SORT +STATS +WHERE +UNKNOWN_CMD LINE_COMMENT MULTILINE_COMMENT WS -EXPLAIN_OPENING_BRACKET -EXPLAIN_PIPE -EXPLAIN_WS -EXPLAIN_LINE_COMMENT -EXPLAIN_MULTILINE_COMMENT PIPE DIGIT LETTER @@ -202,40 +187,43 @@ STRING INTEGER_LITERAL DECIMAL_LITERAL BY -DATE_LITERAL AND +ASC ASSIGN COMMA +DESC DOT +FALSE +FIRST +LAST LP -OPENING_BRACKET -CLOSING_BRACKET -NOT -LIKE -RLIKE IN IS -AS +LIKE +NOT NULL +NULLS OR +PARAM +RLIKE RP -UNDERSCORE +TRUE INFO FUNCTIONS -BOOLEAN_VALUE -COMPARISON_OPERATOR +UNDERSCORE +EQ +NEQ +LT +LTE +GT +GTE PLUS MINUS ASTERISK SLASH PERCENT -TEN -ORDERING -NULLS_ORDERING -NULLS_ORDERING_DIRECTION -MATH_FUNCTION -UNARY_FUNCTION -WHERE_FUNCTIONS +OPENING_BRACKET +CLOSING_BRACKET UNQUOTED_IDENTIFIER QUOTED_IDENTIFIER EXPR_LINE_COMMENT @@ -246,25 +234,16 @@ SRC_OPENING_BRACKET SRC_CLOSING_BRACKET SRC_COMMA SRC_ASSIGN +AS METADATA +ON +WITH SRC_UNQUOTED_IDENTIFIER SRC_UNQUOTED_IDENTIFIER_PART SRC_QUOTED_IDENTIFIER SRC_LINE_COMMENT SRC_MULTILINE_COMMENT SRC_WS -ON -WITH -ENR_PIPE -ENR_CLOSING_BRACKET -ENR_COMMA -ENR_ASSIGN -ENR_UNQUOTED_IDENTIFIER -ENR_UNQUOTED_IDENTIFIER_PART -ENR_QUOTED_IDENTIFIER -ENR_LINE_COMMENT -ENR_MULTILINE_COMMENT -ENR_WS A B C @@ -298,10 +277,8 @@ HIDDEN mode names: DEFAULT_MODE -EXPLAIN_MODE EXPRESSION SOURCE_IDENTIFIERS -ENRICH_IDENTIFIERS atn: -[3, 51485, 51898, 1421, 44986, 20307, 1543, 60043, 49729, 2, 83, 1600, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4, 18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 4, 22, 9, 22, 4, 23, 9, 23, 4, 24, 9, 24, 4, 25, 9, 25, 4, 26, 9, 26, 4, 27, 9, 27, 4, 28, 9, 28, 4, 29, 9, 29, 4, 30, 9, 30, 4, 31, 9, 31, 4, 32, 9, 32, 4, 33, 9, 33, 4, 34, 9, 34, 4, 35, 9, 35, 4, 36, 9, 36, 4, 37, 9, 37, 4, 38, 9, 38, 4, 39, 9, 39, 4, 40, 9, 40, 4, 41, 9, 41, 4, 42, 9, 42, 4, 43, 9, 43, 4, 44, 9, 44, 4, 45, 9, 45, 4, 46, 9, 46, 4, 47, 9, 47, 4, 48, 9, 48, 4, 49, 9, 49, 4, 50, 9, 50, 4, 51, 9, 51, 4, 52, 9, 52, 4, 53, 9, 53, 4, 54, 9, 54, 4, 55, 9, 55, 4, 56, 9, 56, 4, 57, 9, 57, 4, 58, 9, 58, 4, 59, 9, 59, 4, 60, 9, 60, 4, 61, 9, 61, 4, 62, 9, 62, 4, 63, 9, 63, 4, 64, 9, 64, 4, 65, 9, 65, 4, 66, 9, 66, 4, 67, 9, 67, 4, 68, 9, 68, 4, 69, 9, 69, 4, 70, 9, 70, 4, 71, 9, 71, 4, 72, 9, 72, 4, 73, 9, 73, 4, 74, 9, 74, 4, 75, 9, 75, 4, 76, 9, 76, 4, 77, 9, 77, 4, 78, 9, 78, 4, 79, 9, 79, 4, 80, 9, 80, 4, 81, 9, 81, 4, 82, 9, 82, 4, 83, 9, 83, 4, 84, 9, 84, 4, 85, 9, 85, 4, 86, 9, 86, 4, 87, 9, 87, 4, 88, 9, 88, 4, 89, 9, 89, 4, 90, 9, 90, 4, 91, 9, 91, 4, 92, 9, 92, 4, 93, 9, 93, 4, 94, 9, 94, 4, 95, 9, 95, 4, 96, 9, 96, 4, 97, 9, 97, 4, 98, 9, 98, 4, 99, 9, 99, 4, 100, 9, 100, 4, 101, 9, 101, 4, 102, 9, 102, 4, 103, 9, 103, 4, 104, 9, 104, 4, 105, 9, 105, 4, 106, 9, 106, 4, 107, 9, 107, 4, 108, 9, 108, 4, 109, 9, 109, 4, 110, 9, 110, 4, 111, 9, 111, 4, 112, 9, 112, 4, 113, 9, 113, 4, 114, 9, 114, 4, 115, 9, 115, 4, 116, 9, 116, 4, 117, 9, 117, 4, 118, 9, 118, 4, 119, 9, 119, 4, 120, 9, 120, 4, 121, 9, 121, 4, 122, 9, 122, 4, 123, 9, 123, 4, 124, 9, 124, 4, 125, 9, 125, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 3, 14, 3, 14, 3, 14, 3, 14, 3, 14, 3, 14, 3, 14, 3, 15, 3, 15, 3, 15, 3, 15, 3, 15, 3, 15, 3, 15, 3, 15, 3, 15, 3, 16, 3, 16, 3, 16, 3, 16, 3, 16, 3, 16, 3, 16, 3, 17, 3, 17, 3, 17, 3, 17, 3, 17, 3, 17, 3, 17, 3, 17, 3, 17, 3, 18, 3, 18, 3, 18, 3, 18, 3, 18, 3, 18, 3, 18, 3, 19, 3, 19, 3, 19, 3, 19, 7, 19, 399, 10, 19, 12, 19, 14, 19, 402, 11, 19, 3, 19, 5, 19, 405, 10, 19, 3, 19, 5, 19, 408, 10, 19, 3, 19, 3, 19, 3, 20, 3, 20, 3, 20, 3, 20, 3, 20, 7, 20, 417, 10, 20, 12, 20, 14, 20, 420, 11, 20, 3, 20, 3, 20, 3, 20, 3, 20, 3, 20, 3, 21, 6, 21, 428, 10, 21, 13, 21, 14, 21, 429, 3, 21, 3, 21, 3, 22, 3, 22, 3, 22, 3, 22, 3, 22, 3, 23, 3, 23, 3, 23, 3, 23, 3, 23, 3, 24, 3, 24, 3, 24, 3, 24, 3, 25, 3, 25, 3, 25, 3, 25, 3, 26, 3, 26, 3, 26, 3, 26, 3, 27, 3, 27, 3, 27, 3, 27, 3, 28, 3, 28, 3, 29, 3, 29, 3, 30, 3, 30, 3, 30, 3, 31, 3, 31, 3, 32, 3, 32, 5, 32, 471, 10, 32, 3, 32, 6, 32, 474, 10, 32, 13, 32, 14, 32, 475, 3, 33, 3, 33, 3, 33, 7, 33, 481, 10, 33, 12, 33, 14, 33, 484, 11, 33, 3, 33, 3, 33, 3, 33, 3, 33, 3, 33, 3, 33, 7, 33, 492, 10, 33, 12, 33, 14, 33, 495, 11, 33, 3, 33, 3, 33, 3, 33, 3, 33, 3, 33, 5, 33, 502, 10, 33, 3, 33, 5, 33, 505, 10, 33, 5, 33, 507, 10, 33, 3, 34, 6, 34, 510, 10, 34, 13, 34, 14, 34, 511, 3, 35, 6, 35, 515, 10, 35, 13, 35, 14, 35, 516, 3, 35, 3, 35, 7, 35, 521, 10, 35, 12, 35, 14, 35, 524, 11, 35, 3, 35, 3, 35, 6, 35, 528, 10, 35, 13, 35, 14, 35, 529, 3, 35, 6, 35, 533, 10, 35, 13, 35, 14, 35, 534, 3, 35, 3, 35, 7, 35, 539, 10, 35, 12, 35, 14, 35, 542, 11, 35, 5, 35, 544, 10, 35, 3, 35, 3, 35, 3, 35, 3, 35, 6, 35, 550, 10, 35, 13, 35, 14, 35, 551, 3, 35, 3, 35, 5, 35, 556, 10, 35, 3, 36, 3, 36, 3, 36, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 3, 37, 5, 37, 655, 10, 37, 3, 38, 3, 38, 3, 38, 3, 38, 3, 39, 3, 39, 3, 40, 3, 40, 3, 41, 3, 41, 3, 42, 3, 42, 3, 43, 3, 43, 3, 43, 3, 43, 3, 43, 3, 44, 3, 44, 3, 44, 3, 44, 3, 44, 3, 45, 3, 45, 3, 45, 3, 45, 3, 46, 3, 46, 3, 46, 3, 46, 3, 46, 3, 47, 3, 47, 3, 47, 3, 47, 3, 47, 3, 47, 3, 48, 3, 48, 3, 48, 3, 49, 3, 49, 3, 49, 3, 50, 3, 50, 3, 50, 3, 51, 3, 51, 3, 51, 3, 51, 3, 51, 3, 52, 3, 52, 3, 52, 3, 53, 3, 53, 3, 54, 3, 54, 3, 55, 3, 55, 3, 55, 3, 55, 3, 55, 3, 56, 3, 56, 3, 56, 3, 56, 3, 56, 3, 56, 3, 56, 3, 56, 3, 56, 3, 56, 3, 57, 3, 57, 3, 57, 3, 57, 3, 57, 3, 57, 3, 57, 3, 57, 3, 57, 5, 57, 739, 10, 57, 3, 58, 3, 58, 3, 58, 3, 58, 3, 58, 3, 58, 3, 58, 3, 58, 3, 58, 3, 58, 5, 58, 751, 10, 58, 3, 59, 3, 59, 3, 60, 3, 60, 3, 61, 3, 61, 3, 62, 3, 62, 3, 63, 3, 63, 3, 64, 3, 64, 3, 64, 3, 65, 3, 65, 3, 65, 3, 65, 3, 65, 3, 65, 3, 65, 5, 65, 773, 10, 65, 3, 66, 3, 66, 3, 66, 3, 66, 3, 66, 3, 66, 3, 67, 3, 67, 3, 67, 3, 67, 3, 67, 3, 67, 3, 67, 3, 67, 3, 67, 5, 67, 790, 10, 67, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 5, 68, 1222, 10, 68, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 3, 69, 5, 69, 1375, 10, 69, 3, 70, 3, 70, 3, 70, 3, 70, 3, 70, 3, 70, 3, 70, 3, 70, 3, 70, 3, 70, 3, 70, 3, 71, 3, 71, 3, 71, 3, 71, 3, 71, 7, 71, 1393, 10, 71, 12, 71, 14, 71, 1396, 11, 71, 3, 71, 3, 71, 3, 71, 3, 71, 3, 71, 6, 71, 1403, 10, 71, 13, 71, 14, 71, 1404, 5, 71, 1407, 10, 71, 3, 72, 3, 72, 3, 72, 3, 72, 7, 72, 1413, 10, 72, 12, 72, 14, 72, 1416, 11, 72, 3, 72, 3, 72, 3, 73, 3, 73, 3, 73, 3, 73, 3, 74, 3, 74, 3, 74, 3, 74, 3, 75, 3, 75, 3, 75, 3, 75, 3, 76, 3, 76, 3, 76, 3, 76, 3, 76, 3, 77, 3, 77, 3, 77, 3, 77, 3, 77, 3, 77, 3, 78, 3, 78, 3, 78, 3, 78, 3, 78, 3, 78, 3, 79, 3, 79, 3, 79, 3, 79, 3, 80, 3, 80, 3, 80, 3, 80, 3, 81, 3, 81, 3, 81, 3, 81, 3, 81, 3, 81, 3, 81, 3, 81, 3, 81, 3, 82, 6, 82, 1467, 10, 82, 13, 82, 14, 82, 1468, 3, 83, 6, 83, 1472, 10, 83, 13, 83, 14, 83, 1473, 3, 83, 3, 83, 5, 83, 1478, 10, 83, 3, 84, 3, 84, 3, 85, 3, 85, 3, 85, 3, 85, 3, 86, 3, 86, 3, 86, 3, 86, 3, 87, 3, 87, 3, 87, 3, 87, 3, 88, 3, 88, 3, 88, 3, 89, 3, 89, 3, 89, 3, 89, 3, 89, 3, 90, 3, 90, 3, 90, 3, 90, 3, 90, 3, 91, 3, 91, 3, 91, 3, 91, 3, 91, 3, 91, 3, 92, 3, 92, 3, 92, 3, 92, 3, 93, 3, 93, 3, 93, 3, 93, 3, 94, 6, 94, 1522, 10, 94, 13, 94, 14, 94, 1523, 3, 95, 6, 95, 1527, 10, 95, 13, 95, 14, 95, 1528, 3, 95, 3, 95, 5, 95, 1533, 10, 95, 3, 96, 3, 96, 3, 97, 3, 97, 3, 97, 3, 97, 3, 98, 3, 98, 3, 98, 3, 98, 3, 99, 3, 99, 3, 99, 3, 99, 3, 100, 3, 100, 3, 101, 3, 101, 3, 102, 3, 102, 3, 103, 3, 103, 3, 104, 3, 104, 3, 105, 3, 105, 3, 106, 3, 106, 3, 107, 3, 107, 3, 108, 3, 108, 3, 109, 3, 109, 3, 110, 3, 110, 3, 111, 3, 111, 3, 112, 3, 112, 3, 113, 3, 113, 3, 114, 3, 114, 3, 115, 3, 115, 3, 116, 3, 116, 3, 117, 3, 117, 3, 118, 3, 118, 3, 119, 3, 119, 3, 120, 3, 120, 3, 121, 3, 121, 3, 122, 3, 122, 3, 123, 3, 123, 3, 124, 3, 124, 3, 125, 3, 125, 4, 418, 493, 2, 2, 126, 7, 2, 3, 9, 2, 4, 11, 2, 5, 13, 2, 6, 15, 2, 7, 17, 2, 8, 19, 2, 9, 21, 2, 10, 23, 2, 11, 25, 2, 12, 27, 2, 13, 29, 2, 14, 31, 2, 15, 33, 2, 16, 35, 2, 17, 37, 2, 18, 39, 2, 19, 41, 2, 20, 43, 2, 21, 45, 2, 22, 47, 2, 2, 49, 2, 83, 51, 2, 23, 53, 2, 24, 55, 2, 25, 57, 2, 26, 59, 2, 2, 61, 2, 2, 63, 2, 2, 65, 2, 2, 67, 2, 2, 69, 2, 27, 71, 2, 28, 73, 2, 29, 75, 2, 30, 77, 2, 31, 79, 2, 32, 81, 2, 33, 83, 2, 34, 85, 2, 35, 87, 2, 36, 89, 2, 37, 91, 2, 38, 93, 2, 39, 95, 2, 40, 97, 2, 41, 99, 2, 42, 101, 2, 43, 103, 2, 44, 105, 2, 45, 107, 2, 46, 109, 2, 47, 111, 2, 48, 113, 2, 49, 115, 2, 50, 117, 2, 51, 119, 2, 52, 121, 2, 53, 123, 2, 54, 125, 2, 55, 127, 2, 56, 129, 2, 57, 131, 2, 58, 133, 2, 59, 135, 2, 60, 137, 2, 61, 139, 2, 62, 141, 2, 63, 143, 2, 64, 145, 2, 65, 147, 2, 66, 149, 2, 67, 151, 2, 68, 153, 2, 69, 155, 2, 2, 157, 2, 2, 159, 2, 2, 161, 2, 2, 163, 2, 2, 165, 2, 70, 167, 2, 71, 169, 2, 2, 171, 2, 72, 173, 2, 73, 175, 2, 74, 177, 2, 75, 179, 2, 76, 181, 2, 77, 183, 2, 2, 185, 2, 2, 187, 2, 2, 189, 2, 2, 191, 2, 78, 193, 2, 2, 195, 2, 79, 197, 2, 80, 199, 2, 81, 201, 2, 82, 203, 2, 2, 205, 2, 2, 207, 2, 2, 209, 2, 2, 211, 2, 2, 213, 2, 2, 215, 2, 2, 217, 2, 2, 219, 2, 2, 221, 2, 2, 223, 2, 2, 225, 2, 2, 227, 2, 2, 229, 2, 2, 231, 2, 2, 233, 2, 2, 235, 2, 2, 237, 2, 2, 239, 2, 2, 241, 2, 2, 243, 2, 2, 245, 2, 2, 247, 2, 2, 249, 2, 2, 251, 2, 2, 253, 2, 2, 7, 2, 3, 4, 5, 6, 39, 4, 2, 12, 12, 15, 15, 5, 2, 11, 12, 15, 15, 34, 34, 3, 2, 50, 59, 4, 2, 67, 92, 99, 124, 7, 2, 36, 36, 94, 94, 112, 112, 116, 116, 118, 118, 6, 2, 12, 12, 15, 15, 36, 36, 94, 94, 4, 2, 71, 71, 103, 103, 4, 2, 45, 45, 47, 47, 4, 2, 66, 66, 97, 97, 3, 2, 98, 98, 12, 2, 11, 12, 15, 15, 34, 34, 46, 46, 49, 49, 63, 63, 93, 93, 95, 95, 98, 98, 126, 126, 4, 2, 44, 44, 49, 49, 4, 2, 67, 67, 99, 99, 4, 2, 68, 68, 100, 100, 4, 2, 69, 69, 101, 101, 4, 2, 70, 70, 102, 102, 4, 2, 72, 72, 104, 104, 4, 2, 73, 73, 105, 105, 4, 2, 74, 74, 106, 106, 4, 2, 75, 75, 107, 107, 4, 2, 76, 76, 108, 108, 4, 2, 77, 77, 109, 109, 4, 2, 78, 78, 110, 110, 4, 2, 79, 79, 111, 111, 4, 2, 80, 80, 112, 112, 4, 2, 81, 81, 113, 113, 4, 2, 82, 82, 114, 114, 4, 2, 83, 83, 115, 115, 4, 2, 84, 84, 116, 116, 4, 2, 85, 85, 117, 117, 4, 2, 86, 86, 118, 118, 4, 2, 87, 87, 119, 119, 4, 2, 88, 88, 120, 120, 4, 2, 89, 89, 121, 121, 4, 2, 90, 90, 122, 122, 4, 2, 91, 91, 123, 123, 4, 2, 92, 92, 124, 124, 2, 1700, 2, 7, 3, 2, 2, 2, 2, 9, 3, 2, 2, 2, 2, 11, 3, 2, 2, 2, 2, 13, 3, 2, 2, 2, 2, 15, 3, 2, 2, 2, 2, 17, 3, 2, 2, 2, 2, 19, 3, 2, 2, 2, 2, 21, 3, 2, 2, 2, 2, 23, 3, 2, 2, 2, 2, 25, 3, 2, 2, 2, 2, 27, 3, 2, 2, 2, 2, 29, 3, 2, 2, 2, 2, 31, 3, 2, 2, 2, 2, 33, 3, 2, 2, 2, 2, 35, 3, 2, 2, 2, 2, 37, 3, 2, 2, 2, 2, 39, 3, 2, 2, 2, 2, 41, 3, 2, 2, 2, 2, 43, 3, 2, 2, 2, 2, 45, 3, 2, 2, 2, 3, 47, 3, 2, 2, 2, 3, 49, 3, 2, 2, 2, 3, 51, 3, 2, 2, 2, 3, 53, 3, 2, 2, 2, 3, 55, 3, 2, 2, 2, 4, 57, 3, 2, 2, 2, 4, 69, 3, 2, 2, 2, 4, 71, 3, 2, 2, 2, 4, 73, 3, 2, 2, 2, 4, 75, 3, 2, 2, 2, 4, 77, 3, 2, 2, 2, 4, 79, 3, 2, 2, 2, 4, 81, 3, 2, 2, 2, 4, 83, 3, 2, 2, 2, 4, 85, 3, 2, 2, 2, 4, 87, 3, 2, 2, 2, 4, 89, 3, 2, 2, 2, 4, 91, 3, 2, 2, 2, 4, 93, 3, 2, 2, 2, 4, 95, 3, 2, 2, 2, 4, 97, 3, 2, 2, 2, 4, 99, 3, 2, 2, 2, 4, 101, 3, 2, 2, 2, 4, 103, 3, 2, 2, 2, 4, 105, 3, 2, 2, 2, 4, 107, 3, 2, 2, 2, 4, 109, 3, 2, 2, 2, 4, 111, 3, 2, 2, 2, 4, 113, 3, 2, 2, 2, 4, 115, 3, 2, 2, 2, 4, 117, 3, 2, 2, 2, 4, 119, 3, 2, 2, 2, 4, 121, 3, 2, 2, 2, 4, 123, 3, 2, 2, 2, 4, 125, 3, 2, 2, 2, 4, 127, 3, 2, 2, 2, 4, 129, 3, 2, 2, 2, 4, 131, 3, 2, 2, 2, 4, 133, 3, 2, 2, 2, 4, 135, 3, 2, 2, 2, 4, 137, 3, 2, 2, 2, 4, 139, 3, 2, 2, 2, 4, 141, 3, 2, 2, 2, 4, 143, 3, 2, 2, 2, 4, 145, 3, 2, 2, 2, 4, 147, 3, 2, 2, 2, 4, 149, 3, 2, 2, 2, 4, 151, 3, 2, 2, 2, 4, 153, 3, 2, 2, 2, 5, 155, 3, 2, 2, 2, 5, 157, 3, 2, 2, 2, 5, 159, 3, 2, 2, 2, 5, 161, 3, 2, 2, 2, 5, 163, 3, 2, 2, 2, 5, 165, 3, 2, 2, 2, 5, 167, 3, 2, 2, 2, 5, 171, 3, 2, 2, 2, 5, 173, 3, 2, 2, 2, 5, 175, 3, 2, 2, 2, 5, 177, 3, 2, 2, 2, 6, 179, 3, 2, 2, 2, 6, 181, 3, 2, 2, 2, 6, 183, 3, 2, 2, 2, 6, 185, 3, 2, 2, 2, 6, 187, 3, 2, 2, 2, 6, 189, 3, 2, 2, 2, 6, 191, 3, 2, 2, 2, 6, 195, 3, 2, 2, 2, 6, 197, 3, 2, 2, 2, 6, 199, 3, 2, 2, 2, 6, 201, 3, 2, 2, 2, 7, 255, 3, 2, 2, 2, 9, 265, 3, 2, 2, 2, 11, 272, 3, 2, 2, 2, 13, 279, 3, 2, 2, 2, 15, 289, 3, 2, 2, 2, 17, 296, 3, 2, 2, 2, 19, 302, 3, 2, 2, 2, 21, 310, 3, 2, 2, 2, 23, 318, 3, 2, 2, 2, 25, 325, 3, 2, 2, 2, 27, 337, 3, 2, 2, 2, 29, 345, 3, 2, 2, 2, 31, 355, 3, 2, 2, 2, 33, 362, 3, 2, 2, 2, 35, 371, 3, 2, 2, 2, 37, 378, 3, 2, 2, 2, 39, 387, 3, 2, 2, 2, 41, 394, 3, 2, 2, 2, 43, 411, 3, 2, 2, 2, 45, 427, 3, 2, 2, 2, 47, 433, 3, 2, 2, 2, 49, 438, 3, 2, 2, 2, 51, 443, 3, 2, 2, 2, 53, 447, 3, 2, 2, 2, 55, 451, 3, 2, 2, 2, 57, 455, 3, 2, 2, 2, 59, 459, 3, 2, 2, 2, 61, 461, 3, 2, 2, 2, 63, 463, 3, 2, 2, 2, 65, 466, 3, 2, 2, 2, 67, 468, 3, 2, 2, 2, 69, 506, 3, 2, 2, 2, 71, 509, 3, 2, 2, 2, 73, 555, 3, 2, 2, 2, 75, 557, 3, 2, 2, 2, 77, 654, 3, 2, 2, 2, 79, 656, 3, 2, 2, 2, 81, 660, 3, 2, 2, 2, 83, 662, 3, 2, 2, 2, 85, 664, 3, 2, 2, 2, 87, 666, 3, 2, 2, 2, 89, 668, 3, 2, 2, 2, 91, 673, 3, 2, 2, 2, 93, 678, 3, 2, 2, 2, 95, 682, 3, 2, 2, 2, 97, 687, 3, 2, 2, 2, 99, 693, 3, 2, 2, 2, 101, 696, 3, 2, 2, 2, 103, 699, 3, 2, 2, 2, 105, 702, 3, 2, 2, 2, 107, 707, 3, 2, 2, 2, 109, 710, 3, 2, 2, 2, 111, 712, 3, 2, 2, 2, 113, 714, 3, 2, 2, 2, 115, 719, 3, 2, 2, 2, 117, 738, 3, 2, 2, 2, 119, 750, 3, 2, 2, 2, 121, 752, 3, 2, 2, 2, 123, 754, 3, 2, 2, 2, 125, 756, 3, 2, 2, 2, 127, 758, 3, 2, 2, 2, 129, 760, 3, 2, 2, 2, 131, 762, 3, 2, 2, 2, 133, 772, 3, 2, 2, 2, 135, 774, 3, 2, 2, 2, 137, 789, 3, 2, 2, 2, 139, 1221, 3, 2, 2, 2, 141, 1374, 3, 2, 2, 2, 143, 1376, 3, 2, 2, 2, 145, 1406, 3, 2, 2, 2, 147, 1408, 3, 2, 2, 2, 149, 1419, 3, 2, 2, 2, 151, 1423, 3, 2, 2, 2, 153, 1427, 3, 2, 2, 2, 155, 1431, 3, 2, 2, 2, 157, 1436, 3, 2, 2, 2, 159, 1442, 3, 2, 2, 2, 161, 1448, 3, 2, 2, 2, 163, 1452, 3, 2, 2, 2, 165, 1456, 3, 2, 2, 2, 167, 1466, 3, 2, 2, 2, 169, 1477, 3, 2, 2, 2, 171, 1479, 3, 2, 2, 2, 173, 1481, 3, 2, 2, 2, 175, 1485, 3, 2, 2, 2, 177, 1489, 3, 2, 2, 2, 179, 1493, 3, 2, 2, 2, 181, 1496, 3, 2, 2, 2, 183, 1501, 3, 2, 2, 2, 185, 1506, 3, 2, 2, 2, 187, 1512, 3, 2, 2, 2, 189, 1516, 3, 2, 2, 2, 191, 1521, 3, 2, 2, 2, 193, 1532, 3, 2, 2, 2, 195, 1534, 3, 2, 2, 2, 197, 1536, 3, 2, 2, 2, 199, 1540, 3, 2, 2, 2, 201, 1544, 3, 2, 2, 2, 203, 1548, 3, 2, 2, 2, 205, 1550, 3, 2, 2, 2, 207, 1552, 3, 2, 2, 2, 209, 1554, 3, 2, 2, 2, 211, 1556, 3, 2, 2, 2, 213, 1558, 3, 2, 2, 2, 215, 1560, 3, 2, 2, 2, 217, 1562, 3, 2, 2, 2, 219, 1564, 3, 2, 2, 2, 221, 1566, 3, 2, 2, 2, 223, 1568, 3, 2, 2, 2, 225, 1570, 3, 2, 2, 2, 227, 1572, 3, 2, 2, 2, 229, 1574, 3, 2, 2, 2, 231, 1576, 3, 2, 2, 2, 233, 1578, 3, 2, 2, 2, 235, 1580, 3, 2, 2, 2, 237, 1582, 3, 2, 2, 2, 239, 1584, 3, 2, 2, 2, 241, 1586, 3, 2, 2, 2, 243, 1588, 3, 2, 2, 2, 245, 1590, 3, 2, 2, 2, 247, 1592, 3, 2, 2, 2, 249, 1594, 3, 2, 2, 2, 251, 1596, 3, 2, 2, 2, 253, 1598, 3, 2, 2, 2, 255, 256, 5, 209, 103, 2, 256, 257, 5, 219, 108, 2, 257, 258, 5, 239, 118, 2, 258, 259, 5, 239, 118, 2, 259, 260, 5, 211, 104, 2, 260, 261, 5, 207, 102, 2, 261, 262, 5, 241, 119, 2, 262, 263, 3, 2, 2, 2, 263, 264, 8, 2, 2, 2, 264, 8, 3, 2, 2, 2, 265, 266, 5, 215, 106, 2, 266, 267, 5, 237, 117, 2, 267, 268, 5, 231, 114, 2, 268, 269, 5, 223, 110, 2, 269, 270, 3, 2, 2, 2, 270, 271, 8, 3, 2, 2, 271, 10, 3, 2, 2, 2, 272, 273, 5, 211, 104, 2, 273, 274, 5, 245, 121, 2, 274, 275, 5, 203, 100, 2, 275, 276, 5, 225, 111, 2, 276, 277, 3, 2, 2, 2, 277, 278, 8, 4, 2, 2, 278, 12, 3, 2, 2, 2, 279, 280, 5, 211, 104, 2, 280, 281, 5, 249, 123, 2, 281, 282, 5, 233, 115, 2, 282, 283, 5, 225, 111, 2, 283, 284, 5, 203, 100, 2, 284, 285, 5, 219, 108, 2, 285, 286, 5, 229, 113, 2, 286, 287, 3, 2, 2, 2, 287, 288, 8, 5, 3, 2, 288, 14, 3, 2, 2, 2, 289, 290, 5, 213, 105, 2, 290, 291, 5, 237, 117, 2, 291, 292, 5, 231, 114, 2, 292, 293, 5, 227, 112, 2, 293, 294, 3, 2, 2, 2, 294, 295, 8, 6, 4, 2, 295, 16, 3, 2, 2, 2, 296, 297, 5, 237, 117, 2, 297, 298, 5, 231, 114, 2, 298, 299, 5, 247, 122, 2, 299, 300, 3, 2, 2, 2, 300, 301, 8, 7, 2, 2, 301, 18, 3, 2, 2, 2, 302, 303, 5, 239, 118, 2, 303, 304, 5, 241, 119, 2, 304, 305, 5, 203, 100, 2, 305, 306, 5, 241, 119, 2, 306, 307, 5, 239, 118, 2, 307, 308, 3, 2, 2, 2, 308, 309, 8, 8, 2, 2, 309, 20, 3, 2, 2, 2, 310, 311, 5, 247, 122, 2, 311, 312, 5, 217, 107, 2, 312, 313, 5, 211, 104, 2, 313, 314, 5, 237, 117, 2, 314, 315, 5, 211, 104, 2, 315, 316, 3, 2, 2, 2, 316, 317, 8, 9, 2, 2, 317, 22, 3, 2, 2, 2, 318, 319, 5, 239, 118, 2, 319, 320, 5, 231, 114, 2, 320, 321, 5, 237, 117, 2, 321, 322, 5, 241, 119, 2, 322, 323, 3, 2, 2, 2, 323, 324, 8, 10, 2, 2, 324, 24, 3, 2, 2, 2, 325, 326, 5, 227, 112, 2, 326, 327, 5, 245, 121, 2, 327, 328, 5, 111, 54, 2, 328, 329, 5, 211, 104, 2, 329, 330, 5, 249, 123, 2, 330, 331, 5, 233, 115, 2, 331, 332, 5, 203, 100, 2, 332, 333, 5, 229, 113, 2, 333, 334, 5, 209, 103, 2, 334, 335, 3, 2, 2, 2, 335, 336, 8, 11, 2, 2, 336, 26, 3, 2, 2, 2, 337, 338, 5, 225, 111, 2, 338, 339, 5, 219, 108, 2, 339, 340, 5, 227, 112, 2, 340, 341, 5, 219, 108, 2, 341, 342, 5, 241, 119, 2, 342, 343, 3, 2, 2, 2, 343, 344, 8, 12, 2, 2, 344, 28, 3, 2, 2, 2, 345, 346, 5, 233, 115, 2, 346, 347, 5, 237, 117, 2, 347, 348, 5, 231, 114, 2, 348, 349, 5, 221, 109, 2, 349, 350, 5, 211, 104, 2, 350, 351, 5, 207, 102, 2, 351, 352, 5, 241, 119, 2, 352, 353, 3, 2, 2, 2, 353, 354, 8, 13, 2, 2, 354, 30, 3, 2, 2, 2, 355, 356, 5, 209, 103, 2, 356, 357, 5, 237, 117, 2, 357, 358, 5, 231, 114, 2, 358, 359, 5, 233, 115, 2, 359, 360, 3, 2, 2, 2, 360, 361, 8, 14, 2, 2, 361, 32, 3, 2, 2, 2, 362, 363, 5, 237, 117, 2, 363, 364, 5, 211, 104, 2, 364, 365, 5, 229, 113, 2, 365, 366, 5, 203, 100, 2, 366, 367, 5, 227, 112, 2, 367, 368, 5, 211, 104, 2, 368, 369, 3, 2, 2, 2, 369, 370, 8, 15, 2, 2, 370, 34, 3, 2, 2, 2, 371, 372, 5, 239, 118, 2, 372, 373, 5, 217, 107, 2, 373, 374, 5, 231, 114, 2, 374, 375, 5, 247, 122, 2, 375, 376, 3, 2, 2, 2, 376, 377, 8, 16, 2, 2, 377, 36, 3, 2, 2, 2, 378, 379, 5, 211, 104, 2, 379, 380, 5, 229, 113, 2, 380, 381, 5, 237, 117, 2, 381, 382, 5, 219, 108, 2, 382, 383, 5, 207, 102, 2, 383, 384, 5, 217, 107, 2, 384, 385, 3, 2, 2, 2, 385, 386, 8, 17, 5, 2, 386, 38, 3, 2, 2, 2, 387, 388, 5, 223, 110, 2, 388, 389, 5, 211, 104, 2, 389, 390, 5, 211, 104, 2, 390, 391, 5, 233, 115, 2, 391, 392, 3, 2, 2, 2, 392, 393, 8, 18, 2, 2, 393, 40, 3, 2, 2, 2, 394, 395, 7, 49, 2, 2, 395, 396, 7, 49, 2, 2, 396, 400, 3, 2, 2, 2, 397, 399, 10, 2, 2, 2, 398, 397, 3, 2, 2, 2, 399, 402, 3, 2, 2, 2, 400, 398, 3, 2, 2, 2, 400, 401, 3, 2, 2, 2, 401, 404, 3, 2, 2, 2, 402, 400, 3, 2, 2, 2, 403, 405, 7, 15, 2, 2, 404, 403, 3, 2, 2, 2, 404, 405, 3, 2, 2, 2, 405, 407, 3, 2, 2, 2, 406, 408, 7, 12, 2, 2, 407, 406, 3, 2, 2, 2, 407, 408, 3, 2, 2, 2, 408, 409, 3, 2, 2, 2, 409, 410, 8, 19, 6, 2, 410, 42, 3, 2, 2, 2, 411, 412, 7, 49, 2, 2, 412, 413, 7, 44, 2, 2, 413, 418, 3, 2, 2, 2, 414, 417, 5, 43, 20, 2, 415, 417, 11, 2, 2, 2, 416, 414, 3, 2, 2, 2, 416, 415, 3, 2, 2, 2, 417, 420, 3, 2, 2, 2, 418, 419, 3, 2, 2, 2, 418, 416, 3, 2, 2, 2, 419, 421, 3, 2, 2, 2, 420, 418, 3, 2, 2, 2, 421, 422, 7, 44, 2, 2, 422, 423, 7, 49, 2, 2, 423, 424, 3, 2, 2, 2, 424, 425, 8, 20, 6, 2, 425, 44, 3, 2, 2, 2, 426, 428, 9, 3, 2, 2, 427, 426, 3, 2, 2, 2, 428, 429, 3, 2, 2, 2, 429, 427, 3, 2, 2, 2, 429, 430, 3, 2, 2, 2, 430, 431, 3, 2, 2, 2, 431, 432, 8, 21, 6, 2, 432, 46, 3, 2, 2, 2, 433, 434, 7, 93, 2, 2, 434, 435, 3, 2, 2, 2, 435, 436, 8, 22, 7, 2, 436, 437, 8, 22, 8, 2, 437, 48, 3, 2, 2, 2, 438, 439, 7, 126, 2, 2, 439, 440, 3, 2, 2, 2, 440, 441, 8, 23, 9, 2, 441, 442, 8, 23, 10, 2, 442, 50, 3, 2, 2, 2, 443, 444, 5, 45, 21, 2, 444, 445, 3, 2, 2, 2, 445, 446, 8, 24, 6, 2, 446, 52, 3, 2, 2, 2, 447, 448, 5, 41, 19, 2, 448, 449, 3, 2, 2, 2, 449, 450, 8, 25, 6, 2, 450, 54, 3, 2, 2, 2, 451, 452, 5, 43, 20, 2, 452, 453, 3, 2, 2, 2, 453, 454, 8, 26, 6, 2, 454, 56, 3, 2, 2, 2, 455, 456, 7, 126, 2, 2, 456, 457, 3, 2, 2, 2, 457, 458, 8, 27, 10, 2, 458, 58, 3, 2, 2, 2, 459, 460, 9, 4, 2, 2, 460, 60, 3, 2, 2, 2, 461, 462, 9, 5, 2, 2, 462, 62, 3, 2, 2, 2, 463, 464, 7, 94, 2, 2, 464, 465, 9, 6, 2, 2, 465, 64, 3, 2, 2, 2, 466, 467, 10, 7, 2, 2, 467, 66, 3, 2, 2, 2, 468, 470, 9, 8, 2, 2, 469, 471, 9, 9, 2, 2, 470, 469, 3, 2, 2, 2, 470, 471, 3, 2, 2, 2, 471, 473, 3, 2, 2, 2, 472, 474, 5, 59, 28, 2, 473, 472, 3, 2, 2, 2, 474, 475, 3, 2, 2, 2, 475, 473, 3, 2, 2, 2, 475, 476, 3, 2, 2, 2, 476, 68, 3, 2, 2, 2, 477, 482, 7, 36, 2, 2, 478, 481, 5, 63, 30, 2, 479, 481, 5, 65, 31, 2, 480, 478, 3, 2, 2, 2, 480, 479, 3, 2, 2, 2, 481, 484, 3, 2, 2, 2, 482, 480, 3, 2, 2, 2, 482, 483, 3, 2, 2, 2, 483, 485, 3, 2, 2, 2, 484, 482, 3, 2, 2, 2, 485, 507, 7, 36, 2, 2, 486, 487, 7, 36, 2, 2, 487, 488, 7, 36, 2, 2, 488, 489, 7, 36, 2, 2, 489, 493, 3, 2, 2, 2, 490, 492, 10, 2, 2, 2, 491, 490, 3, 2, 2, 2, 492, 495, 3, 2, 2, 2, 493, 494, 3, 2, 2, 2, 493, 491, 3, 2, 2, 2, 494, 496, 3, 2, 2, 2, 495, 493, 3, 2, 2, 2, 496, 497, 7, 36, 2, 2, 497, 498, 7, 36, 2, 2, 498, 499, 7, 36, 2, 2, 499, 501, 3, 2, 2, 2, 500, 502, 7, 36, 2, 2, 501, 500, 3, 2, 2, 2, 501, 502, 3, 2, 2, 2, 502, 504, 3, 2, 2, 2, 503, 505, 7, 36, 2, 2, 504, 503, 3, 2, 2, 2, 504, 505, 3, 2, 2, 2, 505, 507, 3, 2, 2, 2, 506, 477, 3, 2, 2, 2, 506, 486, 3, 2, 2, 2, 507, 70, 3, 2, 2, 2, 508, 510, 5, 59, 28, 2, 509, 508, 3, 2, 2, 2, 510, 511, 3, 2, 2, 2, 511, 509, 3, 2, 2, 2, 511, 512, 3, 2, 2, 2, 512, 72, 3, 2, 2, 2, 513, 515, 5, 59, 28, 2, 514, 513, 3, 2, 2, 2, 515, 516, 3, 2, 2, 2, 516, 514, 3, 2, 2, 2, 516, 517, 3, 2, 2, 2, 517, 518, 3, 2, 2, 2, 518, 522, 5, 85, 41, 2, 519, 521, 5, 59, 28, 2, 520, 519, 3, 2, 2, 2, 521, 524, 3, 2, 2, 2, 522, 520, 3, 2, 2, 2, 522, 523, 3, 2, 2, 2, 523, 556, 3, 2, 2, 2, 524, 522, 3, 2, 2, 2, 525, 527, 5, 85, 41, 2, 526, 528, 5, 59, 28, 2, 527, 526, 3, 2, 2, 2, 528, 529, 3, 2, 2, 2, 529, 527, 3, 2, 2, 2, 529, 530, 3, 2, 2, 2, 530, 556, 3, 2, 2, 2, 531, 533, 5, 59, 28, 2, 532, 531, 3, 2, 2, 2, 533, 534, 3, 2, 2, 2, 534, 532, 3, 2, 2, 2, 534, 535, 3, 2, 2, 2, 535, 543, 3, 2, 2, 2, 536, 540, 5, 85, 41, 2, 537, 539, 5, 59, 28, 2, 538, 537, 3, 2, 2, 2, 539, 542, 3, 2, 2, 2, 540, 538, 3, 2, 2, 2, 540, 541, 3, 2, 2, 2, 541, 544, 3, 2, 2, 2, 542, 540, 3, 2, 2, 2, 543, 536, 3, 2, 2, 2, 543, 544, 3, 2, 2, 2, 544, 545, 3, 2, 2, 2, 545, 546, 5, 67, 32, 2, 546, 556, 3, 2, 2, 2, 547, 549, 5, 85, 41, 2, 548, 550, 5, 59, 28, 2, 549, 548, 3, 2, 2, 2, 550, 551, 3, 2, 2, 2, 551, 549, 3, 2, 2, 2, 551, 552, 3, 2, 2, 2, 552, 553, 3, 2, 2, 2, 553, 554, 5, 67, 32, 2, 554, 556, 3, 2, 2, 2, 555, 514, 3, 2, 2, 2, 555, 525, 3, 2, 2, 2, 555, 532, 3, 2, 2, 2, 555, 547, 3, 2, 2, 2, 556, 74, 3, 2, 2, 2, 557, 558, 7, 100, 2, 2, 558, 559, 7, 123, 2, 2, 559, 76, 3, 2, 2, 2, 560, 561, 7, 123, 2, 2, 561, 562, 7, 103, 2, 2, 562, 563, 7, 99, 2, 2, 563, 655, 7, 116, 2, 2, 564, 565, 7, 111, 2, 2, 565, 566, 7, 113, 2, 2, 566, 567, 7, 112, 2, 2, 567, 568, 7, 118, 2, 2, 568, 655, 7, 106, 2, 2, 569, 570, 7, 102, 2, 2, 570, 571, 7, 99, 2, 2, 571, 655, 7, 123, 2, 2, 572, 573, 7, 117, 2, 2, 573, 574, 7, 103, 2, 2, 574, 575, 7, 101, 2, 2, 575, 576, 7, 113, 2, 2, 576, 577, 7, 112, 2, 2, 577, 655, 7, 102, 2, 2, 578, 579, 7, 111, 2, 2, 579, 580, 7, 107, 2, 2, 580, 581, 7, 112, 2, 2, 581, 582, 7, 119, 2, 2, 582, 583, 7, 118, 2, 2, 583, 655, 7, 103, 2, 2, 584, 585, 7, 106, 2, 2, 585, 586, 7, 113, 2, 2, 586, 587, 7, 119, 2, 2, 587, 655, 7, 116, 2, 2, 588, 589, 7, 121, 2, 2, 589, 590, 7, 103, 2, 2, 590, 591, 7, 103, 2, 2, 591, 655, 7, 109, 2, 2, 592, 593, 7, 111, 2, 2, 593, 594, 7, 107, 2, 2, 594, 595, 7, 110, 2, 2, 595, 596, 7, 110, 2, 2, 596, 597, 7, 107, 2, 2, 597, 598, 7, 117, 2, 2, 598, 599, 7, 103, 2, 2, 599, 600, 7, 101, 2, 2, 600, 601, 7, 113, 2, 2, 601, 602, 7, 112, 2, 2, 602, 655, 7, 102, 2, 2, 603, 604, 7, 123, 2, 2, 604, 605, 7, 103, 2, 2, 605, 606, 7, 99, 2, 2, 606, 607, 7, 116, 2, 2, 607, 655, 7, 117, 2, 2, 608, 609, 7, 111, 2, 2, 609, 610, 7, 113, 2, 2, 610, 611, 7, 112, 2, 2, 611, 612, 7, 118, 2, 2, 612, 613, 7, 106, 2, 2, 613, 655, 7, 117, 2, 2, 614, 615, 7, 102, 2, 2, 615, 616, 7, 99, 2, 2, 616, 617, 7, 123, 2, 2, 617, 655, 7, 117, 2, 2, 618, 619, 7, 117, 2, 2, 619, 620, 7, 103, 2, 2, 620, 621, 7, 101, 2, 2, 621, 622, 7, 113, 2, 2, 622, 623, 7, 112, 2, 2, 623, 624, 7, 102, 2, 2, 624, 655, 7, 117, 2, 2, 625, 626, 7, 111, 2, 2, 626, 627, 7, 107, 2, 2, 627, 628, 7, 112, 2, 2, 628, 629, 7, 119, 2, 2, 629, 630, 7, 118, 2, 2, 630, 631, 7, 103, 2, 2, 631, 655, 7, 117, 2, 2, 632, 633, 7, 106, 2, 2, 633, 634, 7, 113, 2, 2, 634, 635, 7, 119, 2, 2, 635, 636, 7, 116, 2, 2, 636, 655, 7, 117, 2, 2, 637, 638, 7, 121, 2, 2, 638, 639, 7, 103, 2, 2, 639, 640, 7, 103, 2, 2, 640, 641, 7, 109, 2, 2, 641, 655, 7, 117, 2, 2, 642, 643, 7, 111, 2, 2, 643, 644, 7, 107, 2, 2, 644, 645, 7, 110, 2, 2, 645, 646, 7, 110, 2, 2, 646, 647, 7, 107, 2, 2, 647, 648, 7, 117, 2, 2, 648, 649, 7, 103, 2, 2, 649, 650, 7, 101, 2, 2, 650, 651, 7, 113, 2, 2, 651, 652, 7, 112, 2, 2, 652, 653, 7, 102, 2, 2, 653, 655, 7, 117, 2, 2, 654, 560, 3, 2, 2, 2, 654, 564, 3, 2, 2, 2, 654, 569, 3, 2, 2, 2, 654, 572, 3, 2, 2, 2, 654, 578, 3, 2, 2, 2, 654, 584, 3, 2, 2, 2, 654, 588, 3, 2, 2, 2, 654, 592, 3, 2, 2, 2, 654, 603, 3, 2, 2, 2, 654, 608, 3, 2, 2, 2, 654, 614, 3, 2, 2, 2, 654, 618, 3, 2, 2, 2, 654, 625, 3, 2, 2, 2, 654, 632, 3, 2, 2, 2, 654, 637, 3, 2, 2, 2, 654, 642, 3, 2, 2, 2, 655, 78, 3, 2, 2, 2, 656, 657, 7, 99, 2, 2, 657, 658, 7, 112, 2, 2, 658, 659, 7, 102, 2, 2, 659, 80, 3, 2, 2, 2, 660, 661, 7, 63, 2, 2, 661, 82, 3, 2, 2, 2, 662, 663, 7, 46, 2, 2, 663, 84, 3, 2, 2, 2, 664, 665, 7, 48, 2, 2, 665, 86, 3, 2, 2, 2, 666, 667, 7, 42, 2, 2, 667, 88, 3, 2, 2, 2, 668, 669, 7, 93, 2, 2, 669, 670, 3, 2, 2, 2, 670, 671, 8, 43, 2, 2, 671, 672, 8, 43, 2, 2, 672, 90, 3, 2, 2, 2, 673, 674, 7, 95, 2, 2, 674, 675, 3, 2, 2, 2, 675, 676, 8, 44, 10, 2, 676, 677, 8, 44, 10, 2, 677, 92, 3, 2, 2, 2, 678, 679, 5, 229, 113, 2, 679, 680, 5, 231, 114, 2, 680, 681, 5, 241, 119, 2, 681, 94, 3, 2, 2, 2, 682, 683, 5, 225, 111, 2, 683, 684, 5, 219, 108, 2, 684, 685, 5, 223, 110, 2, 685, 686, 5, 211, 104, 2, 686, 96, 3, 2, 2, 2, 687, 688, 5, 237, 117, 2, 688, 689, 5, 225, 111, 2, 689, 690, 5, 219, 108, 2, 690, 691, 5, 223, 110, 2, 691, 692, 5, 211, 104, 2, 692, 98, 3, 2, 2, 2, 693, 694, 5, 219, 108, 2, 694, 695, 5, 229, 113, 2, 695, 100, 3, 2, 2, 2, 696, 697, 5, 219, 108, 2, 697, 698, 5, 239, 118, 2, 698, 102, 3, 2, 2, 2, 699, 700, 5, 203, 100, 2, 700, 701, 5, 239, 118, 2, 701, 104, 3, 2, 2, 2, 702, 703, 5, 229, 113, 2, 703, 704, 5, 243, 120, 2, 704, 705, 5, 225, 111, 2, 705, 706, 5, 225, 111, 2, 706, 106, 3, 2, 2, 2, 707, 708, 7, 113, 2, 2, 708, 709, 7, 116, 2, 2, 709, 108, 3, 2, 2, 2, 710, 711, 7, 43, 2, 2, 711, 110, 3, 2, 2, 2, 712, 713, 7, 97, 2, 2, 713, 112, 3, 2, 2, 2, 714, 715, 7, 107, 2, 2, 715, 716, 7, 112, 2, 2, 716, 717, 7, 104, 2, 2, 717, 718, 7, 113, 2, 2, 718, 114, 3, 2, 2, 2, 719, 720, 7, 104, 2, 2, 720, 721, 7, 119, 2, 2, 721, 722, 7, 112, 2, 2, 722, 723, 7, 101, 2, 2, 723, 724, 7, 118, 2, 2, 724, 725, 7, 107, 2, 2, 725, 726, 7, 113, 2, 2, 726, 727, 7, 112, 2, 2, 727, 728, 7, 117, 2, 2, 728, 116, 3, 2, 2, 2, 729, 730, 7, 118, 2, 2, 730, 731, 7, 116, 2, 2, 731, 732, 7, 119, 2, 2, 732, 739, 7, 103, 2, 2, 733, 734, 7, 104, 2, 2, 734, 735, 7, 99, 2, 2, 735, 736, 7, 110, 2, 2, 736, 737, 7, 117, 2, 2, 737, 739, 7, 103, 2, 2, 738, 729, 3, 2, 2, 2, 738, 733, 3, 2, 2, 2, 739, 118, 3, 2, 2, 2, 740, 741, 7, 63, 2, 2, 741, 751, 7, 63, 2, 2, 742, 743, 7, 35, 2, 2, 743, 751, 7, 63, 2, 2, 744, 751, 7, 62, 2, 2, 745, 746, 7, 62, 2, 2, 746, 751, 7, 63, 2, 2, 747, 751, 7, 64, 2, 2, 748, 749, 7, 64, 2, 2, 749, 751, 7, 63, 2, 2, 750, 740, 3, 2, 2, 2, 750, 742, 3, 2, 2, 2, 750, 744, 3, 2, 2, 2, 750, 745, 3, 2, 2, 2, 750, 747, 3, 2, 2, 2, 750, 748, 3, 2, 2, 2, 751, 120, 3, 2, 2, 2, 752, 753, 7, 45, 2, 2, 753, 122, 3, 2, 2, 2, 754, 755, 7, 47, 2, 2, 755, 124, 3, 2, 2, 2, 756, 757, 7, 44, 2, 2, 757, 126, 3, 2, 2, 2, 758, 759, 7, 49, 2, 2, 759, 128, 3, 2, 2, 2, 760, 761, 7, 39, 2, 2, 761, 130, 3, 2, 2, 2, 762, 763, 7, 51, 2, 2, 763, 764, 7, 50, 2, 2, 764, 132, 3, 2, 2, 2, 765, 766, 7, 99, 2, 2, 766, 767, 7, 117, 2, 2, 767, 773, 7, 101, 2, 2, 768, 769, 7, 102, 2, 2, 769, 770, 7, 103, 2, 2, 770, 771, 7, 117, 2, 2, 771, 773, 7, 101, 2, 2, 772, 765, 3, 2, 2, 2, 772, 768, 3, 2, 2, 2, 773, 134, 3, 2, 2, 2, 774, 775, 7, 112, 2, 2, 775, 776, 7, 119, 2, 2, 776, 777, 7, 110, 2, 2, 777, 778, 7, 110, 2, 2, 778, 779, 7, 117, 2, 2, 779, 136, 3, 2, 2, 2, 780, 781, 7, 104, 2, 2, 781, 782, 7, 107, 2, 2, 782, 783, 7, 116, 2, 2, 783, 784, 7, 117, 2, 2, 784, 790, 7, 118, 2, 2, 785, 786, 7, 110, 2, 2, 786, 787, 7, 99, 2, 2, 787, 788, 7, 117, 2, 2, 788, 790, 7, 118, 2, 2, 789, 780, 3, 2, 2, 2, 789, 785, 3, 2, 2, 2, 790, 138, 3, 2, 2, 2, 791, 792, 5, 237, 117, 2, 792, 793, 5, 231, 114, 2, 793, 794, 5, 243, 120, 2, 794, 795, 5, 229, 113, 2, 795, 796, 5, 209, 103, 2, 796, 1222, 3, 2, 2, 2, 797, 798, 5, 203, 100, 2, 798, 799, 5, 205, 101, 2, 799, 800, 5, 239, 118, 2, 800, 1222, 3, 2, 2, 2, 801, 802, 5, 233, 115, 2, 802, 803, 5, 231, 114, 2, 803, 804, 5, 247, 122, 2, 804, 1222, 3, 2, 2, 2, 805, 806, 5, 225, 111, 2, 806, 807, 5, 231, 114, 2, 807, 808, 5, 215, 106, 2, 808, 809, 5, 131, 64, 2, 809, 1222, 3, 2, 2, 2, 810, 811, 5, 233, 115, 2, 811, 812, 5, 219, 108, 2, 812, 1222, 3, 2, 2, 2, 813, 814, 5, 241, 119, 2, 814, 815, 5, 203, 100, 2, 815, 816, 5, 243, 120, 2, 816, 1222, 3, 2, 2, 2, 817, 1222, 5, 211, 104, 2, 818, 819, 5, 239, 118, 2, 819, 820, 5, 243, 120, 2, 820, 821, 5, 205, 101, 2, 821, 822, 5, 239, 118, 2, 822, 823, 5, 241, 119, 2, 823, 824, 5, 237, 117, 2, 824, 825, 5, 219, 108, 2, 825, 826, 5, 229, 113, 2, 826, 827, 5, 215, 106, 2, 827, 1222, 3, 2, 2, 2, 828, 829, 5, 241, 119, 2, 829, 830, 5, 237, 117, 2, 830, 831, 5, 219, 108, 2, 831, 832, 5, 227, 112, 2, 832, 1222, 3, 2, 2, 2, 833, 834, 5, 207, 102, 2, 834, 835, 5, 231, 114, 2, 835, 836, 5, 229, 113, 2, 836, 837, 5, 207, 102, 2, 837, 838, 5, 203, 100, 2, 838, 839, 5, 241, 119, 2, 839, 1222, 3, 2, 2, 2, 840, 841, 5, 207, 102, 2, 841, 842, 5, 231, 114, 2, 842, 843, 5, 203, 100, 2, 843, 844, 5, 225, 111, 2, 844, 845, 5, 211, 104, 2, 845, 846, 5, 239, 118, 2, 846, 847, 5, 207, 102, 2, 847, 848, 5, 211, 104, 2, 848, 1222, 3, 2, 2, 2, 849, 850, 5, 215, 106, 2, 850, 851, 5, 237, 117, 2, 851, 852, 5, 211, 104, 2, 852, 853, 5, 203, 100, 2, 853, 854, 5, 241, 119, 2, 854, 855, 5, 211, 104, 2, 855, 856, 5, 239, 118, 2, 856, 857, 5, 241, 119, 2, 857, 1222, 3, 2, 2, 2, 858, 859, 5, 225, 111, 2, 859, 860, 5, 211, 104, 2, 860, 861, 5, 213, 105, 2, 861, 862, 5, 241, 119, 2, 862, 1222, 3, 2, 2, 2, 863, 864, 5, 229, 113, 2, 864, 865, 5, 231, 114, 2, 865, 866, 5, 247, 122, 2, 866, 1222, 3, 2, 2, 2, 867, 868, 5, 237, 117, 2, 868, 869, 5, 219, 108, 2, 869, 870, 5, 215, 106, 2, 870, 871, 5, 217, 107, 2, 871, 872, 5, 241, 119, 2, 872, 1222, 3, 2, 2, 2, 873, 874, 5, 239, 118, 2, 874, 875, 5, 241, 119, 2, 875, 876, 5, 203, 100, 2, 876, 877, 5, 237, 117, 2, 877, 878, 5, 241, 119, 2, 878, 879, 5, 239, 118, 2, 879, 880, 5, 111, 54, 2, 880, 881, 5, 247, 122, 2, 881, 882, 5, 219, 108, 2, 882, 883, 5, 241, 119, 2, 883, 884, 5, 217, 107, 2, 884, 1222, 3, 2, 2, 2, 885, 886, 5, 209, 103, 2, 886, 887, 5, 203, 100, 2, 887, 888, 5, 241, 119, 2, 888, 889, 5, 211, 104, 2, 889, 890, 5, 111, 54, 2, 890, 891, 5, 213, 105, 2, 891, 892, 5, 231, 114, 2, 892, 893, 5, 237, 117, 2, 893, 894, 5, 227, 112, 2, 894, 895, 5, 203, 100, 2, 895, 896, 5, 241, 119, 2, 896, 1222, 3, 2, 2, 2, 897, 898, 5, 209, 103, 2, 898, 899, 5, 203, 100, 2, 899, 900, 5, 241, 119, 2, 900, 901, 5, 211, 104, 2, 901, 902, 5, 111, 54, 2, 902, 903, 5, 241, 119, 2, 903, 904, 5, 237, 117, 2, 904, 905, 5, 243, 120, 2, 905, 906, 5, 229, 113, 2, 906, 907, 5, 207, 102, 2, 907, 1222, 3, 2, 2, 2, 908, 909, 5, 209, 103, 2, 909, 910, 5, 203, 100, 2, 910, 911, 5, 241, 119, 2, 911, 912, 5, 211, 104, 2, 912, 913, 5, 111, 54, 2, 913, 914, 5, 233, 115, 2, 914, 915, 5, 203, 100, 2, 915, 916, 5, 237, 117, 2, 916, 917, 5, 239, 118, 2, 917, 918, 5, 211, 104, 2, 918, 1222, 3, 2, 2, 2, 919, 920, 5, 203, 100, 2, 920, 921, 5, 243, 120, 2, 921, 922, 5, 241, 119, 2, 922, 923, 5, 231, 114, 2, 923, 924, 5, 111, 54, 2, 924, 925, 5, 205, 101, 2, 925, 926, 5, 243, 120, 2, 926, 927, 5, 207, 102, 2, 927, 928, 5, 223, 110, 2, 928, 929, 5, 211, 104, 2, 929, 930, 5, 241, 119, 2, 930, 1222, 3, 2, 2, 2, 931, 932, 5, 209, 103, 2, 932, 933, 5, 203, 100, 2, 933, 934, 5, 241, 119, 2, 934, 935, 5, 211, 104, 2, 935, 936, 5, 111, 54, 2, 936, 937, 5, 211, 104, 2, 937, 938, 5, 249, 123, 2, 938, 939, 5, 241, 119, 2, 939, 940, 5, 237, 117, 2, 940, 941, 5, 203, 100, 2, 941, 942, 5, 207, 102, 2, 942, 943, 5, 241, 119, 2, 943, 1222, 3, 2, 2, 2, 944, 945, 5, 219, 108, 2, 945, 946, 5, 239, 118, 2, 946, 947, 5, 111, 54, 2, 947, 948, 5, 213, 105, 2, 948, 949, 5, 219, 108, 2, 949, 950, 5, 229, 113, 2, 950, 951, 5, 219, 108, 2, 951, 952, 5, 241, 119, 2, 952, 953, 5, 211, 104, 2, 953, 1222, 3, 2, 2, 2, 954, 955, 5, 219, 108, 2, 955, 956, 5, 239, 118, 2, 956, 957, 5, 111, 54, 2, 957, 958, 5, 219, 108, 2, 958, 959, 5, 229, 113, 2, 959, 960, 5, 213, 105, 2, 960, 961, 5, 219, 108, 2, 961, 962, 5, 229, 113, 2, 962, 963, 5, 219, 108, 2, 963, 964, 5, 241, 119, 2, 964, 965, 5, 211, 104, 2, 965, 1222, 3, 2, 2, 2, 966, 967, 5, 207, 102, 2, 967, 968, 5, 203, 100, 2, 968, 969, 5, 239, 118, 2, 969, 970, 5, 211, 104, 2, 970, 1222, 3, 2, 2, 2, 971, 972, 5, 225, 111, 2, 972, 973, 5, 211, 104, 2, 973, 974, 5, 229, 113, 2, 974, 975, 5, 215, 106, 2, 975, 976, 5, 241, 119, 2, 976, 977, 5, 217, 107, 2, 977, 1222, 3, 2, 2, 2, 978, 979, 5, 227, 112, 2, 979, 980, 5, 245, 121, 2, 980, 981, 5, 111, 54, 2, 981, 982, 5, 227, 112, 2, 982, 983, 5, 203, 100, 2, 983, 984, 5, 249, 123, 2, 984, 1222, 3, 2, 2, 2, 985, 986, 5, 227, 112, 2, 986, 987, 5, 245, 121, 2, 987, 988, 5, 111, 54, 2, 988, 989, 5, 227, 112, 2, 989, 990, 5, 219, 108, 2, 990, 991, 5, 229, 113, 2, 991, 1222, 3, 2, 2, 2, 992, 993, 5, 227, 112, 2, 993, 994, 5, 245, 121, 2, 994, 995, 5, 111, 54, 2, 995, 996, 5, 203, 100, 2, 996, 997, 5, 245, 121, 2, 997, 998, 5, 215, 106, 2, 998, 1222, 3, 2, 2, 2, 999, 1000, 5, 227, 112, 2, 1000, 1001, 5, 245, 121, 2, 1001, 1002, 5, 111, 54, 2, 1002, 1003, 5, 239, 118, 2, 1003, 1004, 5, 243, 120, 2, 1004, 1005, 5, 227, 112, 2, 1005, 1222, 3, 2, 2, 2, 1006, 1007, 5, 227, 112, 2, 1007, 1008, 5, 245, 121, 2, 1008, 1009, 5, 111, 54, 2, 1009, 1010, 5, 207, 102, 2, 1010, 1011, 5, 231, 114, 2, 1011, 1012, 5, 243, 120, 2, 1012, 1013, 5, 229, 113, 2, 1013, 1014, 5, 241, 119, 2, 1014, 1222, 3, 2, 2, 2, 1015, 1016, 5, 227, 112, 2, 1016, 1017, 5, 245, 121, 2, 1017, 1018, 5, 111, 54, 2, 1018, 1019, 5, 207, 102, 2, 1019, 1020, 5, 231, 114, 2, 1020, 1021, 5, 229, 113, 2, 1021, 1022, 5, 207, 102, 2, 1022, 1023, 5, 203, 100, 2, 1023, 1024, 5, 241, 119, 2, 1024, 1222, 3, 2, 2, 2, 1025, 1026, 5, 227, 112, 2, 1026, 1027, 5, 245, 121, 2, 1027, 1028, 5, 111, 54, 2, 1028, 1029, 5, 221, 109, 2, 1029, 1030, 5, 231, 114, 2, 1030, 1031, 5, 219, 108, 2, 1031, 1032, 5, 229, 113, 2, 1032, 1222, 3, 2, 2, 2, 1033, 1034, 5, 227, 112, 2, 1034, 1035, 5, 245, 121, 2, 1035, 1036, 5, 111, 54, 2, 1036, 1037, 5, 227, 112, 2, 1037, 1038, 5, 211, 104, 2, 1038, 1039, 5, 209, 103, 2, 1039, 1040, 5, 219, 108, 2, 1040, 1041, 5, 203, 100, 2, 1041, 1042, 5, 229, 113, 2, 1042, 1222, 3, 2, 2, 2, 1043, 1044, 5, 227, 112, 2, 1044, 1045, 5, 245, 121, 2, 1045, 1046, 5, 111, 54, 2, 1046, 1047, 5, 209, 103, 2, 1047, 1048, 5, 211, 104, 2, 1048, 1049, 5, 209, 103, 2, 1049, 1050, 5, 243, 120, 2, 1050, 1051, 5, 233, 115, 2, 1051, 1052, 5, 211, 104, 2, 1052, 1222, 3, 2, 2, 2, 1053, 1054, 5, 227, 112, 2, 1054, 1055, 5, 211, 104, 2, 1055, 1056, 5, 241, 119, 2, 1056, 1057, 5, 203, 100, 2, 1057, 1058, 5, 209, 103, 2, 1058, 1059, 5, 203, 100, 2, 1059, 1060, 5, 241, 119, 2, 1060, 1061, 5, 203, 100, 2, 1061, 1222, 3, 2, 2, 2, 1062, 1063, 5, 239, 118, 2, 1063, 1064, 5, 233, 115, 2, 1064, 1065, 5, 225, 111, 2, 1065, 1066, 5, 219, 108, 2, 1066, 1067, 5, 241, 119, 2, 1067, 1222, 3, 2, 2, 2, 1068, 1069, 5, 241, 119, 2, 1069, 1070, 5, 231, 114, 2, 1070, 1071, 5, 111, 54, 2, 1071, 1072, 5, 239, 118, 2, 1072, 1073, 5, 241, 119, 2, 1073, 1074, 5, 237, 117, 2, 1074, 1075, 5, 219, 108, 2, 1075, 1076, 5, 229, 113, 2, 1076, 1077, 5, 215, 106, 2, 1077, 1222, 3, 2, 2, 2, 1078, 1079, 5, 241, 119, 2, 1079, 1080, 5, 231, 114, 2, 1080, 1081, 5, 111, 54, 2, 1081, 1082, 5, 239, 118, 2, 1082, 1083, 5, 241, 119, 2, 1083, 1084, 5, 237, 117, 2, 1084, 1222, 3, 2, 2, 2, 1085, 1086, 5, 241, 119, 2, 1086, 1087, 5, 231, 114, 2, 1087, 1088, 5, 111, 54, 2, 1088, 1089, 5, 205, 101, 2, 1089, 1090, 5, 231, 114, 2, 1090, 1091, 5, 231, 114, 2, 1091, 1092, 5, 225, 111, 2, 1092, 1222, 3, 2, 2, 2, 1093, 1094, 5, 241, 119, 2, 1094, 1095, 5, 231, 114, 2, 1095, 1096, 5, 111, 54, 2, 1096, 1097, 5, 205, 101, 2, 1097, 1098, 5, 231, 114, 2, 1098, 1099, 5, 231, 114, 2, 1099, 1100, 5, 225, 111, 2, 1100, 1101, 5, 211, 104, 2, 1101, 1102, 5, 203, 100, 2, 1102, 1103, 5, 229, 113, 2, 1103, 1222, 3, 2, 2, 2, 1104, 1105, 5, 241, 119, 2, 1105, 1106, 5, 231, 114, 2, 1106, 1107, 5, 111, 54, 2, 1107, 1108, 5, 209, 103, 2, 1108, 1109, 5, 203, 100, 2, 1109, 1110, 5, 241, 119, 2, 1110, 1111, 5, 211, 104, 2, 1111, 1112, 5, 241, 119, 2, 1112, 1113, 5, 219, 108, 2, 1113, 1114, 5, 227, 112, 2, 1114, 1115, 5, 211, 104, 2, 1115, 1222, 3, 2, 2, 2, 1116, 1117, 5, 241, 119, 2, 1117, 1118, 5, 231, 114, 2, 1118, 1119, 5, 111, 54, 2, 1119, 1120, 5, 209, 103, 2, 1120, 1121, 5, 241, 119, 2, 1121, 1222, 3, 2, 2, 2, 1122, 1123, 5, 241, 119, 2, 1123, 1124, 5, 231, 114, 2, 1124, 1125, 5, 111, 54, 2, 1125, 1126, 5, 209, 103, 2, 1126, 1127, 5, 205, 101, 2, 1127, 1128, 5, 225, 111, 2, 1128, 1222, 3, 2, 2, 2, 1129, 1130, 5, 241, 119, 2, 1130, 1131, 5, 231, 114, 2, 1131, 1132, 5, 111, 54, 2, 1132, 1133, 5, 209, 103, 2, 1133, 1134, 5, 231, 114, 2, 1134, 1135, 5, 243, 120, 2, 1135, 1136, 5, 205, 101, 2, 1136, 1137, 5, 225, 111, 2, 1137, 1138, 5, 211, 104, 2, 1138, 1222, 3, 2, 2, 2, 1139, 1140, 5, 241, 119, 2, 1140, 1141, 5, 231, 114, 2, 1141, 1142, 5, 111, 54, 2, 1142, 1143, 5, 209, 103, 2, 1143, 1144, 5, 211, 104, 2, 1144, 1145, 5, 215, 106, 2, 1145, 1146, 5, 237, 117, 2, 1146, 1147, 5, 211, 104, 2, 1147, 1148, 5, 211, 104, 2, 1148, 1149, 5, 239, 118, 2, 1149, 1222, 3, 2, 2, 2, 1150, 1151, 5, 241, 119, 2, 1151, 1152, 5, 231, 114, 2, 1152, 1153, 5, 111, 54, 2, 1153, 1154, 5, 219, 108, 2, 1154, 1155, 5, 229, 113, 2, 1155, 1156, 5, 241, 119, 2, 1156, 1222, 3, 2, 2, 2, 1157, 1158, 5, 241, 119, 2, 1158, 1159, 5, 231, 114, 2, 1159, 1160, 5, 111, 54, 2, 1160, 1161, 5, 219, 108, 2, 1161, 1162, 5, 229, 113, 2, 1162, 1163, 5, 241, 119, 2, 1163, 1164, 5, 211, 104, 2, 1164, 1165, 5, 215, 106, 2, 1165, 1166, 5, 211, 104, 2, 1166, 1167, 5, 237, 117, 2, 1167, 1222, 3, 2, 2, 2, 1168, 1169, 5, 241, 119, 2, 1169, 1170, 5, 231, 114, 2, 1170, 1171, 5, 111, 54, 2, 1171, 1172, 5, 219, 108, 2, 1172, 1173, 5, 233, 115, 2, 1173, 1222, 3, 2, 2, 2, 1174, 1175, 5, 241, 119, 2, 1175, 1176, 5, 231, 114, 2, 1176, 1177, 5, 111, 54, 2, 1177, 1178, 5, 225, 111, 2, 1178, 1179, 5, 231, 114, 2, 1179, 1180, 5, 229, 113, 2, 1180, 1181, 5, 215, 106, 2, 1181, 1222, 3, 2, 2, 2, 1182, 1183, 5, 241, 119, 2, 1183, 1184, 5, 231, 114, 2, 1184, 1185, 5, 111, 54, 2, 1185, 1186, 5, 237, 117, 2, 1186, 1187, 5, 203, 100, 2, 1187, 1188, 5, 209, 103, 2, 1188, 1189, 5, 219, 108, 2, 1189, 1190, 5, 203, 100, 2, 1190, 1191, 5, 229, 113, 2, 1191, 1192, 5, 239, 118, 2, 1192, 1222, 3, 2, 2, 2, 1193, 1194, 5, 241, 119, 2, 1194, 1195, 5, 231, 114, 2, 1195, 1196, 5, 111, 54, 2, 1196, 1197, 5, 245, 121, 2, 1197, 1198, 5, 211, 104, 2, 1198, 1199, 5, 237, 117, 2, 1199, 1200, 5, 239, 118, 2, 1200, 1201, 5, 219, 108, 2, 1201, 1202, 5, 231, 114, 2, 1202, 1203, 5, 229, 113, 2, 1203, 1222, 3, 2, 2, 2, 1204, 1205, 5, 241, 119, 2, 1205, 1206, 5, 231, 114, 2, 1206, 1207, 5, 111, 54, 2, 1207, 1208, 5, 243, 120, 2, 1208, 1209, 5, 229, 113, 2, 1209, 1210, 5, 239, 118, 2, 1210, 1211, 5, 219, 108, 2, 1211, 1212, 5, 215, 106, 2, 1212, 1213, 5, 229, 113, 2, 1213, 1214, 5, 211, 104, 2, 1214, 1215, 5, 209, 103, 2, 1215, 1216, 5, 111, 54, 2, 1216, 1217, 5, 225, 111, 2, 1217, 1218, 5, 231, 114, 2, 1218, 1219, 5, 229, 113, 2, 1219, 1220, 5, 215, 106, 2, 1220, 1222, 3, 2, 2, 2, 1221, 791, 3, 2, 2, 2, 1221, 797, 3, 2, 2, 2, 1221, 801, 3, 2, 2, 2, 1221, 805, 3, 2, 2, 2, 1221, 810, 3, 2, 2, 2, 1221, 813, 3, 2, 2, 2, 1221, 817, 3, 2, 2, 2, 1221, 818, 3, 2, 2, 2, 1221, 828, 3, 2, 2, 2, 1221, 833, 3, 2, 2, 2, 1221, 840, 3, 2, 2, 2, 1221, 849, 3, 2, 2, 2, 1221, 858, 3, 2, 2, 2, 1221, 863, 3, 2, 2, 2, 1221, 867, 3, 2, 2, 2, 1221, 873, 3, 2, 2, 2, 1221, 885, 3, 2, 2, 2, 1221, 897, 3, 2, 2, 2, 1221, 908, 3, 2, 2, 2, 1221, 919, 3, 2, 2, 2, 1221, 931, 3, 2, 2, 2, 1221, 944, 3, 2, 2, 2, 1221, 954, 3, 2, 2, 2, 1221, 966, 3, 2, 2, 2, 1221, 971, 3, 2, 2, 2, 1221, 978, 3, 2, 2, 2, 1221, 985, 3, 2, 2, 2, 1221, 992, 3, 2, 2, 2, 1221, 999, 3, 2, 2, 2, 1221, 1006, 3, 2, 2, 2, 1221, 1015, 3, 2, 2, 2, 1221, 1025, 3, 2, 2, 2, 1221, 1033, 3, 2, 2, 2, 1221, 1043, 3, 2, 2, 2, 1221, 1053, 3, 2, 2, 2, 1221, 1062, 3, 2, 2, 2, 1221, 1068, 3, 2, 2, 2, 1221, 1078, 3, 2, 2, 2, 1221, 1085, 3, 2, 2, 2, 1221, 1093, 3, 2, 2, 2, 1221, 1104, 3, 2, 2, 2, 1221, 1116, 3, 2, 2, 2, 1221, 1122, 3, 2, 2, 2, 1221, 1129, 3, 2, 2, 2, 1221, 1139, 3, 2, 2, 2, 1221, 1150, 3, 2, 2, 2, 1221, 1157, 3, 2, 2, 2, 1221, 1168, 3, 2, 2, 2, 1221, 1174, 3, 2, 2, 2, 1221, 1182, 3, 2, 2, 2, 1221, 1193, 3, 2, 2, 2, 1221, 1204, 3, 2, 2, 2, 1222, 140, 3, 2, 2, 2, 1223, 1224, 5, 203, 100, 2, 1224, 1225, 5, 245, 121, 2, 1225, 1226, 5, 215, 106, 2, 1226, 1375, 3, 2, 2, 2, 1227, 1228, 5, 227, 112, 2, 1228, 1229, 5, 219, 108, 2, 1229, 1230, 5, 229, 113, 2, 1230, 1375, 3, 2, 2, 2, 1231, 1232, 5, 227, 112, 2, 1232, 1233, 5, 203, 100, 2, 1233, 1234, 5, 249, 123, 2, 1234, 1375, 3, 2, 2, 2, 1235, 1236, 5, 239, 118, 2, 1236, 1237, 5, 243, 120, 2, 1237, 1238, 5, 227, 112, 2, 1238, 1375, 3, 2, 2, 2, 1239, 1240, 5, 207, 102, 2, 1240, 1241, 5, 231, 114, 2, 1241, 1242, 5, 243, 120, 2, 1242, 1243, 5, 229, 113, 2, 1243, 1244, 5, 241, 119, 2, 1244, 1375, 3, 2, 2, 2, 1245, 1246, 5, 207, 102, 2, 1246, 1247, 5, 231, 114, 2, 1247, 1248, 5, 243, 120, 2, 1248, 1249, 5, 229, 113, 2, 1249, 1250, 5, 241, 119, 2, 1250, 1251, 5, 111, 54, 2, 1251, 1252, 5, 209, 103, 2, 1252, 1253, 5, 219, 108, 2, 1253, 1254, 5, 239, 118, 2, 1254, 1255, 5, 241, 119, 2, 1255, 1256, 5, 219, 108, 2, 1256, 1257, 5, 229, 113, 2, 1257, 1258, 5, 207, 102, 2, 1258, 1259, 5, 241, 119, 2, 1259, 1375, 3, 2, 2, 2, 1260, 1261, 5, 233, 115, 2, 1261, 1262, 5, 211, 104, 2, 1262, 1263, 5, 237, 117, 2, 1263, 1264, 5, 207, 102, 2, 1264, 1265, 5, 211, 104, 2, 1265, 1266, 5, 229, 113, 2, 1266, 1267, 5, 241, 119, 2, 1267, 1268, 5, 219, 108, 2, 1268, 1269, 5, 225, 111, 2, 1269, 1270, 5, 211, 104, 2, 1270, 1375, 3, 2, 2, 2, 1271, 1272, 5, 227, 112, 2, 1272, 1273, 5, 211, 104, 2, 1273, 1274, 5, 209, 103, 2, 1274, 1275, 5, 219, 108, 2, 1275, 1276, 5, 203, 100, 2, 1276, 1277, 5, 229, 113, 2, 1277, 1375, 3, 2, 2, 2, 1278, 1279, 5, 227, 112, 2, 1279, 1280, 5, 211, 104, 2, 1280, 1281, 5, 209, 103, 2, 1281, 1282, 5, 219, 108, 2, 1282, 1283, 5, 203, 100, 2, 1283, 1284, 5, 229, 113, 2, 1284, 1285, 5, 111, 54, 2, 1285, 1286, 5, 203, 100, 2, 1286, 1287, 5, 205, 101, 2, 1287, 1288, 5, 239, 118, 2, 1288, 1289, 5, 231, 114, 2, 1289, 1290, 5, 225, 111, 2, 1290, 1291, 5, 243, 120, 2, 1291, 1292, 5, 241, 119, 2, 1292, 1293, 5, 211, 104, 2, 1293, 1294, 5, 111, 54, 2, 1294, 1295, 5, 209, 103, 2, 1295, 1296, 5, 211, 104, 2, 1296, 1297, 5, 245, 121, 2, 1297, 1298, 5, 219, 108, 2, 1298, 1299, 5, 203, 100, 2, 1299, 1300, 5, 241, 119, 2, 1300, 1301, 5, 219, 108, 2, 1301, 1302, 5, 231, 114, 2, 1302, 1303, 5, 229, 113, 2, 1303, 1375, 3, 2, 2, 2, 1304, 1305, 5, 203, 100, 2, 1305, 1306, 5, 207, 102, 2, 1306, 1307, 5, 231, 114, 2, 1307, 1308, 5, 239, 118, 2, 1308, 1375, 3, 2, 2, 2, 1309, 1310, 5, 203, 100, 2, 1310, 1311, 5, 239, 118, 2, 1311, 1312, 5, 219, 108, 2, 1312, 1313, 5, 229, 113, 2, 1313, 1375, 3, 2, 2, 2, 1314, 1315, 5, 203, 100, 2, 1315, 1316, 5, 241, 119, 2, 1316, 1317, 5, 203, 100, 2, 1317, 1318, 5, 229, 113, 2, 1318, 1375, 3, 2, 2, 2, 1319, 1320, 5, 203, 100, 2, 1320, 1321, 5, 241, 119, 2, 1321, 1322, 5, 203, 100, 2, 1322, 1323, 5, 229, 113, 2, 1323, 1324, 7, 52, 2, 2, 1324, 1375, 3, 2, 2, 2, 1325, 1326, 5, 207, 102, 2, 1326, 1327, 5, 211, 104, 2, 1327, 1328, 5, 219, 108, 2, 1328, 1329, 5, 225, 111, 2, 1329, 1375, 3, 2, 2, 2, 1330, 1331, 5, 207, 102, 2, 1331, 1332, 5, 231, 114, 2, 1332, 1333, 5, 239, 118, 2, 1333, 1375, 3, 2, 2, 2, 1334, 1335, 5, 207, 102, 2, 1335, 1336, 5, 231, 114, 2, 1336, 1337, 5, 239, 118, 2, 1337, 1338, 5, 217, 107, 2, 1338, 1375, 3, 2, 2, 2, 1339, 1340, 5, 213, 105, 2, 1340, 1341, 5, 225, 111, 2, 1341, 1342, 5, 231, 114, 2, 1342, 1343, 5, 231, 114, 2, 1343, 1344, 5, 237, 117, 2, 1344, 1375, 3, 2, 2, 2, 1345, 1346, 5, 225, 111, 2, 1346, 1347, 5, 241, 119, 2, 1347, 1348, 5, 237, 117, 2, 1348, 1349, 5, 219, 108, 2, 1349, 1350, 5, 227, 112, 2, 1350, 1375, 3, 2, 2, 2, 1351, 1352, 5, 239, 118, 2, 1352, 1353, 5, 219, 108, 2, 1353, 1354, 5, 229, 113, 2, 1354, 1375, 3, 2, 2, 2, 1355, 1356, 5, 239, 118, 2, 1356, 1357, 5, 219, 108, 2, 1357, 1358, 5, 229, 113, 2, 1358, 1359, 5, 217, 107, 2, 1359, 1375, 3, 2, 2, 2, 1360, 1361, 5, 239, 118, 2, 1361, 1362, 5, 235, 116, 2, 1362, 1363, 5, 237, 117, 2, 1363, 1364, 5, 241, 119, 2, 1364, 1375, 3, 2, 2, 2, 1365, 1366, 5, 241, 119, 2, 1366, 1367, 5, 203, 100, 2, 1367, 1368, 5, 229, 113, 2, 1368, 1375, 3, 2, 2, 2, 1369, 1370, 5, 241, 119, 2, 1370, 1371, 5, 203, 100, 2, 1371, 1372, 5, 229, 113, 2, 1372, 1373, 5, 217, 107, 2, 1373, 1375, 3, 2, 2, 2, 1374, 1223, 3, 2, 2, 2, 1374, 1227, 3, 2, 2, 2, 1374, 1231, 3, 2, 2, 2, 1374, 1235, 3, 2, 2, 2, 1374, 1239, 3, 2, 2, 2, 1374, 1245, 3, 2, 2, 2, 1374, 1260, 3, 2, 2, 2, 1374, 1271, 3, 2, 2, 2, 1374, 1278, 3, 2, 2, 2, 1374, 1304, 3, 2, 2, 2, 1374, 1309, 3, 2, 2, 2, 1374, 1314, 3, 2, 2, 2, 1374, 1319, 3, 2, 2, 2, 1374, 1325, 3, 2, 2, 2, 1374, 1330, 3, 2, 2, 2, 1374, 1334, 3, 2, 2, 2, 1374, 1339, 3, 2, 2, 2, 1374, 1345, 3, 2, 2, 2, 1374, 1351, 3, 2, 2, 2, 1374, 1355, 3, 2, 2, 2, 1374, 1360, 3, 2, 2, 2, 1374, 1365, 3, 2, 2, 2, 1374, 1369, 3, 2, 2, 2, 1375, 142, 3, 2, 2, 2, 1376, 1377, 5, 207, 102, 2, 1377, 1378, 5, 219, 108, 2, 1378, 1379, 5, 209, 103, 2, 1379, 1380, 5, 237, 117, 2, 1380, 1381, 5, 111, 54, 2, 1381, 1382, 5, 227, 112, 2, 1382, 1383, 5, 203, 100, 2, 1383, 1384, 5, 241, 119, 2, 1384, 1385, 5, 207, 102, 2, 1385, 1386, 5, 217, 107, 2, 1386, 144, 3, 2, 2, 2, 1387, 1394, 5, 61, 29, 2, 1388, 1393, 5, 61, 29, 2, 1389, 1393, 5, 59, 28, 2, 1390, 1393, 7, 97, 2, 2, 1391, 1393, 5, 125, 61, 2, 1392, 1388, 3, 2, 2, 2, 1392, 1389, 3, 2, 2, 2, 1392, 1390, 3, 2, 2, 2, 1392, 1391, 3, 2, 2, 2, 1393, 1396, 3, 2, 2, 2, 1394, 1392, 3, 2, 2, 2, 1394, 1395, 3, 2, 2, 2, 1395, 1407, 3, 2, 2, 2, 1396, 1394, 3, 2, 2, 2, 1397, 1402, 9, 10, 2, 2, 1398, 1403, 5, 61, 29, 2, 1399, 1403, 5, 59, 28, 2, 1400, 1403, 7, 97, 2, 2, 1401, 1403, 5, 125, 61, 2, 1402, 1398, 3, 2, 2, 2, 1402, 1399, 3, 2, 2, 2, 1402, 1400, 3, 2, 2, 2, 1402, 1401, 3, 2, 2, 2, 1403, 1404, 3, 2, 2, 2, 1404, 1402, 3, 2, 2, 2, 1404, 1405, 3, 2, 2, 2, 1405, 1407, 3, 2, 2, 2, 1406, 1387, 3, 2, 2, 2, 1406, 1397, 3, 2, 2, 2, 1407, 146, 3, 2, 2, 2, 1408, 1414, 7, 98, 2, 2, 1409, 1413, 10, 11, 2, 2, 1410, 1411, 7, 98, 2, 2, 1411, 1413, 7, 98, 2, 2, 1412, 1409, 3, 2, 2, 2, 1412, 1410, 3, 2, 2, 2, 1413, 1416, 3, 2, 2, 2, 1414, 1412, 3, 2, 2, 2, 1414, 1415, 3, 2, 2, 2, 1415, 1417, 3, 2, 2, 2, 1416, 1414, 3, 2, 2, 2, 1417, 1418, 7, 98, 2, 2, 1418, 148, 3, 2, 2, 2, 1419, 1420, 5, 41, 19, 2, 1420, 1421, 3, 2, 2, 2, 1421, 1422, 8, 73, 6, 2, 1422, 150, 3, 2, 2, 2, 1423, 1424, 5, 43, 20, 2, 1424, 1425, 3, 2, 2, 2, 1425, 1426, 8, 74, 6, 2, 1426, 152, 3, 2, 2, 2, 1427, 1428, 5, 45, 21, 2, 1428, 1429, 3, 2, 2, 2, 1429, 1430, 8, 75, 6, 2, 1430, 154, 3, 2, 2, 2, 1431, 1432, 7, 126, 2, 2, 1432, 1433, 3, 2, 2, 2, 1433, 1434, 8, 76, 9, 2, 1434, 1435, 8, 76, 10, 2, 1435, 156, 3, 2, 2, 2, 1436, 1437, 7, 93, 2, 2, 1437, 1438, 3, 2, 2, 2, 1438, 1439, 8, 77, 7, 2, 1439, 1440, 8, 77, 4, 2, 1440, 1441, 8, 77, 4, 2, 1441, 158, 3, 2, 2, 2, 1442, 1443, 7, 95, 2, 2, 1443, 1444, 3, 2, 2, 2, 1444, 1445, 8, 78, 10, 2, 1445, 1446, 8, 78, 10, 2, 1446, 1447, 8, 78, 11, 2, 1447, 160, 3, 2, 2, 2, 1448, 1449, 7, 46, 2, 2, 1449, 1450, 3, 2, 2, 2, 1450, 1451, 8, 79, 12, 2, 1451, 162, 3, 2, 2, 2, 1452, 1453, 7, 63, 2, 2, 1453, 1454, 3, 2, 2, 2, 1454, 1455, 8, 80, 13, 2, 1455, 164, 3, 2, 2, 2, 1456, 1457, 5, 227, 112, 2, 1457, 1458, 5, 211, 104, 2, 1458, 1459, 5, 241, 119, 2, 1459, 1460, 5, 203, 100, 2, 1460, 1461, 5, 209, 103, 2, 1461, 1462, 5, 203, 100, 2, 1462, 1463, 5, 241, 119, 2, 1463, 1464, 5, 203, 100, 2, 1464, 166, 3, 2, 2, 2, 1465, 1467, 5, 169, 83, 2, 1466, 1465, 3, 2, 2, 2, 1467, 1468, 3, 2, 2, 2, 1468, 1466, 3, 2, 2, 2, 1468, 1469, 3, 2, 2, 2, 1469, 168, 3, 2, 2, 2, 1470, 1472, 10, 12, 2, 2, 1471, 1470, 3, 2, 2, 2, 1472, 1473, 3, 2, 2, 2, 1473, 1471, 3, 2, 2, 2, 1473, 1474, 3, 2, 2, 2, 1474, 1478, 3, 2, 2, 2, 1475, 1476, 7, 49, 2, 2, 1476, 1478, 10, 13, 2, 2, 1477, 1471, 3, 2, 2, 2, 1477, 1475, 3, 2, 2, 2, 1478, 170, 3, 2, 2, 2, 1479, 1480, 5, 147, 72, 2, 1480, 172, 3, 2, 2, 2, 1481, 1482, 5, 41, 19, 2, 1482, 1483, 3, 2, 2, 2, 1483, 1484, 8, 85, 6, 2, 1484, 174, 3, 2, 2, 2, 1485, 1486, 5, 43, 20, 2, 1486, 1487, 3, 2, 2, 2, 1487, 1488, 8, 86, 6, 2, 1488, 176, 3, 2, 2, 2, 1489, 1490, 5, 45, 21, 2, 1490, 1491, 3, 2, 2, 2, 1491, 1492, 8, 87, 6, 2, 1492, 178, 3, 2, 2, 2, 1493, 1494, 5, 231, 114, 2, 1494, 1495, 5, 229, 113, 2, 1495, 180, 3, 2, 2, 2, 1496, 1497, 5, 247, 122, 2, 1497, 1498, 5, 219, 108, 2, 1498, 1499, 5, 241, 119, 2, 1499, 1500, 5, 217, 107, 2, 1500, 182, 3, 2, 2, 2, 1501, 1502, 7, 126, 2, 2, 1502, 1503, 3, 2, 2, 2, 1503, 1504, 8, 90, 9, 2, 1504, 1505, 8, 90, 10, 2, 1505, 184, 3, 2, 2, 2, 1506, 1507, 7, 95, 2, 2, 1507, 1508, 3, 2, 2, 2, 1508, 1509, 8, 91, 10, 2, 1509, 1510, 8, 91, 10, 2, 1510, 1511, 8, 91, 11, 2, 1511, 186, 3, 2, 2, 2, 1512, 1513, 7, 46, 2, 2, 1513, 1514, 3, 2, 2, 2, 1514, 1515, 8, 92, 12, 2, 1515, 188, 3, 2, 2, 2, 1516, 1517, 7, 63, 2, 2, 1517, 1518, 3, 2, 2, 2, 1518, 1519, 8, 93, 13, 2, 1519, 190, 3, 2, 2, 2, 1520, 1522, 5, 193, 95, 2, 1521, 1520, 3, 2, 2, 2, 1522, 1523, 3, 2, 2, 2, 1523, 1521, 3, 2, 2, 2, 1523, 1524, 3, 2, 2, 2, 1524, 192, 3, 2, 2, 2, 1525, 1527, 10, 12, 2, 2, 1526, 1525, 3, 2, 2, 2, 1527, 1528, 3, 2, 2, 2, 1528, 1526, 3, 2, 2, 2, 1528, 1529, 3, 2, 2, 2, 1529, 1533, 3, 2, 2, 2, 1530, 1531, 7, 49, 2, 2, 1531, 1533, 10, 13, 2, 2, 1532, 1526, 3, 2, 2, 2, 1532, 1530, 3, 2, 2, 2, 1533, 194, 3, 2, 2, 2, 1534, 1535, 5, 147, 72, 2, 1535, 196, 3, 2, 2, 2, 1536, 1537, 5, 41, 19, 2, 1537, 1538, 3, 2, 2, 2, 1538, 1539, 8, 97, 6, 2, 1539, 198, 3, 2, 2, 2, 1540, 1541, 5, 43, 20, 2, 1541, 1542, 3, 2, 2, 2, 1542, 1543, 8, 98, 6, 2, 1543, 200, 3, 2, 2, 2, 1544, 1545, 5, 45, 21, 2, 1545, 1546, 3, 2, 2, 2, 1546, 1547, 8, 99, 6, 2, 1547, 202, 3, 2, 2, 2, 1548, 1549, 9, 14, 2, 2, 1549, 204, 3, 2, 2, 2, 1550, 1551, 9, 15, 2, 2, 1551, 206, 3, 2, 2, 2, 1552, 1553, 9, 16, 2, 2, 1553, 208, 3, 2, 2, 2, 1554, 1555, 9, 17, 2, 2, 1555, 210, 3, 2, 2, 2, 1556, 1557, 9, 8, 2, 2, 1557, 212, 3, 2, 2, 2, 1558, 1559, 9, 18, 2, 2, 1559, 214, 3, 2, 2, 2, 1560, 1561, 9, 19, 2, 2, 1561, 216, 3, 2, 2, 2, 1562, 1563, 9, 20, 2, 2, 1563, 218, 3, 2, 2, 2, 1564, 1565, 9, 21, 2, 2, 1565, 220, 3, 2, 2, 2, 1566, 1567, 9, 22, 2, 2, 1567, 222, 3, 2, 2, 2, 1568, 1569, 9, 23, 2, 2, 1569, 224, 3, 2, 2, 2, 1570, 1571, 9, 24, 2, 2, 1571, 226, 3, 2, 2, 2, 1572, 1573, 9, 25, 2, 2, 1573, 228, 3, 2, 2, 2, 1574, 1575, 9, 26, 2, 2, 1575, 230, 3, 2, 2, 2, 1576, 1577, 9, 27, 2, 2, 1577, 232, 3, 2, 2, 2, 1578, 1579, 9, 28, 2, 2, 1579, 234, 3, 2, 2, 2, 1580, 1581, 9, 29, 2, 2, 1581, 236, 3, 2, 2, 2, 1582, 1583, 9, 30, 2, 2, 1583, 238, 3, 2, 2, 2, 1584, 1585, 9, 31, 2, 2, 1585, 240, 3, 2, 2, 2, 1586, 1587, 9, 32, 2, 2, 1587, 242, 3, 2, 2, 2, 1588, 1589, 9, 33, 2, 2, 1589, 244, 3, 2, 2, 2, 1590, 1591, 9, 34, 2, 2, 1591, 246, 3, 2, 2, 2, 1592, 1593, 9, 35, 2, 2, 1593, 248, 3, 2, 2, 2, 1594, 1595, 9, 36, 2, 2, 1595, 250, 3, 2, 2, 2, 1596, 1597, 9, 37, 2, 2, 1597, 252, 3, 2, 2, 2, 1598, 1599, 9, 38, 2, 2, 1599, 254, 3, 2, 2, 2, 50, 2, 3, 4, 5, 6, 400, 404, 407, 416, 418, 429, 470, 475, 480, 482, 493, 501, 504, 506, 511, 516, 522, 529, 534, 540, 543, 551, 555, 654, 738, 750, 772, 789, 1221, 1374, 1392, 1394, 1402, 1404, 1406, 1412, 1414, 1468, 1473, 1477, 1523, 1528, 1532, 14, 7, 4, 2, 7, 3, 2, 7, 5, 2, 7, 6, 2, 2, 3, 2, 9, 37, 2, 7, 2, 2, 9, 26, 2, 6, 2, 2, 9, 38, 2, 9, 34, 2, 9, 33, 2] \ No newline at end of file +[3, 51485, 51898, 1421, 44986, 20307, 1543, 60043, 49729, 2, 78, 813, 8, 1, 8, 1, 8, 1, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4, 18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 4, 22, 9, 22, 4, 23, 9, 23, 4, 24, 9, 24, 4, 25, 9, 25, 4, 26, 9, 26, 4, 27, 9, 27, 4, 28, 9, 28, 4, 29, 9, 29, 4, 30, 9, 30, 4, 31, 9, 31, 4, 32, 9, 32, 4, 33, 9, 33, 4, 34, 9, 34, 4, 35, 9, 35, 4, 36, 9, 36, 4, 37, 9, 37, 4, 38, 9, 38, 4, 39, 9, 39, 4, 40, 9, 40, 4, 41, 9, 41, 4, 42, 9, 42, 4, 43, 9, 43, 4, 44, 9, 44, 4, 45, 9, 45, 4, 46, 9, 46, 4, 47, 9, 47, 4, 48, 9, 48, 4, 49, 9, 49, 4, 50, 9, 50, 4, 51, 9, 51, 4, 52, 9, 52, 4, 53, 9, 53, 4, 54, 9, 54, 4, 55, 9, 55, 4, 56, 9, 56, 4, 57, 9, 57, 4, 58, 9, 58, 4, 59, 9, 59, 4, 60, 9, 60, 4, 61, 9, 61, 4, 62, 9, 62, 4, 63, 9, 63, 4, 64, 9, 64, 4, 65, 9, 65, 4, 66, 9, 66, 4, 67, 9, 67, 4, 68, 9, 68, 4, 69, 9, 69, 4, 70, 9, 70, 4, 71, 9, 71, 4, 72, 9, 72, 4, 73, 9, 73, 4, 74, 9, 74, 4, 75, 9, 75, 4, 76, 9, 76, 4, 77, 9, 77, 4, 78, 9, 78, 4, 79, 9, 79, 4, 80, 9, 80, 4, 81, 9, 81, 4, 82, 9, 82, 4, 83, 9, 83, 4, 84, 9, 84, 4, 85, 9, 85, 4, 86, 9, 86, 4, 87, 9, 87, 4, 88, 9, 88, 4, 89, 9, 89, 4, 90, 9, 90, 4, 91, 9, 91, 4, 92, 9, 92, 4, 93, 9, 93, 4, 94, 9, 94, 4, 95, 9, 95, 4, 96, 9, 96, 4, 97, 9, 97, 4, 98, 9, 98, 4, 99, 9, 99, 4, 100, 9, 100, 4, 101, 9, 101, 4, 102, 9, 102, 4, 103, 9, 103, 4, 104, 9, 104, 4, 105, 9, 105, 4, 106, 9, 106, 4, 107, 9, 107, 4, 108, 9, 108, 4, 109, 9, 109, 4, 110, 9, 110, 4, 111, 9, 111, 4, 112, 9, 112, 4, 113, 9, 113, 4, 114, 9, 114, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 3, 14, 3, 14, 3, 14, 3, 14, 3, 14, 3, 14, 3, 14, 3, 15, 3, 15, 3, 15, 3, 15, 3, 15, 3, 15, 3, 15, 3, 16, 3, 16, 3, 16, 3, 16, 3, 16, 3, 16, 3, 16, 3, 16, 3, 17, 3, 17, 3, 17, 3, 17, 3, 17, 3, 17, 3, 17, 3, 17, 3, 18, 6, 18, 362, 10, 18, 13, 18, 14, 18, 363, 3, 18, 3, 18, 3, 19, 3, 19, 3, 19, 3, 19, 7, 19, 372, 10, 19, 12, 19, 14, 19, 375, 11, 19, 3, 19, 5, 19, 378, 10, 19, 3, 19, 5, 19, 381, 10, 19, 3, 19, 3, 19, 3, 20, 3, 20, 3, 20, 3, 20, 3, 20, 7, 20, 390, 10, 20, 12, 20, 14, 20, 393, 11, 20, 3, 20, 3, 20, 3, 20, 3, 20, 3, 20, 3, 21, 6, 21, 401, 10, 21, 13, 21, 14, 21, 402, 3, 21, 3, 21, 3, 22, 3, 22, 3, 22, 3, 22, 3, 23, 3, 23, 3, 24, 3, 24, 3, 25, 3, 25, 3, 25, 3, 26, 3, 26, 3, 27, 3, 27, 5, 27, 422, 10, 27, 3, 27, 6, 27, 425, 10, 27, 13, 27, 14, 27, 426, 3, 28, 3, 28, 3, 28, 7, 28, 432, 10, 28, 12, 28, 14, 28, 435, 11, 28, 3, 28, 3, 28, 3, 28, 3, 28, 3, 28, 3, 28, 7, 28, 443, 10, 28, 12, 28, 14, 28, 446, 11, 28, 3, 28, 3, 28, 3, 28, 3, 28, 3, 28, 5, 28, 453, 10, 28, 3, 28, 5, 28, 456, 10, 28, 5, 28, 458, 10, 28, 3, 29, 6, 29, 461, 10, 29, 13, 29, 14, 29, 462, 3, 30, 6, 30, 466, 10, 30, 13, 30, 14, 30, 467, 3, 30, 3, 30, 7, 30, 472, 10, 30, 12, 30, 14, 30, 475, 11, 30, 3, 30, 3, 30, 6, 30, 479, 10, 30, 13, 30, 14, 30, 480, 3, 30, 6, 30, 484, 10, 30, 13, 30, 14, 30, 485, 3, 30, 3, 30, 7, 30, 490, 10, 30, 12, 30, 14, 30, 493, 11, 30, 5, 30, 495, 10, 30, 3, 30, 3, 30, 3, 30, 3, 30, 6, 30, 501, 10, 30, 13, 30, 14, 30, 502, 3, 30, 3, 30, 5, 30, 507, 10, 30, 3, 31, 3, 31, 3, 31, 3, 32, 3, 32, 3, 32, 3, 32, 3, 33, 3, 33, 3, 33, 3, 33, 3, 34, 3, 34, 3, 35, 3, 35, 3, 36, 3, 36, 3, 36, 3, 36, 3, 36, 3, 37, 3, 37, 3, 38, 3, 38, 3, 38, 3, 38, 3, 38, 3, 38, 3, 39, 3, 39, 3, 39, 3, 39, 3, 39, 3, 39, 3, 40, 3, 40, 3, 40, 3, 40, 3, 40, 3, 41, 3, 41, 3, 42, 3, 42, 3, 42, 3, 43, 3, 43, 3, 43, 3, 44, 3, 44, 3, 44, 3, 44, 3, 44, 3, 45, 3, 45, 3, 45, 3, 45, 3, 46, 3, 46, 3, 46, 3, 46, 3, 46, 3, 47, 3, 47, 3, 47, 3, 47, 3, 47, 3, 47, 3, 48, 3, 48, 3, 48, 3, 49, 3, 49, 3, 50, 3, 50, 3, 50, 3, 50, 3, 50, 3, 50, 3, 51, 3, 51, 3, 52, 3, 52, 3, 52, 3, 52, 3, 52, 3, 53, 3, 53, 3, 53, 3, 53, 3, 53, 3, 54, 3, 54, 3, 54, 3, 54, 3, 54, 3, 54, 3, 54, 3, 54, 3, 54, 3, 54, 3, 55, 3, 55, 3, 56, 3, 56, 3, 56, 3, 57, 3, 57, 3, 57, 3, 58, 3, 58, 3, 59, 3, 59, 3, 59, 3, 60, 3, 60, 3, 61, 3, 61, 3, 61, 3, 62, 3, 62, 3, 63, 3, 63, 3, 64, 3, 64, 3, 65, 3, 65, 3, 66, 3, 66, 3, 67, 3, 67, 3, 67, 3, 67, 3, 67, 3, 68, 3, 68, 3, 68, 3, 68, 3, 68, 3, 69, 3, 69, 3, 69, 3, 69, 7, 69, 651, 10, 69, 12, 69, 14, 69, 654, 11, 69, 3, 69, 3, 69, 3, 69, 3, 69, 6, 69, 660, 10, 69, 13, 69, 14, 69, 661, 5, 69, 664, 10, 69, 3, 70, 3, 70, 3, 70, 3, 70, 7, 70, 670, 10, 70, 12, 70, 14, 70, 673, 11, 70, 3, 70, 3, 70, 3, 71, 3, 71, 3, 71, 3, 71, 3, 72, 3, 72, 3, 72, 3, 72, 3, 73, 3, 73, 3, 73, 3, 73, 3, 74, 3, 74, 3, 74, 3, 74, 3, 74, 3, 75, 3, 75, 3, 75, 3, 75, 3, 75, 3, 75, 3, 76, 3, 76, 3, 76, 3, 76, 3, 76, 3, 76, 3, 77, 3, 77, 3, 77, 3, 77, 3, 78, 3, 78, 3, 78, 3, 78, 3, 79, 3, 79, 3, 79, 3, 80, 3, 80, 3, 80, 3, 80, 3, 80, 3, 80, 3, 80, 3, 80, 3, 80, 3, 81, 3, 81, 3, 81, 3, 82, 3, 82, 3, 82, 3, 82, 3, 82, 3, 83, 6, 83, 735, 10, 83, 13, 83, 14, 83, 736, 3, 84, 6, 84, 740, 10, 84, 13, 84, 14, 84, 741, 3, 84, 3, 84, 5, 84, 746, 10, 84, 3, 85, 3, 85, 3, 86, 3, 86, 3, 86, 3, 86, 3, 87, 3, 87, 3, 87, 3, 87, 3, 88, 3, 88, 3, 88, 3, 88, 3, 89, 3, 89, 3, 90, 3, 90, 3, 91, 3, 91, 3, 92, 3, 92, 3, 93, 3, 93, 3, 94, 3, 94, 3, 95, 3, 95, 3, 96, 3, 96, 3, 97, 3, 97, 3, 98, 3, 98, 3, 99, 3, 99, 3, 100, 3, 100, 3, 101, 3, 101, 3, 102, 3, 102, 3, 103, 3, 103, 3, 104, 3, 104, 3, 105, 3, 105, 3, 106, 3, 106, 3, 107, 3, 107, 3, 108, 3, 108, 3, 109, 3, 109, 3, 110, 3, 110, 3, 111, 3, 111, 3, 112, 3, 112, 3, 113, 3, 113, 3, 114, 3, 114, 4, 391, 444, 2, 2, 115, 5, 2, 3, 7, 2, 4, 9, 2, 5, 11, 2, 6, 13, 2, 7, 15, 2, 8, 17, 2, 9, 19, 2, 10, 21, 2, 11, 23, 2, 12, 25, 2, 13, 27, 2, 14, 29, 2, 15, 31, 2, 16, 33, 2, 17, 35, 2, 18, 37, 2, 19, 39, 2, 20, 41, 2, 21, 43, 2, 22, 45, 2, 23, 47, 2, 2, 49, 2, 2, 51, 2, 2, 53, 2, 2, 55, 2, 2, 57, 2, 24, 59, 2, 25, 61, 2, 26, 63, 2, 27, 65, 2, 28, 67, 2, 29, 69, 2, 30, 71, 2, 31, 73, 2, 32, 75, 2, 33, 77, 2, 34, 79, 2, 35, 81, 2, 36, 83, 2, 37, 85, 2, 38, 87, 2, 39, 89, 2, 40, 91, 2, 41, 93, 2, 42, 95, 2, 43, 97, 2, 44, 99, 2, 45, 101, 2, 46, 103, 2, 47, 105, 2, 48, 107, 2, 49, 109, 2, 50, 111, 2, 51, 113, 2, 52, 115, 2, 53, 117, 2, 54, 119, 2, 55, 121, 2, 56, 123, 2, 57, 125, 2, 58, 127, 2, 59, 129, 2, 60, 131, 2, 61, 133, 2, 62, 135, 2, 63, 137, 2, 64, 139, 2, 65, 141, 2, 66, 143, 2, 67, 145, 2, 68, 147, 2, 69, 149, 2, 2, 151, 2, 2, 153, 2, 2, 155, 2, 2, 157, 2, 2, 159, 2, 70, 161, 2, 71, 163, 2, 72, 165, 2, 73, 167, 2, 74, 169, 2, 2, 171, 2, 75, 173, 2, 76, 175, 2, 77, 177, 2, 78, 179, 2, 2, 181, 2, 2, 183, 2, 2, 185, 2, 2, 187, 2, 2, 189, 2, 2, 191, 2, 2, 193, 2, 2, 195, 2, 2, 197, 2, 2, 199, 2, 2, 201, 2, 2, 203, 2, 2, 205, 2, 2, 207, 2, 2, 209, 2, 2, 211, 2, 2, 213, 2, 2, 215, 2, 2, 217, 2, 2, 219, 2, 2, 221, 2, 2, 223, 2, 2, 225, 2, 2, 227, 2, 2, 229, 2, 2, 5, 2, 3, 4, 40, 8, 2, 11, 12, 15, 15, 34, 34, 49, 49, 93, 93, 95, 95, 4, 2, 12, 12, 15, 15, 5, 2, 11, 12, 15, 15, 34, 34, 3, 2, 50, 59, 4, 2, 67, 92, 99, 124, 7, 2, 36, 36, 94, 94, 112, 112, 116, 116, 118, 118, 6, 2, 12, 12, 15, 15, 36, 36, 94, 94, 4, 2, 71, 71, 103, 103, 4, 2, 45, 45, 47, 47, 4, 2, 66, 66, 97, 97, 3, 2, 98, 98, 12, 2, 11, 12, 15, 15, 34, 34, 46, 46, 49, 49, 63, 63, 93, 93, 95, 95, 98, 98, 126, 126, 4, 2, 44, 44, 49, 49, 4, 2, 67, 67, 99, 99, 4, 2, 68, 68, 100, 100, 4, 2, 69, 69, 101, 101, 4, 2, 70, 70, 102, 102, 4, 2, 72, 72, 104, 104, 4, 2, 73, 73, 105, 105, 4, 2, 74, 74, 106, 106, 4, 2, 75, 75, 107, 107, 4, 2, 76, 76, 108, 108, 4, 2, 77, 77, 109, 109, 4, 2, 78, 78, 110, 110, 4, 2, 79, 79, 111, 111, 4, 2, 80, 80, 112, 112, 4, 2, 81, 81, 113, 113, 4, 2, 82, 82, 114, 114, 4, 2, 83, 83, 115, 115, 4, 2, 84, 84, 116, 116, 4, 2, 85, 85, 117, 117, 4, 2, 86, 86, 118, 118, 4, 2, 87, 87, 119, 119, 4, 2, 88, 88, 120, 120, 4, 2, 89, 89, 121, 121, 4, 2, 90, 90, 122, 122, 4, 2, 91, 91, 123, 123, 4, 2, 92, 92, 124, 124, 2, 816, 2, 5, 3, 2, 2, 2, 2, 7, 3, 2, 2, 2, 2, 9, 3, 2, 2, 2, 2, 11, 3, 2, 2, 2, 2, 13, 3, 2, 2, 2, 2, 15, 3, 2, 2, 2, 2, 17, 3, 2, 2, 2, 2, 19, 3, 2, 2, 2, 2, 21, 3, 2, 2, 2, 2, 23, 3, 2, 2, 2, 2, 25, 3, 2, 2, 2, 2, 27, 3, 2, 2, 2, 2, 29, 3, 2, 2, 2, 2, 31, 3, 2, 2, 2, 2, 33, 3, 2, 2, 2, 2, 35, 3, 2, 2, 2, 2, 37, 3, 2, 2, 2, 2, 39, 3, 2, 2, 2, 2, 41, 3, 2, 2, 2, 2, 43, 3, 2, 2, 2, 3, 45, 3, 2, 2, 2, 3, 57, 3, 2, 2, 2, 3, 59, 3, 2, 2, 2, 3, 61, 3, 2, 2, 2, 3, 63, 3, 2, 2, 2, 3, 65, 3, 2, 2, 2, 3, 67, 3, 2, 2, 2, 3, 69, 3, 2, 2, 2, 3, 71, 3, 2, 2, 2, 3, 73, 3, 2, 2, 2, 3, 75, 3, 2, 2, 2, 3, 77, 3, 2, 2, 2, 3, 79, 3, 2, 2, 2, 3, 81, 3, 2, 2, 2, 3, 83, 3, 2, 2, 2, 3, 85, 3, 2, 2, 2, 3, 87, 3, 2, 2, 2, 3, 89, 3, 2, 2, 2, 3, 91, 3, 2, 2, 2, 3, 93, 3, 2, 2, 2, 3, 95, 3, 2, 2, 2, 3, 97, 3, 2, 2, 2, 3, 99, 3, 2, 2, 2, 3, 101, 3, 2, 2, 2, 3, 103, 3, 2, 2, 2, 3, 105, 3, 2, 2, 2, 3, 107, 3, 2, 2, 2, 3, 109, 3, 2, 2, 2, 3, 111, 3, 2, 2, 2, 3, 113, 3, 2, 2, 2, 3, 115, 3, 2, 2, 2, 3, 117, 3, 2, 2, 2, 3, 119, 3, 2, 2, 2, 3, 121, 3, 2, 2, 2, 3, 123, 3, 2, 2, 2, 3, 125, 3, 2, 2, 2, 3, 127, 3, 2, 2, 2, 3, 129, 3, 2, 2, 2, 3, 131, 3, 2, 2, 2, 3, 133, 3, 2, 2, 2, 3, 135, 3, 2, 2, 2, 3, 137, 3, 2, 2, 2, 3, 139, 3, 2, 2, 2, 3, 141, 3, 2, 2, 2, 3, 143, 3, 2, 2, 2, 3, 145, 3, 2, 2, 2, 3, 147, 3, 2, 2, 2, 4, 149, 3, 2, 2, 2, 4, 151, 3, 2, 2, 2, 4, 153, 3, 2, 2, 2, 4, 155, 3, 2, 2, 2, 4, 157, 3, 2, 2, 2, 4, 159, 3, 2, 2, 2, 4, 161, 3, 2, 2, 2, 4, 163, 3, 2, 2, 2, 4, 165, 3, 2, 2, 2, 4, 167, 3, 2, 2, 2, 4, 171, 3, 2, 2, 2, 4, 173, 3, 2, 2, 2, 4, 175, 3, 2, 2, 2, 4, 177, 3, 2, 2, 2, 5, 231, 3, 2, 2, 2, 7, 241, 3, 2, 2, 2, 9, 248, 3, 2, 2, 2, 11, 257, 3, 2, 2, 2, 13, 264, 3, 2, 2, 2, 15, 271, 3, 2, 2, 2, 17, 278, 3, 2, 2, 2, 19, 285, 3, 2, 2, 2, 21, 293, 3, 2, 2, 2, 23, 305, 3, 2, 2, 2, 25, 315, 3, 2, 2, 2, 27, 324, 3, 2, 2, 2, 29, 330, 3, 2, 2, 2, 31, 337, 3, 2, 2, 2, 33, 344, 3, 2, 2, 2, 35, 352, 3, 2, 2, 2, 37, 361, 3, 2, 2, 2, 39, 367, 3, 2, 2, 2, 41, 384, 3, 2, 2, 2, 43, 400, 3, 2, 2, 2, 45, 406, 3, 2, 2, 2, 47, 410, 3, 2, 2, 2, 49, 412, 3, 2, 2, 2, 51, 414, 3, 2, 2, 2, 53, 417, 3, 2, 2, 2, 55, 419, 3, 2, 2, 2, 57, 457, 3, 2, 2, 2, 59, 460, 3, 2, 2, 2, 61, 506, 3, 2, 2, 2, 63, 508, 3, 2, 2, 2, 65, 511, 3, 2, 2, 2, 67, 515, 3, 2, 2, 2, 69, 519, 3, 2, 2, 2, 71, 521, 3, 2, 2, 2, 73, 523, 3, 2, 2, 2, 75, 528, 3, 2, 2, 2, 77, 530, 3, 2, 2, 2, 79, 536, 3, 2, 2, 2, 81, 542, 3, 2, 2, 2, 83, 547, 3, 2, 2, 2, 85, 549, 3, 2, 2, 2, 87, 552, 3, 2, 2, 2, 89, 555, 3, 2, 2, 2, 91, 560, 3, 2, 2, 2, 93, 564, 3, 2, 2, 2, 95, 569, 3, 2, 2, 2, 97, 575, 3, 2, 2, 2, 99, 578, 3, 2, 2, 2, 101, 580, 3, 2, 2, 2, 103, 586, 3, 2, 2, 2, 105, 588, 3, 2, 2, 2, 107, 593, 3, 2, 2, 2, 109, 598, 3, 2, 2, 2, 111, 608, 3, 2, 2, 2, 113, 610, 3, 2, 2, 2, 115, 613, 3, 2, 2, 2, 117, 616, 3, 2, 2, 2, 119, 618, 3, 2, 2, 2, 121, 621, 3, 2, 2, 2, 123, 623, 3, 2, 2, 2, 125, 626, 3, 2, 2, 2, 127, 628, 3, 2, 2, 2, 129, 630, 3, 2, 2, 2, 131, 632, 3, 2, 2, 2, 133, 634, 3, 2, 2, 2, 135, 636, 3, 2, 2, 2, 137, 641, 3, 2, 2, 2, 139, 663, 3, 2, 2, 2, 141, 665, 3, 2, 2, 2, 143, 676, 3, 2, 2, 2, 145, 680, 3, 2, 2, 2, 147, 684, 3, 2, 2, 2, 149, 688, 3, 2, 2, 2, 151, 693, 3, 2, 2, 2, 153, 699, 3, 2, 2, 2, 155, 705, 3, 2, 2, 2, 157, 709, 3, 2, 2, 2, 159, 713, 3, 2, 2, 2, 161, 716, 3, 2, 2, 2, 163, 725, 3, 2, 2, 2, 165, 728, 3, 2, 2, 2, 167, 734, 3, 2, 2, 2, 169, 745, 3, 2, 2, 2, 171, 747, 3, 2, 2, 2, 173, 749, 3, 2, 2, 2, 175, 753, 3, 2, 2, 2, 177, 757, 3, 2, 2, 2, 179, 761, 3, 2, 2, 2, 181, 763, 3, 2, 2, 2, 183, 765, 3, 2, 2, 2, 185, 767, 3, 2, 2, 2, 187, 769, 3, 2, 2, 2, 189, 771, 3, 2, 2, 2, 191, 773, 3, 2, 2, 2, 193, 775, 3, 2, 2, 2, 195, 777, 3, 2, 2, 2, 197, 779, 3, 2, 2, 2, 199, 781, 3, 2, 2, 2, 201, 783, 3, 2, 2, 2, 203, 785, 3, 2, 2, 2, 205, 787, 3, 2, 2, 2, 207, 789, 3, 2, 2, 2, 209, 791, 3, 2, 2, 2, 211, 793, 3, 2, 2, 2, 213, 795, 3, 2, 2, 2, 215, 797, 3, 2, 2, 2, 217, 799, 3, 2, 2, 2, 219, 801, 3, 2, 2, 2, 221, 803, 3, 2, 2, 2, 223, 805, 3, 2, 2, 2, 225, 807, 3, 2, 2, 2, 227, 809, 3, 2, 2, 2, 229, 811, 3, 2, 2, 2, 231, 232, 5, 185, 92, 2, 232, 233, 5, 195, 97, 2, 233, 234, 5, 215, 107, 2, 234, 235, 5, 215, 107, 2, 235, 236, 5, 187, 93, 2, 236, 237, 5, 183, 91, 2, 237, 238, 5, 217, 108, 2, 238, 239, 3, 2, 2, 2, 239, 240, 8, 2, 2, 2, 240, 6, 3, 2, 2, 2, 241, 242, 5, 185, 92, 2, 242, 243, 5, 213, 106, 2, 243, 244, 5, 207, 103, 2, 244, 245, 5, 209, 104, 2, 245, 246, 3, 2, 2, 2, 246, 247, 8, 3, 3, 2, 247, 8, 3, 2, 2, 2, 248, 249, 5, 187, 93, 2, 249, 250, 5, 205, 102, 2, 250, 251, 5, 213, 106, 2, 251, 252, 5, 195, 97, 2, 252, 253, 5, 183, 91, 2, 253, 254, 5, 193, 96, 2, 254, 255, 3, 2, 2, 2, 255, 256, 8, 4, 3, 2, 256, 10, 3, 2, 2, 2, 257, 258, 5, 187, 93, 2, 258, 259, 5, 221, 110, 2, 259, 260, 5, 179, 89, 2, 260, 261, 5, 201, 100, 2, 261, 262, 3, 2, 2, 2, 262, 263, 8, 5, 2, 2, 263, 12, 3, 2, 2, 2, 264, 265, 5, 189, 94, 2, 265, 266, 5, 213, 106, 2, 266, 267, 5, 207, 103, 2, 267, 268, 5, 203, 101, 2, 268, 269, 3, 2, 2, 2, 269, 270, 8, 6, 3, 2, 270, 14, 3, 2, 2, 2, 271, 272, 5, 191, 95, 2, 272, 273, 5, 213, 106, 2, 273, 274, 5, 207, 103, 2, 274, 275, 5, 199, 99, 2, 275, 276, 3, 2, 2, 2, 276, 277, 8, 7, 2, 2, 277, 16, 3, 2, 2, 2, 278, 279, 5, 199, 99, 2, 279, 280, 5, 187, 93, 2, 280, 281, 5, 187, 93, 2, 281, 282, 5, 209, 104, 2, 282, 283, 3, 2, 2, 2, 283, 284, 8, 8, 3, 2, 284, 18, 3, 2, 2, 2, 285, 286, 5, 201, 100, 2, 286, 287, 5, 195, 97, 2, 287, 288, 5, 203, 101, 2, 288, 289, 5, 195, 97, 2, 289, 290, 5, 217, 108, 2, 290, 291, 3, 2, 2, 2, 291, 292, 8, 9, 2, 2, 292, 20, 3, 2, 2, 2, 293, 294, 5, 203, 101, 2, 294, 295, 5, 221, 110, 2, 295, 296, 5, 111, 55, 2, 296, 297, 5, 187, 93, 2, 297, 298, 5, 225, 112, 2, 298, 299, 5, 209, 104, 2, 299, 300, 5, 179, 89, 2, 300, 301, 5, 205, 102, 2, 301, 302, 5, 185, 92, 2, 302, 303, 3, 2, 2, 2, 303, 304, 8, 10, 3, 2, 304, 22, 3, 2, 2, 2, 305, 306, 5, 209, 104, 2, 306, 307, 5, 213, 106, 2, 307, 308, 5, 207, 103, 2, 308, 309, 5, 197, 98, 2, 309, 310, 5, 187, 93, 2, 310, 311, 5, 183, 91, 2, 311, 312, 5, 217, 108, 2, 312, 313, 3, 2, 2, 2, 313, 314, 8, 11, 3, 2, 314, 24, 3, 2, 2, 2, 315, 316, 5, 213, 106, 2, 316, 317, 5, 187, 93, 2, 317, 318, 5, 205, 102, 2, 318, 319, 5, 179, 89, 2, 319, 320, 5, 203, 101, 2, 320, 321, 5, 187, 93, 2, 321, 322, 3, 2, 2, 2, 322, 323, 8, 12, 3, 2, 323, 26, 3, 2, 2, 2, 324, 325, 5, 213, 106, 2, 325, 326, 5, 207, 103, 2, 326, 327, 5, 223, 111, 2, 327, 328, 3, 2, 2, 2, 328, 329, 8, 13, 2, 2, 329, 28, 3, 2, 2, 2, 330, 331, 5, 215, 107, 2, 331, 332, 5, 193, 96, 2, 332, 333, 5, 207, 103, 2, 333, 334, 5, 223, 111, 2, 334, 335, 3, 2, 2, 2, 335, 336, 8, 14, 2, 2, 336, 30, 3, 2, 2, 2, 337, 338, 5, 215, 107, 2, 338, 339, 5, 207, 103, 2, 339, 340, 5, 213, 106, 2, 340, 341, 5, 217, 108, 2, 341, 342, 3, 2, 2, 2, 342, 343, 8, 15, 2, 2, 343, 32, 3, 2, 2, 2, 344, 345, 5, 215, 107, 2, 345, 346, 5, 217, 108, 2, 346, 347, 5, 179, 89, 2, 347, 348, 5, 217, 108, 2, 348, 349, 5, 215, 107, 2, 349, 350, 3, 2, 2, 2, 350, 351, 8, 16, 2, 2, 351, 34, 3, 2, 2, 2, 352, 353, 5, 223, 111, 2, 353, 354, 5, 193, 96, 2, 354, 355, 5, 187, 93, 2, 355, 356, 5, 213, 106, 2, 356, 357, 5, 187, 93, 2, 357, 358, 3, 2, 2, 2, 358, 359, 8, 17, 2, 2, 359, 36, 3, 2, 2, 2, 360, 362, 10, 2, 2, 2, 361, 360, 3, 2, 2, 2, 362, 363, 3, 2, 2, 2, 363, 361, 3, 2, 2, 2, 363, 364, 3, 2, 2, 2, 364, 365, 3, 2, 2, 2, 365, 366, 8, 18, 2, 2, 366, 38, 3, 2, 2, 2, 367, 368, 7, 49, 2, 2, 368, 369, 7, 49, 2, 2, 369, 373, 3, 2, 2, 2, 370, 372, 10, 3, 2, 2, 371, 370, 3, 2, 2, 2, 372, 375, 3, 2, 2, 2, 373, 371, 3, 2, 2, 2, 373, 374, 3, 2, 2, 2, 374, 377, 3, 2, 2, 2, 375, 373, 3, 2, 2, 2, 376, 378, 7, 15, 2, 2, 377, 376, 3, 2, 2, 2, 377, 378, 3, 2, 2, 2, 378, 380, 3, 2, 2, 2, 379, 381, 7, 12, 2, 2, 380, 379, 3, 2, 2, 2, 380, 381, 3, 2, 2, 2, 381, 382, 3, 2, 2, 2, 382, 383, 8, 19, 4, 2, 383, 40, 3, 2, 2, 2, 384, 385, 7, 49, 2, 2, 385, 386, 7, 44, 2, 2, 386, 391, 3, 2, 2, 2, 387, 390, 5, 41, 20, 2, 388, 390, 11, 2, 2, 2, 389, 387, 3, 2, 2, 2, 389, 388, 3, 2, 2, 2, 390, 393, 3, 2, 2, 2, 391, 392, 3, 2, 2, 2, 391, 389, 3, 2, 2, 2, 392, 394, 3, 2, 2, 2, 393, 391, 3, 2, 2, 2, 394, 395, 7, 44, 2, 2, 395, 396, 7, 49, 2, 2, 396, 397, 3, 2, 2, 2, 397, 398, 8, 20, 4, 2, 398, 42, 3, 2, 2, 2, 399, 401, 9, 4, 2, 2, 400, 399, 3, 2, 2, 2, 401, 402, 3, 2, 2, 2, 402, 400, 3, 2, 2, 2, 402, 403, 3, 2, 2, 2, 403, 404, 3, 2, 2, 2, 404, 405, 8, 21, 4, 2, 405, 44, 3, 2, 2, 2, 406, 407, 7, 126, 2, 2, 407, 408, 3, 2, 2, 2, 408, 409, 8, 22, 5, 2, 409, 46, 3, 2, 2, 2, 410, 411, 9, 5, 2, 2, 411, 48, 3, 2, 2, 2, 412, 413, 9, 6, 2, 2, 413, 50, 3, 2, 2, 2, 414, 415, 7, 94, 2, 2, 415, 416, 9, 7, 2, 2, 416, 52, 3, 2, 2, 2, 417, 418, 10, 8, 2, 2, 418, 54, 3, 2, 2, 2, 419, 421, 9, 9, 2, 2, 420, 422, 9, 10, 2, 2, 421, 420, 3, 2, 2, 2, 421, 422, 3, 2, 2, 2, 422, 424, 3, 2, 2, 2, 423, 425, 5, 47, 23, 2, 424, 423, 3, 2, 2, 2, 425, 426, 3, 2, 2, 2, 426, 424, 3, 2, 2, 2, 426, 427, 3, 2, 2, 2, 427, 56, 3, 2, 2, 2, 428, 433, 7, 36, 2, 2, 429, 432, 5, 51, 25, 2, 430, 432, 5, 53, 26, 2, 431, 429, 3, 2, 2, 2, 431, 430, 3, 2, 2, 2, 432, 435, 3, 2, 2, 2, 433, 431, 3, 2, 2, 2, 433, 434, 3, 2, 2, 2, 434, 436, 3, 2, 2, 2, 435, 433, 3, 2, 2, 2, 436, 458, 7, 36, 2, 2, 437, 438, 7, 36, 2, 2, 438, 439, 7, 36, 2, 2, 439, 440, 7, 36, 2, 2, 440, 444, 3, 2, 2, 2, 441, 443, 10, 3, 2, 2, 442, 441, 3, 2, 2, 2, 443, 446, 3, 2, 2, 2, 444, 445, 3, 2, 2, 2, 444, 442, 3, 2, 2, 2, 445, 447, 3, 2, 2, 2, 446, 444, 3, 2, 2, 2, 447, 448, 7, 36, 2, 2, 448, 449, 7, 36, 2, 2, 449, 450, 7, 36, 2, 2, 450, 452, 3, 2, 2, 2, 451, 453, 7, 36, 2, 2, 452, 451, 3, 2, 2, 2, 452, 453, 3, 2, 2, 2, 453, 455, 3, 2, 2, 2, 454, 456, 7, 36, 2, 2, 455, 454, 3, 2, 2, 2, 455, 456, 3, 2, 2, 2, 456, 458, 3, 2, 2, 2, 457, 428, 3, 2, 2, 2, 457, 437, 3, 2, 2, 2, 458, 58, 3, 2, 2, 2, 459, 461, 5, 47, 23, 2, 460, 459, 3, 2, 2, 2, 461, 462, 3, 2, 2, 2, 462, 460, 3, 2, 2, 2, 462, 463, 3, 2, 2, 2, 463, 60, 3, 2, 2, 2, 464, 466, 5, 47, 23, 2, 465, 464, 3, 2, 2, 2, 466, 467, 3, 2, 2, 2, 467, 465, 3, 2, 2, 2, 467, 468, 3, 2, 2, 2, 468, 469, 3, 2, 2, 2, 469, 473, 5, 75, 37, 2, 470, 472, 5, 47, 23, 2, 471, 470, 3, 2, 2, 2, 472, 475, 3, 2, 2, 2, 473, 471, 3, 2, 2, 2, 473, 474, 3, 2, 2, 2, 474, 507, 3, 2, 2, 2, 475, 473, 3, 2, 2, 2, 476, 478, 5, 75, 37, 2, 477, 479, 5, 47, 23, 2, 478, 477, 3, 2, 2, 2, 479, 480, 3, 2, 2, 2, 480, 478, 3, 2, 2, 2, 480, 481, 3, 2, 2, 2, 481, 507, 3, 2, 2, 2, 482, 484, 5, 47, 23, 2, 483, 482, 3, 2, 2, 2, 484, 485, 3, 2, 2, 2, 485, 483, 3, 2, 2, 2, 485, 486, 3, 2, 2, 2, 486, 494, 3, 2, 2, 2, 487, 491, 5, 75, 37, 2, 488, 490, 5, 47, 23, 2, 489, 488, 3, 2, 2, 2, 490, 493, 3, 2, 2, 2, 491, 489, 3, 2, 2, 2, 491, 492, 3, 2, 2, 2, 492, 495, 3, 2, 2, 2, 493, 491, 3, 2, 2, 2, 494, 487, 3, 2, 2, 2, 494, 495, 3, 2, 2, 2, 495, 496, 3, 2, 2, 2, 496, 497, 5, 55, 27, 2, 497, 507, 3, 2, 2, 2, 498, 500, 5, 75, 37, 2, 499, 501, 5, 47, 23, 2, 500, 499, 3, 2, 2, 2, 501, 502, 3, 2, 2, 2, 502, 500, 3, 2, 2, 2, 502, 503, 3, 2, 2, 2, 503, 504, 3, 2, 2, 2, 504, 505, 5, 55, 27, 2, 505, 507, 3, 2, 2, 2, 506, 465, 3, 2, 2, 2, 506, 476, 3, 2, 2, 2, 506, 483, 3, 2, 2, 2, 506, 498, 3, 2, 2, 2, 507, 62, 3, 2, 2, 2, 508, 509, 5, 181, 90, 2, 509, 510, 5, 227, 113, 2, 510, 64, 3, 2, 2, 2, 511, 512, 5, 179, 89, 2, 512, 513, 5, 205, 102, 2, 513, 514, 5, 185, 92, 2, 514, 66, 3, 2, 2, 2, 515, 516, 5, 179, 89, 2, 516, 517, 5, 215, 107, 2, 517, 518, 5, 183, 91, 2, 518, 68, 3, 2, 2, 2, 519, 520, 7, 63, 2, 2, 520, 70, 3, 2, 2, 2, 521, 522, 7, 46, 2, 2, 522, 72, 3, 2, 2, 2, 523, 524, 5, 185, 92, 2, 524, 525, 5, 187, 93, 2, 525, 526, 5, 215, 107, 2, 526, 527, 5, 183, 91, 2, 527, 74, 3, 2, 2, 2, 528, 529, 7, 48, 2, 2, 529, 76, 3, 2, 2, 2, 530, 531, 5, 189, 94, 2, 531, 532, 5, 179, 89, 2, 532, 533, 5, 201, 100, 2, 533, 534, 5, 215, 107, 2, 534, 535, 5, 187, 93, 2, 535, 78, 3, 2, 2, 2, 536, 537, 5, 189, 94, 2, 537, 538, 5, 195, 97, 2, 538, 539, 5, 213, 106, 2, 539, 540, 5, 215, 107, 2, 540, 541, 5, 217, 108, 2, 541, 80, 3, 2, 2, 2, 542, 543, 5, 201, 100, 2, 543, 544, 5, 179, 89, 2, 544, 545, 5, 215, 107, 2, 545, 546, 5, 217, 108, 2, 546, 82, 3, 2, 2, 2, 547, 548, 7, 42, 2, 2, 548, 84, 3, 2, 2, 2, 549, 550, 5, 195, 97, 2, 550, 551, 5, 205, 102, 2, 551, 86, 3, 2, 2, 2, 552, 553, 5, 195, 97, 2, 553, 554, 5, 215, 107, 2, 554, 88, 3, 2, 2, 2, 555, 556, 5, 201, 100, 2, 556, 557, 5, 195, 97, 2, 557, 558, 5, 199, 99, 2, 558, 559, 5, 187, 93, 2, 559, 90, 3, 2, 2, 2, 560, 561, 5, 205, 102, 2, 561, 562, 5, 207, 103, 2, 562, 563, 5, 217, 108, 2, 563, 92, 3, 2, 2, 2, 564, 565, 5, 205, 102, 2, 565, 566, 5, 219, 109, 2, 566, 567, 5, 201, 100, 2, 567, 568, 5, 201, 100, 2, 568, 94, 3, 2, 2, 2, 569, 570, 5, 205, 102, 2, 570, 571, 5, 219, 109, 2, 571, 572, 5, 201, 100, 2, 572, 573, 5, 201, 100, 2, 573, 574, 5, 215, 107, 2, 574, 96, 3, 2, 2, 2, 575, 576, 5, 207, 103, 2, 576, 577, 5, 213, 106, 2, 577, 98, 3, 2, 2, 2, 578, 579, 7, 65, 2, 2, 579, 100, 3, 2, 2, 2, 580, 581, 5, 213, 106, 2, 581, 582, 5, 201, 100, 2, 582, 583, 5, 195, 97, 2, 583, 584, 5, 199, 99, 2, 584, 585, 5, 187, 93, 2, 585, 102, 3, 2, 2, 2, 586, 587, 7, 43, 2, 2, 587, 104, 3, 2, 2, 2, 588, 589, 5, 217, 108, 2, 589, 590, 5, 213, 106, 2, 590, 591, 5, 219, 109, 2, 591, 592, 5, 187, 93, 2, 592, 106, 3, 2, 2, 2, 593, 594, 5, 195, 97, 2, 594, 595, 5, 205, 102, 2, 595, 596, 5, 189, 94, 2, 596, 597, 5, 207, 103, 2, 597, 108, 3, 2, 2, 2, 598, 599, 5, 189, 94, 2, 599, 600, 5, 219, 109, 2, 600, 601, 5, 205, 102, 2, 601, 602, 5, 183, 91, 2, 602, 603, 5, 217, 108, 2, 603, 604, 5, 195, 97, 2, 604, 605, 5, 207, 103, 2, 605, 606, 5, 205, 102, 2, 606, 607, 5, 215, 107, 2, 607, 110, 3, 2, 2, 2, 608, 609, 7, 97, 2, 2, 609, 112, 3, 2, 2, 2, 610, 611, 7, 63, 2, 2, 611, 612, 7, 63, 2, 2, 612, 114, 3, 2, 2, 2, 613, 614, 7, 35, 2, 2, 614, 615, 7, 63, 2, 2, 615, 116, 3, 2, 2, 2, 616, 617, 7, 62, 2, 2, 617, 118, 3, 2, 2, 2, 618, 619, 7, 62, 2, 2, 619, 620, 7, 63, 2, 2, 620, 120, 3, 2, 2, 2, 621, 622, 7, 64, 2, 2, 622, 122, 3, 2, 2, 2, 623, 624, 7, 64, 2, 2, 624, 625, 7, 63, 2, 2, 625, 124, 3, 2, 2, 2, 626, 627, 7, 45, 2, 2, 627, 126, 3, 2, 2, 2, 628, 629, 7, 47, 2, 2, 629, 128, 3, 2, 2, 2, 630, 631, 7, 44, 2, 2, 631, 130, 3, 2, 2, 2, 632, 633, 7, 49, 2, 2, 633, 132, 3, 2, 2, 2, 634, 635, 7, 39, 2, 2, 635, 134, 3, 2, 2, 2, 636, 637, 7, 93, 2, 2, 637, 638, 3, 2, 2, 2, 638, 639, 8, 67, 2, 2, 639, 640, 8, 67, 2, 2, 640, 136, 3, 2, 2, 2, 641, 642, 7, 95, 2, 2, 642, 643, 3, 2, 2, 2, 643, 644, 8, 68, 5, 2, 644, 645, 8, 68, 5, 2, 645, 138, 3, 2, 2, 2, 646, 652, 5, 49, 24, 2, 647, 651, 5, 49, 24, 2, 648, 651, 5, 47, 23, 2, 649, 651, 7, 97, 2, 2, 650, 647, 3, 2, 2, 2, 650, 648, 3, 2, 2, 2, 650, 649, 3, 2, 2, 2, 651, 654, 3, 2, 2, 2, 652, 650, 3, 2, 2, 2, 652, 653, 3, 2, 2, 2, 653, 664, 3, 2, 2, 2, 654, 652, 3, 2, 2, 2, 655, 659, 9, 11, 2, 2, 656, 660, 5, 49, 24, 2, 657, 660, 5, 47, 23, 2, 658, 660, 7, 97, 2, 2, 659, 656, 3, 2, 2, 2, 659, 657, 3, 2, 2, 2, 659, 658, 3, 2, 2, 2, 660, 661, 3, 2, 2, 2, 661, 659, 3, 2, 2, 2, 661, 662, 3, 2, 2, 2, 662, 664, 3, 2, 2, 2, 663, 646, 3, 2, 2, 2, 663, 655, 3, 2, 2, 2, 664, 140, 3, 2, 2, 2, 665, 671, 7, 98, 2, 2, 666, 670, 10, 12, 2, 2, 667, 668, 7, 98, 2, 2, 668, 670, 7, 98, 2, 2, 669, 666, 3, 2, 2, 2, 669, 667, 3, 2, 2, 2, 670, 673, 3, 2, 2, 2, 671, 669, 3, 2, 2, 2, 671, 672, 3, 2, 2, 2, 672, 674, 3, 2, 2, 2, 673, 671, 3, 2, 2, 2, 674, 675, 7, 98, 2, 2, 675, 142, 3, 2, 2, 2, 676, 677, 5, 39, 19, 2, 677, 678, 3, 2, 2, 2, 678, 679, 8, 71, 4, 2, 679, 144, 3, 2, 2, 2, 680, 681, 5, 41, 20, 2, 681, 682, 3, 2, 2, 2, 682, 683, 8, 72, 4, 2, 683, 146, 3, 2, 2, 2, 684, 685, 5, 43, 21, 2, 685, 686, 3, 2, 2, 2, 686, 687, 8, 73, 4, 2, 687, 148, 3, 2, 2, 2, 688, 689, 7, 126, 2, 2, 689, 690, 3, 2, 2, 2, 690, 691, 8, 74, 6, 2, 691, 692, 8, 74, 5, 2, 692, 150, 3, 2, 2, 2, 693, 694, 7, 93, 2, 2, 694, 695, 3, 2, 2, 2, 695, 696, 8, 75, 7, 2, 696, 697, 8, 75, 3, 2, 697, 698, 8, 75, 3, 2, 698, 152, 3, 2, 2, 2, 699, 700, 7, 95, 2, 2, 700, 701, 3, 2, 2, 2, 701, 702, 8, 76, 5, 2, 702, 703, 8, 76, 5, 2, 703, 704, 8, 76, 8, 2, 704, 154, 3, 2, 2, 2, 705, 706, 7, 46, 2, 2, 706, 707, 3, 2, 2, 2, 707, 708, 8, 77, 9, 2, 708, 156, 3, 2, 2, 2, 709, 710, 7, 63, 2, 2, 710, 711, 3, 2, 2, 2, 711, 712, 8, 78, 10, 2, 712, 158, 3, 2, 2, 2, 713, 714, 5, 179, 89, 2, 714, 715, 5, 215, 107, 2, 715, 160, 3, 2, 2, 2, 716, 717, 5, 203, 101, 2, 717, 718, 5, 187, 93, 2, 718, 719, 5, 217, 108, 2, 719, 720, 5, 179, 89, 2, 720, 721, 5, 185, 92, 2, 721, 722, 5, 179, 89, 2, 722, 723, 5, 217, 108, 2, 723, 724, 5, 179, 89, 2, 724, 162, 3, 2, 2, 2, 725, 726, 5, 207, 103, 2, 726, 727, 5, 205, 102, 2, 727, 164, 3, 2, 2, 2, 728, 729, 5, 223, 111, 2, 729, 730, 5, 195, 97, 2, 730, 731, 5, 217, 108, 2, 731, 732, 5, 193, 96, 2, 732, 166, 3, 2, 2, 2, 733, 735, 5, 169, 84, 2, 734, 733, 3, 2, 2, 2, 735, 736, 3, 2, 2, 2, 736, 734, 3, 2, 2, 2, 736, 737, 3, 2, 2, 2, 737, 168, 3, 2, 2, 2, 738, 740, 10, 13, 2, 2, 739, 738, 3, 2, 2, 2, 740, 741, 3, 2, 2, 2, 741, 739, 3, 2, 2, 2, 741, 742, 3, 2, 2, 2, 742, 746, 3, 2, 2, 2, 743, 744, 7, 49, 2, 2, 744, 746, 10, 14, 2, 2, 745, 739, 3, 2, 2, 2, 745, 743, 3, 2, 2, 2, 746, 170, 3, 2, 2, 2, 747, 748, 5, 141, 70, 2, 748, 172, 3, 2, 2, 2, 749, 750, 5, 39, 19, 2, 750, 751, 3, 2, 2, 2, 751, 752, 8, 86, 4, 2, 752, 174, 3, 2, 2, 2, 753, 754, 5, 41, 20, 2, 754, 755, 3, 2, 2, 2, 755, 756, 8, 87, 4, 2, 756, 176, 3, 2, 2, 2, 757, 758, 5, 43, 21, 2, 758, 759, 3, 2, 2, 2, 759, 760, 8, 88, 4, 2, 760, 178, 3, 2, 2, 2, 761, 762, 9, 15, 2, 2, 762, 180, 3, 2, 2, 2, 763, 764, 9, 16, 2, 2, 764, 182, 3, 2, 2, 2, 765, 766, 9, 17, 2, 2, 766, 184, 3, 2, 2, 2, 767, 768, 9, 18, 2, 2, 768, 186, 3, 2, 2, 2, 769, 770, 9, 9, 2, 2, 770, 188, 3, 2, 2, 2, 771, 772, 9, 19, 2, 2, 772, 190, 3, 2, 2, 2, 773, 774, 9, 20, 2, 2, 774, 192, 3, 2, 2, 2, 775, 776, 9, 21, 2, 2, 776, 194, 3, 2, 2, 2, 777, 778, 9, 22, 2, 2, 778, 196, 3, 2, 2, 2, 779, 780, 9, 23, 2, 2, 780, 198, 3, 2, 2, 2, 781, 782, 9, 24, 2, 2, 782, 200, 3, 2, 2, 2, 783, 784, 9, 25, 2, 2, 784, 202, 3, 2, 2, 2, 785, 786, 9, 26, 2, 2, 786, 204, 3, 2, 2, 2, 787, 788, 9, 27, 2, 2, 788, 206, 3, 2, 2, 2, 789, 790, 9, 28, 2, 2, 790, 208, 3, 2, 2, 2, 791, 792, 9, 29, 2, 2, 792, 210, 3, 2, 2, 2, 793, 794, 9, 30, 2, 2, 794, 212, 3, 2, 2, 2, 795, 796, 9, 31, 2, 2, 796, 214, 3, 2, 2, 2, 797, 798, 9, 32, 2, 2, 798, 216, 3, 2, 2, 2, 799, 800, 9, 33, 2, 2, 800, 218, 3, 2, 2, 2, 801, 802, 9, 34, 2, 2, 802, 220, 3, 2, 2, 2, 803, 804, 9, 35, 2, 2, 804, 222, 3, 2, 2, 2, 805, 806, 9, 36, 2, 2, 806, 224, 3, 2, 2, 2, 807, 808, 9, 37, 2, 2, 808, 226, 3, 2, 2, 2, 809, 810, 9, 38, 2, 2, 810, 228, 3, 2, 2, 2, 811, 812, 9, 39, 2, 2, 812, 230, 3, 2, 2, 2, 39, 2, 3, 4, 363, 373, 377, 380, 389, 391, 402, 421, 426, 431, 433, 444, 452, 455, 457, 462, 467, 473, 480, 485, 491, 494, 502, 506, 650, 652, 659, 661, 663, 669, 671, 736, 741, 745, 11, 7, 3, 2, 7, 4, 2, 2, 3, 2, 6, 2, 2, 9, 23, 2, 9, 63, 2, 9, 64, 2, 9, 31, 2, 9, 30, 2] \ No newline at end of file diff --git a/packages/kbn-monaco/src/esql/antlr/esql_lexer.tokens b/packages/kbn-monaco/src/esql/antlr/esql_lexer.tokens index b72e97b9a2961..c3160ce1f6472 100644 --- a/packages/kbn-monaco/src/esql/antlr/esql_lexer.tokens +++ b/packages/kbn-monaco/src/esql/antlr/esql_lexer.tokens @@ -1,98 +1,93 @@ DISSECT=1 -GROK=2 -EVAL=3 -EXPLAIN=4 +DROP=2 +ENRICH=3 +EVAL=4 FROM=5 -ROW=6 -STATS=7 -WHERE=8 -SORT=9 -MV_EXPAND=10 -LIMIT=11 -PROJECT=12 -DROP=13 -RENAME=14 -SHOW=15 -ENRICH=16 -KEEP=17 +GROK=6 +KEEP=7 +LIMIT=8 +MV_EXPAND=9 +PROJECT=10 +RENAME=11 +ROW=12 +SHOW=13 +SORT=14 +STATS=15 +WHERE=16 +UNKNOWN_CMD=17 LINE_COMMENT=18 MULTILINE_COMMENT=19 WS=20 -EXPLAIN_WS=21 -EXPLAIN_LINE_COMMENT=22 -EXPLAIN_MULTILINE_COMMENT=23 -PIPE=24 -STRING=25 -INTEGER_LITERAL=26 -DECIMAL_LITERAL=27 -BY=28 -DATE_LITERAL=29 -AND=30 -ASSIGN=31 -COMMA=32 -DOT=33 -LP=34 -OPENING_BRACKET=35 -CLOSING_BRACKET=36 -NOT=37 +PIPE=21 +STRING=22 +INTEGER_LITERAL=23 +DECIMAL_LITERAL=24 +BY=25 +AND=26 +ASC=27 +ASSIGN=28 +COMMA=29 +DESC=30 +DOT=31 +FALSE=32 +FIRST=33 +LAST=34 +LP=35 +IN=36 +IS=37 LIKE=38 -RLIKE=39 -IN=40 -IS=41 -AS=42 -NULL=43 -OR=44 +NOT=39 +NULL=40 +NULLS=41 +OR=42 +PARAM=43 +RLIKE=44 RP=45 -UNDERSCORE=46 +TRUE=46 INFO=47 FUNCTIONS=48 -BOOLEAN_VALUE=49 -COMPARISON_OPERATOR=50 -PLUS=51 -MINUS=52 -ASTERISK=53 -SLASH=54 -PERCENT=55 -TEN=56 -ORDERING=57 -NULLS_ORDERING=58 -NULLS_ORDERING_DIRECTION=59 -MATH_FUNCTION=60 -UNARY_FUNCTION=61 -WHERE_FUNCTIONS=62 +UNDERSCORE=49 +EQ=50 +NEQ=51 +LT=52 +LTE=53 +GT=54 +GTE=55 +PLUS=56 +MINUS=57 +ASTERISK=58 +SLASH=59 +PERCENT=60 +OPENING_BRACKET=61 +CLOSING_BRACKET=62 UNQUOTED_IDENTIFIER=63 QUOTED_IDENTIFIER=64 EXPR_LINE_COMMENT=65 EXPR_MULTILINE_COMMENT=66 EXPR_WS=67 -METADATA=68 -SRC_UNQUOTED_IDENTIFIER=69 -SRC_QUOTED_IDENTIFIER=70 -SRC_LINE_COMMENT=71 -SRC_MULTILINE_COMMENT=72 -SRC_WS=73 -ON=74 -WITH=75 -ENR_UNQUOTED_IDENTIFIER=76 -ENR_QUOTED_IDENTIFIER=77 -ENR_LINE_COMMENT=78 -ENR_MULTILINE_COMMENT=79 -ENR_WS=80 -EXPLAIN_PIPE=81 -'by'=28 -'and'=30 -'.'=33 -'('=34 -']'=36 -'or'=44 +AS=68 +METADATA=69 +ON=70 +WITH=71 +SRC_UNQUOTED_IDENTIFIER=72 +SRC_QUOTED_IDENTIFIER=73 +SRC_LINE_COMMENT=74 +SRC_MULTILINE_COMMENT=75 +SRC_WS=76 +'.'=31 +'('=35 +'?'=43 ')'=45 -'_'=46 -'info'=47 -'functions'=48 -'+'=51 -'-'=52 -'*'=53 -'/'=54 -'%'=55 -'10'=56 -'nulls'=58 +'_'=49 +'=='=50 +'!='=51 +'<'=52 +'<='=53 +'>'=54 +'>='=55 +'+'=56 +'-'=57 +'*'=58 +'/'=59 +'%'=60 +']'=62 diff --git a/packages/kbn-monaco/src/esql/antlr/esql_lexer.ts b/packages/kbn-monaco/src/esql/antlr/esql_lexer.ts index 1c5fc5a918aa4..4bbb3eb4968c3 100644 --- a/packages/kbn-monaco/src/esql/antlr/esql_lexer.ts +++ b/packages/kbn-monaco/src/esql/antlr/esql_lexer.ts @@ -18,90 +18,83 @@ import * as Utils from "antlr4ts/misc/Utils"; export class esql_lexer extends Lexer { public static readonly DISSECT = 1; - public static readonly GROK = 2; - public static readonly EVAL = 3; - public static readonly EXPLAIN = 4; + public static readonly DROP = 2; + public static readonly ENRICH = 3; + public static readonly EVAL = 4; public static readonly FROM = 5; - public static readonly ROW = 6; - public static readonly STATS = 7; - public static readonly WHERE = 8; - public static readonly SORT = 9; - public static readonly MV_EXPAND = 10; - public static readonly LIMIT = 11; - public static readonly PROJECT = 12; - public static readonly DROP = 13; - public static readonly RENAME = 14; - public static readonly SHOW = 15; - public static readonly ENRICH = 16; - public static readonly KEEP = 17; + public static readonly GROK = 6; + public static readonly KEEP = 7; + public static readonly LIMIT = 8; + public static readonly MV_EXPAND = 9; + public static readonly PROJECT = 10; + public static readonly RENAME = 11; + public static readonly ROW = 12; + public static readonly SHOW = 13; + public static readonly SORT = 14; + public static readonly STATS = 15; + public static readonly WHERE = 16; + public static readonly UNKNOWN_CMD = 17; public static readonly LINE_COMMENT = 18; public static readonly MULTILINE_COMMENT = 19; public static readonly WS = 20; - public static readonly EXPLAIN_WS = 21; - public static readonly EXPLAIN_LINE_COMMENT = 22; - public static readonly EXPLAIN_MULTILINE_COMMENT = 23; - public static readonly PIPE = 24; - public static readonly STRING = 25; - public static readonly INTEGER_LITERAL = 26; - public static readonly DECIMAL_LITERAL = 27; - public static readonly BY = 28; - public static readonly DATE_LITERAL = 29; - public static readonly AND = 30; - public static readonly ASSIGN = 31; - public static readonly COMMA = 32; - public static readonly DOT = 33; - public static readonly LP = 34; - public static readonly OPENING_BRACKET = 35; - public static readonly CLOSING_BRACKET = 36; - public static readonly NOT = 37; + public static readonly PIPE = 21; + public static readonly STRING = 22; + public static readonly INTEGER_LITERAL = 23; + public static readonly DECIMAL_LITERAL = 24; + public static readonly BY = 25; + public static readonly AND = 26; + public static readonly ASC = 27; + public static readonly ASSIGN = 28; + public static readonly COMMA = 29; + public static readonly DESC = 30; + public static readonly DOT = 31; + public static readonly FALSE = 32; + public static readonly FIRST = 33; + public static readonly LAST = 34; + public static readonly LP = 35; + public static readonly IN = 36; + public static readonly IS = 37; public static readonly LIKE = 38; - public static readonly RLIKE = 39; - public static readonly IN = 40; - public static readonly IS = 41; - public static readonly AS = 42; - public static readonly NULL = 43; - public static readonly OR = 44; + public static readonly NOT = 39; + public static readonly NULL = 40; + public static readonly NULLS = 41; + public static readonly OR = 42; + public static readonly PARAM = 43; + public static readonly RLIKE = 44; public static readonly RP = 45; - public static readonly UNDERSCORE = 46; + public static readonly TRUE = 46; public static readonly INFO = 47; public static readonly FUNCTIONS = 48; - public static readonly BOOLEAN_VALUE = 49; - public static readonly COMPARISON_OPERATOR = 50; - public static readonly PLUS = 51; - public static readonly MINUS = 52; - public static readonly ASTERISK = 53; - public static readonly SLASH = 54; - public static readonly PERCENT = 55; - public static readonly TEN = 56; - public static readonly ORDERING = 57; - public static readonly NULLS_ORDERING = 58; - public static readonly NULLS_ORDERING_DIRECTION = 59; - public static readonly MATH_FUNCTION = 60; - public static readonly UNARY_FUNCTION = 61; - public static readonly WHERE_FUNCTIONS = 62; + public static readonly UNDERSCORE = 49; + public static readonly EQ = 50; + public static readonly NEQ = 51; + public static readonly LT = 52; + public static readonly LTE = 53; + public static readonly GT = 54; + public static readonly GTE = 55; + public static readonly PLUS = 56; + public static readonly MINUS = 57; + public static readonly ASTERISK = 58; + public static readonly SLASH = 59; + public static readonly PERCENT = 60; + public static readonly OPENING_BRACKET = 61; + public static readonly CLOSING_BRACKET = 62; public static readonly UNQUOTED_IDENTIFIER = 63; public static readonly QUOTED_IDENTIFIER = 64; public static readonly EXPR_LINE_COMMENT = 65; public static readonly EXPR_MULTILINE_COMMENT = 66; public static readonly EXPR_WS = 67; - public static readonly METADATA = 68; - public static readonly SRC_UNQUOTED_IDENTIFIER = 69; - public static readonly SRC_QUOTED_IDENTIFIER = 70; - public static readonly SRC_LINE_COMMENT = 71; - public static readonly SRC_MULTILINE_COMMENT = 72; - public static readonly SRC_WS = 73; - public static readonly ON = 74; - public static readonly WITH = 75; - public static readonly ENR_UNQUOTED_IDENTIFIER = 76; - public static readonly ENR_QUOTED_IDENTIFIER = 77; - public static readonly ENR_LINE_COMMENT = 78; - public static readonly ENR_MULTILINE_COMMENT = 79; - public static readonly ENR_WS = 80; - public static readonly EXPLAIN_PIPE = 81; - public static readonly EXPLAIN_MODE = 1; - public static readonly EXPRESSION = 2; - public static readonly SOURCE_IDENTIFIERS = 3; - public static readonly ENRICH_IDENTIFIERS = 4; + public static readonly AS = 68; + public static readonly METADATA = 69; + public static readonly ON = 70; + public static readonly WITH = 71; + public static readonly SRC_UNQUOTED_IDENTIFIER = 72; + public static readonly SRC_QUOTED_IDENTIFIER = 73; + public static readonly SRC_LINE_COMMENT = 74; + public static readonly SRC_MULTILINE_COMMENT = 75; + public static readonly SRC_WS = 76; + public static readonly EXPRESSION = 1; + public static readonly SOURCE_IDENTIFIERS = 2; // tslint:disable:no-trailing-whitespace public static readonly channelNames: string[] = [ @@ -110,30 +103,26 @@ export class esql_lexer extends Lexer { // tslint:disable:no-trailing-whitespace public static readonly modeNames: string[] = [ - "DEFAULT_MODE", "EXPLAIN_MODE", "EXPRESSION", "SOURCE_IDENTIFIERS", "ENRICH_IDENTIFIERS", + "DEFAULT_MODE", "EXPRESSION", "SOURCE_IDENTIFIERS", ]; public static readonly ruleNames: string[] = [ - "DISSECT", "GROK", "EVAL", "EXPLAIN", "FROM", "ROW", "STATS", "WHERE", - "SORT", "MV_EXPAND", "LIMIT", "PROJECT", "DROP", "RENAME", "SHOW", "ENRICH", - "KEEP", "LINE_COMMENT", "MULTILINE_COMMENT", "WS", "EXPLAIN_OPENING_BRACKET", - "EXPLAIN_PIPE", "EXPLAIN_WS", "EXPLAIN_LINE_COMMENT", "EXPLAIN_MULTILINE_COMMENT", - "PIPE", "DIGIT", "LETTER", "ESCAPE_SEQUENCE", "UNESCAPED_CHARS", "EXPONENT", - "STRING", "INTEGER_LITERAL", "DECIMAL_LITERAL", "BY", "DATE_LITERAL", - "AND", "ASSIGN", "COMMA", "DOT", "LP", "OPENING_BRACKET", "CLOSING_BRACKET", - "NOT", "LIKE", "RLIKE", "IN", "IS", "AS", "NULL", "OR", "RP", "UNDERSCORE", - "INFO", "FUNCTIONS", "BOOLEAN_VALUE", "COMPARISON_OPERATOR", "PLUS", "MINUS", - "ASTERISK", "SLASH", "PERCENT", "TEN", "ORDERING", "NULLS_ORDERING", "NULLS_ORDERING_DIRECTION", - "MATH_FUNCTION", "UNARY_FUNCTION", "WHERE_FUNCTIONS", "UNQUOTED_IDENTIFIER", - "QUOTED_IDENTIFIER", "EXPR_LINE_COMMENT", "EXPR_MULTILINE_COMMENT", "EXPR_WS", - "SRC_PIPE", "SRC_OPENING_BRACKET", "SRC_CLOSING_BRACKET", "SRC_COMMA", - "SRC_ASSIGN", "METADATA", "SRC_UNQUOTED_IDENTIFIER", "SRC_UNQUOTED_IDENTIFIER_PART", - "SRC_QUOTED_IDENTIFIER", "SRC_LINE_COMMENT", "SRC_MULTILINE_COMMENT", - "SRC_WS", "ON", "WITH", "ENR_PIPE", "ENR_CLOSING_BRACKET", "ENR_COMMA", - "ENR_ASSIGN", "ENR_UNQUOTED_IDENTIFIER", "ENR_UNQUOTED_IDENTIFIER_PART", - "ENR_QUOTED_IDENTIFIER", "ENR_LINE_COMMENT", "ENR_MULTILINE_COMMENT", - "ENR_WS", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", - "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", + "DISSECT", "DROP", "ENRICH", "EVAL", "FROM", "GROK", "KEEP", "LIMIT", + "MV_EXPAND", "PROJECT", "RENAME", "ROW", "SHOW", "SORT", "STATS", "WHERE", + "UNKNOWN_CMD", "LINE_COMMENT", "MULTILINE_COMMENT", "WS", "PIPE", "DIGIT", + "LETTER", "ESCAPE_SEQUENCE", "UNESCAPED_CHARS", "EXPONENT", "STRING", + "INTEGER_LITERAL", "DECIMAL_LITERAL", "BY", "AND", "ASC", "ASSIGN", "COMMA", + "DESC", "DOT", "FALSE", "FIRST", "LAST", "LP", "IN", "IS", "LIKE", "NOT", + "NULL", "NULLS", "OR", "PARAM", "RLIKE", "RP", "TRUE", "INFO", "FUNCTIONS", + "UNDERSCORE", "EQ", "NEQ", "LT", "LTE", "GT", "GTE", "PLUS", "MINUS", + "ASTERISK", "SLASH", "PERCENT", "OPENING_BRACKET", "CLOSING_BRACKET", + "UNQUOTED_IDENTIFIER", "QUOTED_IDENTIFIER", "EXPR_LINE_COMMENT", "EXPR_MULTILINE_COMMENT", + "EXPR_WS", "SRC_PIPE", "SRC_OPENING_BRACKET", "SRC_CLOSING_BRACKET", "SRC_COMMA", + "SRC_ASSIGN", "AS", "METADATA", "ON", "WITH", "SRC_UNQUOTED_IDENTIFIER", + "SRC_UNQUOTED_IDENTIFIER_PART", "SRC_QUOTED_IDENTIFIER", "SRC_LINE_COMMENT", + "SRC_MULTILINE_COMMENT", "SRC_WS", "A", "B", "C", "D", "E", "F", "G", + "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", + "V", "W", "X", "Y", "Z", ]; private static readonly _LITERAL_NAMES: Array = [ @@ -141,27 +130,25 @@ export class esql_lexer extends Lexer { undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, - "'by'", undefined, "'and'", undefined, undefined, "'.'", "'('", undefined, - "']'", undefined, undefined, undefined, undefined, undefined, undefined, - undefined, "'or'", "')'", "'_'", "'info'", "'functions'", undefined, undefined, - "'+'", "'-'", "'*'", "'/'", "'%'", "'10'", undefined, "'nulls'", + undefined, undefined, undefined, "'.'", undefined, undefined, undefined, + "'('", undefined, undefined, undefined, undefined, undefined, undefined, + undefined, "'?'", undefined, "')'", undefined, undefined, undefined, "'_'", + "'=='", "'!='", "'<'", "'<='", "'>'", "'>='", "'+'", "'-'", "'*'", "'/'", + "'%'", undefined, "']'", ]; private static readonly _SYMBOLIC_NAMES: Array = [ - undefined, "DISSECT", "GROK", "EVAL", "EXPLAIN", "FROM", "ROW", "STATS", - "WHERE", "SORT", "MV_EXPAND", "LIMIT", "PROJECT", "DROP", "RENAME", "SHOW", - "ENRICH", "KEEP", "LINE_COMMENT", "MULTILINE_COMMENT", "WS", "EXPLAIN_WS", - "EXPLAIN_LINE_COMMENT", "EXPLAIN_MULTILINE_COMMENT", "PIPE", "STRING", - "INTEGER_LITERAL", "DECIMAL_LITERAL", "BY", "DATE_LITERAL", "AND", "ASSIGN", - "COMMA", "DOT", "LP", "OPENING_BRACKET", "CLOSING_BRACKET", "NOT", "LIKE", - "RLIKE", "IN", "IS", "AS", "NULL", "OR", "RP", "UNDERSCORE", "INFO", "FUNCTIONS", - "BOOLEAN_VALUE", "COMPARISON_OPERATOR", "PLUS", "MINUS", "ASTERISK", "SLASH", - "PERCENT", "TEN", "ORDERING", "NULLS_ORDERING", "NULLS_ORDERING_DIRECTION", - "MATH_FUNCTION", "UNARY_FUNCTION", "WHERE_FUNCTIONS", "UNQUOTED_IDENTIFIER", - "QUOTED_IDENTIFIER", "EXPR_LINE_COMMENT", "EXPR_MULTILINE_COMMENT", "EXPR_WS", - "METADATA", "SRC_UNQUOTED_IDENTIFIER", "SRC_QUOTED_IDENTIFIER", "SRC_LINE_COMMENT", - "SRC_MULTILINE_COMMENT", "SRC_WS", "ON", "WITH", "ENR_UNQUOTED_IDENTIFIER", - "ENR_QUOTED_IDENTIFIER", "ENR_LINE_COMMENT", "ENR_MULTILINE_COMMENT", - "ENR_WS", "EXPLAIN_PIPE", + undefined, "DISSECT", "DROP", "ENRICH", "EVAL", "FROM", "GROK", "KEEP", + "LIMIT", "MV_EXPAND", "PROJECT", "RENAME", "ROW", "SHOW", "SORT", "STATS", + "WHERE", "UNKNOWN_CMD", "LINE_COMMENT", "MULTILINE_COMMENT", "WS", "PIPE", + "STRING", "INTEGER_LITERAL", "DECIMAL_LITERAL", "BY", "AND", "ASC", "ASSIGN", + "COMMA", "DESC", "DOT", "FALSE", "FIRST", "LAST", "LP", "IN", "IS", "LIKE", + "NOT", "NULL", "NULLS", "OR", "PARAM", "RLIKE", "RP", "TRUE", "INFO", + "FUNCTIONS", "UNDERSCORE", "EQ", "NEQ", "LT", "LTE", "GT", "GTE", "PLUS", + "MINUS", "ASTERISK", "SLASH", "PERCENT", "OPENING_BRACKET", "CLOSING_BRACKET", + "UNQUOTED_IDENTIFIER", "QUOTED_IDENTIFIER", "EXPR_LINE_COMMENT", "EXPR_MULTILINE_COMMENT", + "EXPR_WS", "AS", "METADATA", "ON", "WITH", "SRC_UNQUOTED_IDENTIFIER", + "SRC_QUOTED_IDENTIFIER", "SRC_LINE_COMMENT", "SRC_MULTILINE_COMMENT", + "SRC_WS", ]; public static readonly VOCABULARY: Vocabulary = new VocabularyImpl(esql_lexer._LITERAL_NAMES, esql_lexer._SYMBOLIC_NAMES, []); @@ -193,780 +180,400 @@ export class esql_lexer extends Lexer { // @Override public get modeNames(): string[] { return esql_lexer.modeNames; } - private static readonly _serializedATNSegments: number = 3; + private static readonly _serializedATNSegments: number = 2; private static readonly _serializedATNSegment0: string = - "\x03\uC91D\uCABA\u058D\uAFBA\u4F53\u0607\uEA8B\uC241\x02S\u0640\b\x01" + - "\b\x01\b\x01\b\x01\b\x01\x04\x02\t\x02\x04\x03\t\x03\x04\x04\t\x04\x04" + - "\x05\t\x05\x04\x06\t\x06\x04\x07\t\x07\x04\b\t\b\x04\t\t\t\x04\n\t\n\x04" + - "\v\t\v\x04\f\t\f\x04\r\t\r\x04\x0E\t\x0E\x04\x0F\t\x0F\x04\x10\t\x10\x04" + - "\x11\t\x11\x04\x12\t\x12\x04\x13\t\x13\x04\x14\t\x14\x04\x15\t\x15\x04" + - "\x16\t\x16\x04\x17\t\x17\x04\x18\t\x18\x04\x19\t\x19\x04\x1A\t\x1A\x04" + - "\x1B\t\x1B\x04\x1C\t\x1C\x04\x1D\t\x1D\x04\x1E\t\x1E\x04\x1F\t\x1F\x04" + - " \t \x04!\t!\x04\"\t\"\x04#\t#\x04$\t$\x04%\t%\x04&\t&\x04\'\t\'\x04(" + - "\t(\x04)\t)\x04*\t*\x04+\t+\x04,\t,\x04-\t-\x04.\t.\x04/\t/\x040\t0\x04" + - "1\t1\x042\t2\x043\t3\x044\t4\x045\t5\x046\t6\x047\t7\x048\t8\x049\t9\x04" + - ":\t:\x04;\t;\x04<\t<\x04=\t=\x04>\t>\x04?\t?\x04@\t@\x04A\tA\x04B\tB\x04" + - "C\tC\x04D\tD\x04E\tE\x04F\tF\x04G\tG\x04H\tH\x04I\tI\x04J\tJ\x04K\tK\x04" + - "L\tL\x04M\tM\x04N\tN\x04O\tO\x04P\tP\x04Q\tQ\x04R\tR\x04S\tS\x04T\tT\x04" + - "U\tU\x04V\tV\x04W\tW\x04X\tX\x04Y\tY\x04Z\tZ\x04[\t[\x04\\\t\\\x04]\t" + - "]\x04^\t^\x04_\t_\x04`\t`\x04a\ta\x04b\tb\x04c\tc\x04d\td\x04e\te\x04" + - "f\tf\x04g\tg\x04h\th\x04i\ti\x04j\tj\x04k\tk\x04l\tl\x04m\tm\x04n\tn\x04" + - "o\to\x04p\tp\x04q\tq\x04r\tr\x04s\ts\x04t\tt\x04u\tu\x04v\tv\x04w\tw\x04" + - "x\tx\x04y\ty\x04z\tz\x04{\t{\x04|\t|\x04}\t}\x03\x02\x03\x02\x03\x02\x03" + - "\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x03\x03\x03\x03" + - "\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x04\x03\x04\x03\x04\x03\x04\x03" + - "\x04\x03\x04\x03\x04\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03" + - "\x05\x03\x05\x03\x05\x03\x05\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03" + - "\x06\x03\x06\x03\x07\x03\x07\x03\x07\x03\x07\x03\x07\x03\x07\x03\b\x03" + - "\b\x03\b\x03\b\x03\b\x03\b\x03\b\x03\b\x03\t\x03\t\x03\t\x03\t\x03\t\x03" + - "\t\x03\t\x03\t\x03\n\x03\n\x03\n\x03\n\x03\n\x03\n\x03\n\x03\v\x03\v\x03" + - "\v\x03\v\x03\v\x03\v\x03\v\x03\v\x03\v\x03\v\x03\v\x03\v\x03\f\x03\f\x03" + - "\f\x03\f\x03\f\x03\f\x03\f\x03\f\x03\r\x03\r\x03\r\x03\r\x03\r\x03\r\x03" + - "\r\x03\r\x03\r\x03\r\x03\x0E\x03\x0E\x03\x0E\x03\x0E\x03\x0E\x03\x0E\x03" + - "\x0E\x03\x0F\x03\x0F\x03\x0F\x03\x0F\x03\x0F\x03\x0F\x03\x0F\x03\x0F\x03" + - "\x0F\x03\x10\x03\x10\x03\x10\x03\x10\x03\x10\x03\x10\x03\x10\x03\x11\x03" + - "\x11\x03\x11\x03\x11\x03\x11\x03\x11\x03\x11\x03\x11\x03\x11\x03\x12\x03" + - "\x12\x03\x12\x03\x12\x03\x12\x03\x12\x03\x12\x03\x13\x03\x13\x03\x13\x03" + - "\x13\x07\x13\u018F\n\x13\f\x13\x0E\x13\u0192\v\x13\x03\x13\x05\x13\u0195" + - "\n\x13\x03\x13\x05\x13\u0198\n\x13\x03\x13\x03\x13\x03\x14\x03\x14\x03" + - "\x14\x03\x14\x03\x14\x07\x14\u01A1\n\x14\f\x14\x0E\x14\u01A4\v\x14\x03" + - "\x14\x03\x14\x03\x14\x03\x14\x03\x14\x03\x15\x06\x15\u01AC\n\x15\r\x15" + - "\x0E\x15\u01AD\x03\x15\x03\x15\x03\x16\x03\x16\x03\x16\x03\x16\x03\x16" + - "\x03\x17\x03\x17\x03\x17\x03\x17\x03\x17\x03\x18\x03\x18\x03\x18\x03\x18" + - "\x03\x19\x03\x19\x03\x19\x03\x19\x03\x1A\x03\x1A\x03\x1A\x03\x1A\x03\x1B" + - "\x03\x1B\x03\x1B\x03\x1B\x03\x1C\x03\x1C\x03\x1D\x03\x1D\x03\x1E\x03\x1E" + - "\x03\x1E\x03\x1F\x03\x1F\x03 \x03 \x05 \u01D7\n \x03 \x06 \u01DA\n \r" + - " \x0E \u01DB\x03!\x03!\x03!\x07!\u01E1\n!\f!\x0E!\u01E4\v!\x03!\x03!\x03" + - "!\x03!\x03!\x03!\x07!\u01EC\n!\f!\x0E!\u01EF\v!\x03!\x03!\x03!\x03!\x03" + - "!\x05!\u01F6\n!\x03!\x05!\u01F9\n!\x05!\u01FB\n!\x03\"\x06\"\u01FE\n\"" + - "\r\"\x0E\"\u01FF\x03#\x06#\u0203\n#\r#\x0E#\u0204\x03#\x03#\x07#\u0209" + - "\n#\f#\x0E#\u020C\v#\x03#\x03#\x06#\u0210\n#\r#\x0E#\u0211\x03#\x06#\u0215" + - "\n#\r#\x0E#\u0216\x03#\x03#\x07#\u021B\n#\f#\x0E#\u021E\v#\x05#\u0220" + - "\n#\x03#\x03#\x03#\x03#\x06#\u0226\n#\r#\x0E#\u0227\x03#\x03#\x05#\u022C" + - "\n#\x03$\x03$\x03$\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03" + - "%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03" + - "%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03" + - "%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03" + - "%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03" + - "%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03" + - "%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x03%\x05" + - "%\u028F\n%\x03&\x03&\x03&\x03&\x03\'\x03\'\x03(\x03(\x03)\x03)\x03*\x03" + - "*\x03+\x03+\x03+\x03+\x03+\x03,\x03,\x03,\x03,\x03,\x03-\x03-\x03-\x03" + - "-\x03.\x03.\x03.\x03.\x03.\x03/\x03/\x03/\x03/\x03/\x03/\x030\x030\x03" + - "0\x031\x031\x031\x032\x032\x032\x033\x033\x033\x033\x033\x034\x034\x03" + - "4\x035\x035\x036\x036\x037\x037\x037\x037\x037\x038\x038\x038\x038\x03" + - "8\x038\x038\x038\x038\x038\x039\x039\x039\x039\x039\x039\x039\x039\x03" + - "9\x059\u02E3\n9\x03:\x03:\x03:\x03:\x03:\x03:\x03:\x03:\x03:\x03:\x05" + - ":\u02EF\n:\x03;\x03;\x03<\x03<\x03=\x03=\x03>\x03>\x03?\x03?\x03@\x03" + - "@\x03@\x03A\x03A\x03A\x03A\x03A\x03A\x03A\x05A\u0305\nA\x03B\x03B\x03" + - "B\x03B\x03B\x03B\x03C\x03C\x03C\x03C\x03C\x03C\x03C\x03C\x03C\x05C\u0316" + - "\nC\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03" + - "D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03" + - "D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03" + - "D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03" + - "D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03" + - "D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03" + - "D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03" + - "D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03" + - "D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03" + - "D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03" + - "D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03" + - "D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03" + - "D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03" + - "D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03" + - "D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03" + - "D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03" + - "D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03" + - "D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03" + - "D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03" + - "D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03" + - "D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03" + - "D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03" + - "D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03" + - "D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03" + - "D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03" + - "D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03" + - "D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03" + - "D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03" + - "D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03" + - "D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03" + - "D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x03D\x05D\u04C6\nD\x03" + - "E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03" + - "E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03" + - "E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03" + - "E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03" + - "E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03" + - "E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03" + - "E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03" + - "E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03" + - "E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03" + - "E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03" + - "E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x03E\x05E\u055F\nE\x03" + - "F\x03F\x03F\x03F\x03F\x03F\x03F\x03F\x03F\x03F\x03F\x03G\x03G\x03G\x03" + - "G\x03G\x07G\u0571\nG\fG\x0EG\u0574\vG\x03G\x03G\x03G\x03G\x03G\x06G\u057B" + - "\nG\rG\x0EG\u057C\x05G\u057F\nG\x03H\x03H\x03H\x03H\x07H\u0585\nH\fH\x0E" + - "H\u0588\vH\x03H\x03H\x03I\x03I\x03I\x03I\x03J\x03J\x03J\x03J\x03K\x03" + - "K\x03K\x03K\x03L\x03L\x03L\x03L\x03L\x03M\x03M\x03M\x03M\x03M\x03M\x03" + - "N\x03N\x03N\x03N\x03N\x03N\x03O\x03O\x03O\x03O\x03P\x03P\x03P\x03P\x03" + - "Q\x03Q\x03Q\x03Q\x03Q\x03Q\x03Q\x03Q\x03Q\x03R\x06R\u05BB\nR\rR\x0ER\u05BC" + - "\x03S\x06S\u05C0\nS\rS\x0ES\u05C1\x03S\x03S\x05S\u05C6\nS\x03T\x03T\x03" + - "U\x03U\x03U\x03U\x03V\x03V\x03V\x03V\x03W\x03W\x03W\x03W\x03X\x03X\x03" + - "X\x03Y\x03Y\x03Y\x03Y\x03Y\x03Z\x03Z\x03Z\x03Z\x03Z\x03[\x03[\x03[\x03" + - "[\x03[\x03[\x03\\\x03\\\x03\\\x03\\\x03]\x03]\x03]\x03]\x03^\x06^\u05F2" + - "\n^\r^\x0E^\u05F3\x03_\x06_\u05F7\n_\r_\x0E_\u05F8\x03_\x03_\x05_\u05FD" + - "\n_\x03`\x03`\x03a\x03a\x03a\x03a\x03b\x03b\x03b\x03b\x03c\x03c\x03c\x03" + - "c\x03d\x03d\x03e\x03e\x03f\x03f\x03g\x03g\x03h\x03h\x03i\x03i\x03j\x03" + - "j\x03k\x03k\x03l\x03l\x03m\x03m\x03n\x03n\x03o\x03o\x03p\x03p\x03q\x03" + - "q\x03r\x03r\x03s\x03s\x03t\x03t\x03u\x03u\x03v\x03v\x03w\x03w\x03x\x03" + - "x\x03y\x03y\x03z\x03z\x03{\x03{\x03|\x03|\x03}\x03}\x04\u01A2\u01ED\x02" + - "\x02~\x07\x02\x03\t\x02\x04\v\x02\x05\r\x02\x06\x0F\x02\x07\x11\x02\b" + - "\x13\x02\t\x15\x02\n\x17\x02\v\x19\x02\f\x1B\x02\r\x1D\x02\x0E\x1F\x02" + - "\x0F!\x02\x10#\x02\x11%\x02\x12\'\x02\x13)\x02\x14+\x02\x15-\x02\x16/" + - "\x02\x021\x02S3\x02\x175\x02\x187\x02\x199\x02\x1A;\x02\x02=\x02\x02?" + - "\x02\x02A\x02\x02C\x02\x02E\x02\x1BG\x02\x1CI\x02\x1DK\x02\x1EM\x02\x1F" + - "O\x02 Q\x02!S\x02\"U\x02#W\x02$Y\x02%[\x02&]\x02\'_\x02(a\x02)c\x02*e" + - "\x02+g\x02,i\x02-k\x02.m\x02/o\x020q\x021s\x022u\x023w\x024y\x025{\x02" + - "6}\x027\x7F\x028\x81\x029\x83\x02:\x85\x02;\x87\x02<\x89\x02=\x8B\x02" + - ">\x8D\x02?\x8F\x02@\x91\x02A\x93\x02B\x95\x02C\x97\x02D\x99\x02E\x9B\x02" + - "\x02\x9D\x02\x02\x9F\x02\x02\xA1\x02\x02\xA3\x02\x02\xA5\x02F\xA7\x02" + - "G\xA9\x02\x02\xAB\x02H\xAD\x02I\xAF\x02J\xB1\x02K\xB3\x02L\xB5\x02M\xB7" + - "\x02\x02\xB9\x02\x02\xBB\x02\x02\xBD\x02\x02\xBF\x02N\xC1\x02\x02\xC3" + - "\x02O\xC5\x02P\xC7\x02Q\xC9\x02R\xCB\x02\x02\xCD\x02\x02\xCF\x02\x02\xD1" + - "\x02\x02\xD3\x02\x02\xD5\x02\x02\xD7\x02\x02\xD9\x02\x02\xDB\x02\x02\xDD" + - "\x02\x02\xDF\x02\x02\xE1\x02\x02\xE3\x02\x02\xE5\x02\x02\xE7\x02\x02\xE9" + - "\x02\x02\xEB\x02\x02\xED\x02\x02\xEF\x02\x02\xF1\x02\x02\xF3\x02\x02\xF5" + - "\x02\x02\xF7\x02\x02\xF9\x02\x02\xFB\x02\x02\xFD\x02\x02\x07\x02\x03\x04" + - "\x05\x06\'\x04\x02\f\f\x0F\x0F\x05\x02\v\f\x0F\x0F\"\"\x03\x022;\x04\x02" + - "C\\c|\x07\x02$$^^ppttvv\x06\x02\f\f\x0F\x0F$$^^\x04\x02GGgg\x04\x02--" + - "//\x04\x02BBaa\x03\x02bb\f\x02\v\f\x0F\x0F\"\"..11??]]__bb~~\x04\x02," + - ",11\x04\x02CCcc\x04\x02DDdd\x04\x02EEee\x04\x02FFff\x04\x02HHhh\x04\x02" + + "\x03\uC91D\uCABA\u058D\uAFBA\u4F53\u0607\uEA8B\uC241\x02N\u032D\b\x01" + + "\b\x01\b\x01\x04\x02\t\x02\x04\x03\t\x03\x04\x04\t\x04\x04\x05\t\x05\x04" + + "\x06\t\x06\x04\x07\t\x07\x04\b\t\b\x04\t\t\t\x04\n\t\n\x04\v\t\v\x04\f" + + "\t\f\x04\r\t\r\x04\x0E\t\x0E\x04\x0F\t\x0F\x04\x10\t\x10\x04\x11\t\x11" + + "\x04\x12\t\x12\x04\x13\t\x13\x04\x14\t\x14\x04\x15\t\x15\x04\x16\t\x16" + + "\x04\x17\t\x17\x04\x18\t\x18\x04\x19\t\x19\x04\x1A\t\x1A\x04\x1B\t\x1B" + + "\x04\x1C\t\x1C\x04\x1D\t\x1D\x04\x1E\t\x1E\x04\x1F\t\x1F\x04 \t \x04!" + + "\t!\x04\"\t\"\x04#\t#\x04$\t$\x04%\t%\x04&\t&\x04\'\t\'\x04(\t(\x04)\t" + + ")\x04*\t*\x04+\t+\x04,\t,\x04-\t-\x04.\t.\x04/\t/\x040\t0\x041\t1\x04" + + "2\t2\x043\t3\x044\t4\x045\t5\x046\t6\x047\t7\x048\t8\x049\t9\x04:\t:\x04" + + ";\t;\x04<\t<\x04=\t=\x04>\t>\x04?\t?\x04@\t@\x04A\tA\x04B\tB\x04C\tC\x04" + + "D\tD\x04E\tE\x04F\tF\x04G\tG\x04H\tH\x04I\tI\x04J\tJ\x04K\tK\x04L\tL\x04" + + "M\tM\x04N\tN\x04O\tO\x04P\tP\x04Q\tQ\x04R\tR\x04S\tS\x04T\tT\x04U\tU\x04" + + "V\tV\x04W\tW\x04X\tX\x04Y\tY\x04Z\tZ\x04[\t[\x04\\\t\\\x04]\t]\x04^\t" + + "^\x04_\t_\x04`\t`\x04a\ta\x04b\tb\x04c\tc\x04d\td\x04e\te\x04f\tf\x04" + + "g\tg\x04h\th\x04i\ti\x04j\tj\x04k\tk\x04l\tl\x04m\tm\x04n\tn\x04o\to\x04" + + "p\tp\x04q\tq\x04r\tr\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03\x02\x03" + + "\x02\x03\x02\x03\x02\x03\x02\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03" + + "\x03\x03\x03\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03\x04\x03" + + "\x04\x03\x04\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03" + + "\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x06\x03\x07\x03\x07\x03" + + "\x07\x03\x07\x03\x07\x03\x07\x03\x07\x03\b\x03\b\x03\b\x03\b\x03\b\x03" + + "\b\x03\b\x03\t\x03\t\x03\t\x03\t\x03\t\x03\t\x03\t\x03\t\x03\n\x03\n\x03" + + "\n\x03\n\x03\n\x03\n\x03\n\x03\n\x03\n\x03\n\x03\n\x03\n\x03\v\x03\v\x03" + + "\v\x03\v\x03\v\x03\v\x03\v\x03\v\x03\v\x03\v\x03\f\x03\f\x03\f\x03\f\x03" + + "\f\x03\f\x03\f\x03\f\x03\f\x03\r\x03\r\x03\r\x03\r\x03\r\x03\r\x03\x0E" + + "\x03\x0E\x03\x0E\x03\x0E\x03\x0E\x03\x0E\x03\x0E\x03\x0F\x03\x0F\x03\x0F" + + "\x03\x0F\x03\x0F\x03\x0F\x03\x0F\x03\x10\x03\x10\x03\x10\x03\x10\x03\x10" + + "\x03\x10\x03\x10\x03\x10\x03\x11\x03\x11\x03\x11\x03\x11\x03\x11\x03\x11" + + "\x03\x11\x03\x11\x03\x12\x06\x12\u016A\n\x12\r\x12\x0E\x12\u016B\x03\x12" + + "\x03\x12\x03\x13\x03\x13\x03\x13\x03\x13\x07\x13\u0174\n\x13\f\x13\x0E" + + "\x13\u0177\v\x13\x03\x13\x05\x13\u017A\n\x13\x03\x13\x05\x13\u017D\n\x13" + + "\x03\x13\x03\x13\x03\x14\x03\x14\x03\x14\x03\x14\x03\x14\x07\x14\u0186" + + "\n\x14\f\x14\x0E\x14\u0189\v\x14\x03\x14\x03\x14\x03\x14\x03\x14\x03\x14" + + "\x03\x15\x06\x15\u0191\n\x15\r\x15\x0E\x15\u0192\x03\x15\x03\x15\x03\x16" + + "\x03\x16\x03\x16\x03\x16\x03\x17\x03\x17\x03\x18\x03\x18\x03\x19\x03\x19" + + "\x03\x19\x03\x1A\x03\x1A\x03\x1B\x03\x1B\x05\x1B\u01A6\n\x1B\x03\x1B\x06" + + "\x1B\u01A9\n\x1B\r\x1B\x0E\x1B\u01AA\x03\x1C\x03\x1C\x03\x1C\x07\x1C\u01B0" + + "\n\x1C\f\x1C\x0E\x1C\u01B3\v\x1C\x03\x1C\x03\x1C\x03\x1C\x03\x1C\x03\x1C" + + "\x03\x1C\x07\x1C\u01BB\n\x1C\f\x1C\x0E\x1C\u01BE\v\x1C\x03\x1C\x03\x1C" + + "\x03\x1C\x03\x1C\x03\x1C\x05\x1C\u01C5\n\x1C\x03\x1C\x05\x1C\u01C8\n\x1C" + + "\x05\x1C\u01CA\n\x1C\x03\x1D\x06\x1D\u01CD\n\x1D\r\x1D\x0E\x1D\u01CE\x03" + + "\x1E\x06\x1E\u01D2\n\x1E\r\x1E\x0E\x1E\u01D3\x03\x1E\x03\x1E\x07\x1E\u01D8" + + "\n\x1E\f\x1E\x0E\x1E\u01DB\v\x1E\x03\x1E\x03\x1E\x06\x1E\u01DF\n\x1E\r" + + "\x1E\x0E\x1E\u01E0\x03\x1E\x06\x1E\u01E4\n\x1E\r\x1E\x0E\x1E\u01E5\x03" + + "\x1E\x03\x1E\x07\x1E\u01EA\n\x1E\f\x1E\x0E\x1E\u01ED\v\x1E\x05\x1E\u01EF" + + "\n\x1E\x03\x1E\x03\x1E\x03\x1E\x03\x1E\x06\x1E\u01F5\n\x1E\r\x1E\x0E\x1E" + + "\u01F6\x03\x1E\x03\x1E\x05\x1E\u01FB\n\x1E\x03\x1F\x03\x1F\x03\x1F\x03" + + " \x03 \x03 \x03 \x03!\x03!\x03!\x03!\x03\"\x03\"\x03#\x03#\x03$\x03$\x03" + + "$\x03$\x03$\x03%\x03%\x03&\x03&\x03&\x03&\x03&\x03&\x03\'\x03\'\x03\'" + + "\x03\'\x03\'\x03\'\x03(\x03(\x03(\x03(\x03(\x03)\x03)\x03*\x03*\x03*\x03" + + "+\x03+\x03+\x03,\x03,\x03,\x03,\x03,\x03-\x03-\x03-\x03-\x03.\x03.\x03" + + ".\x03.\x03.\x03/\x03/\x03/\x03/\x03/\x03/\x030\x030\x030\x031\x031\x03" + + "2\x032\x032\x032\x032\x032\x033\x033\x034\x034\x034\x034\x034\x035\x03" + + "5\x035\x035\x035\x036\x036\x036\x036\x036\x036\x036\x036\x036\x036\x03" + + "7\x037\x038\x038\x038\x039\x039\x039\x03:\x03:\x03;\x03;\x03;\x03<\x03" + + "<\x03=\x03=\x03=\x03>\x03>\x03?\x03?\x03@\x03@\x03A\x03A\x03B\x03B\x03" + + "C\x03C\x03C\x03C\x03C\x03D\x03D\x03D\x03D\x03D\x03E\x03E\x03E\x03E\x07" + + "E\u028B\nE\fE\x0EE\u028E\vE\x03E\x03E\x03E\x03E\x06E\u0294\nE\rE\x0EE" + + "\u0295\x05E\u0298\nE\x03F\x03F\x03F\x03F\x07F\u029E\nF\fF\x0EF\u02A1\v" + + "F\x03F\x03F\x03G\x03G\x03G\x03G\x03H\x03H\x03H\x03H\x03I\x03I\x03I\x03" + + "I\x03J\x03J\x03J\x03J\x03J\x03K\x03K\x03K\x03K\x03K\x03K\x03L\x03L\x03" + + "L\x03L\x03L\x03L\x03M\x03M\x03M\x03M\x03N\x03N\x03N\x03N\x03O\x03O\x03" + + "O\x03P\x03P\x03P\x03P\x03P\x03P\x03P\x03P\x03P\x03Q\x03Q\x03Q\x03R\x03" + + "R\x03R\x03R\x03R\x03S\x06S\u02DF\nS\rS\x0ES\u02E0\x03T\x06T\u02E4\nT\r" + + "T\x0ET\u02E5\x03T\x03T\x05T\u02EA\nT\x03U\x03U\x03V\x03V\x03V\x03V\x03" + + "W\x03W\x03W\x03W\x03X\x03X\x03X\x03X\x03Y\x03Y\x03Z\x03Z\x03[\x03[\x03" + + "\\\x03\\\x03]\x03]\x03^\x03^\x03_\x03_\x03`\x03`\x03a\x03a\x03b\x03b\x03" + + "c\x03c\x03d\x03d\x03e\x03e\x03f\x03f\x03g\x03g\x03h\x03h\x03i\x03i\x03" + + "j\x03j\x03k\x03k\x03l\x03l\x03m\x03m\x03n\x03n\x03o\x03o\x03p\x03p\x03" + + "q\x03q\x03r\x03r\x04\u0187\u01BC\x02\x02s\x05\x02\x03\x07\x02\x04\t\x02" + + "\x05\v\x02\x06\r\x02\x07\x0F\x02\b\x11\x02\t\x13\x02\n\x15\x02\v\x17\x02" + + "\f\x19\x02\r\x1B\x02\x0E\x1D\x02\x0F\x1F\x02\x10!\x02\x11#\x02\x12%\x02" + + "\x13\'\x02\x14)\x02\x15+\x02\x16-\x02\x17/\x02\x021\x02\x023\x02\x025" + + "\x02\x027\x02\x029\x02\x18;\x02\x19=\x02\x1A?\x02\x1BA\x02\x1CC\x02\x1D" + + "E\x02\x1EG\x02\x1FI\x02 K\x02!M\x02\"O\x02#Q\x02$S\x02%U\x02&W\x02\'Y" + + "\x02([\x02)]\x02*_\x02+a\x02,c\x02-e\x02.g\x02/i\x020k\x021m\x022o\x02" + + "3q\x024s\x025u\x026w\x027y\x028{\x029}\x02:\x7F\x02;\x81\x02<\x83\x02" + + "=\x85\x02>\x87\x02?\x89\x02@\x8B\x02A\x8D\x02B\x8F\x02C\x91\x02D\x93\x02" + + "E\x95\x02\x02\x97\x02\x02\x99\x02\x02\x9B\x02\x02\x9D\x02\x02\x9F\x02" + + "F\xA1\x02G\xA3\x02H\xA5\x02I\xA7\x02J\xA9\x02\x02\xAB\x02K\xAD\x02L\xAF" + + "\x02M\xB1\x02N\xB3\x02\x02\xB5\x02\x02\xB7\x02\x02\xB9\x02\x02\xBB\x02" + + "\x02\xBD\x02\x02\xBF\x02\x02\xC1\x02\x02\xC3\x02\x02\xC5\x02\x02\xC7\x02" + + "\x02\xC9\x02\x02\xCB\x02\x02\xCD\x02\x02\xCF\x02\x02\xD1\x02\x02\xD3\x02" + + "\x02\xD5\x02\x02\xD7\x02\x02\xD9\x02\x02\xDB\x02\x02\xDD\x02\x02\xDF\x02" + + "\x02\xE1\x02\x02\xE3\x02\x02\xE5\x02\x02\x05\x02\x03\x04(\b\x02\v\f\x0F" + + "\x0F\"\"11]]__\x04\x02\f\f\x0F\x0F\x05\x02\v\f\x0F\x0F\"\"\x03\x022;\x04" + + "\x02C\\c|\x07\x02$$^^ppttvv\x06\x02\f\f\x0F\x0F$$^^\x04\x02GGgg\x04\x02" + + "--//\x04\x02BBaa\x03\x02bb\f\x02\v\f\x0F\x0F\"\"..11??]]__bb~~\x04\x02" + + ",,11\x04\x02CCcc\x04\x02DDdd\x04\x02EEee\x04\x02FFff\x04\x02HHhh\x04\x02" + "IIii\x04\x02JJjj\x04\x02KKkk\x04\x02LLll\x04\x02MMmm\x04\x02NNnn\x04\x02" + "OOoo\x04\x02PPpp\x04\x02QQqq\x04\x02RRrr\x04\x02SSss\x04\x02TTtt\x04\x02" + "UUuu\x04\x02VVvv\x04\x02WWww\x04\x02XXxx\x04\x02YYyy\x04\x02ZZzz\x04\x02" + - "[[{{\x04\x02\\\\||\x02\u06A4\x02\x07\x03\x02\x02\x02\x02\t\x03\x02\x02" + - "\x02\x02\v\x03\x02\x02\x02\x02\r\x03\x02\x02\x02\x02\x0F\x03\x02\x02\x02" + - "\x02\x11\x03\x02\x02\x02\x02\x13\x03\x02\x02\x02\x02\x15\x03\x02\x02\x02" + - "\x02\x17\x03\x02\x02\x02\x02\x19\x03\x02\x02\x02\x02\x1B\x03\x02\x02\x02" + - "\x02\x1D\x03\x02\x02\x02\x02\x1F\x03\x02\x02\x02\x02!\x03\x02\x02\x02" + - "\x02#\x03\x02\x02\x02\x02%\x03\x02\x02\x02\x02\'\x03\x02\x02\x02\x02)" + - "\x03\x02\x02\x02\x02+\x03\x02\x02\x02\x02-\x03\x02\x02\x02\x03/\x03\x02" + - "\x02\x02\x031\x03\x02\x02\x02\x033\x03\x02\x02\x02\x035\x03\x02\x02\x02" + - "\x037\x03\x02\x02\x02\x049\x03\x02\x02\x02\x04E\x03\x02\x02\x02\x04G\x03" + - "\x02\x02\x02\x04I\x03\x02\x02\x02\x04K\x03\x02\x02\x02\x04M\x03\x02\x02" + - "\x02\x04O\x03\x02\x02\x02\x04Q\x03\x02\x02\x02\x04S\x03\x02\x02\x02\x04" + - "U\x03\x02\x02\x02\x04W\x03\x02\x02\x02\x04Y\x03\x02\x02\x02\x04[\x03\x02" + - "\x02\x02\x04]\x03\x02\x02\x02\x04_\x03\x02\x02\x02\x04a\x03\x02\x02\x02" + - "\x04c\x03\x02\x02\x02\x04e\x03\x02\x02\x02\x04g\x03\x02\x02\x02\x04i\x03" + - "\x02\x02\x02\x04k\x03\x02\x02\x02\x04m\x03\x02\x02\x02\x04o\x03\x02\x02" + - "\x02\x04q\x03\x02\x02\x02\x04s\x03\x02\x02\x02\x04u\x03\x02\x02\x02\x04" + - "w\x03\x02\x02\x02\x04y\x03\x02\x02\x02\x04{\x03\x02\x02\x02\x04}\x03\x02" + - "\x02\x02\x04\x7F\x03\x02\x02\x02\x04\x81\x03\x02\x02\x02\x04\x83\x03\x02" + - "\x02\x02\x04\x85\x03\x02\x02\x02\x04\x87\x03\x02\x02\x02\x04\x89\x03\x02" + - "\x02\x02\x04\x8B\x03\x02\x02\x02\x04\x8D\x03\x02\x02\x02\x04\x8F\x03\x02" + - "\x02\x02\x04\x91\x03\x02\x02\x02\x04\x93\x03\x02\x02\x02\x04\x95\x03\x02" + - "\x02\x02\x04\x97\x03\x02\x02\x02\x04\x99\x03\x02\x02\x02\x05\x9B\x03\x02" + - "\x02\x02\x05\x9D\x03\x02\x02\x02\x05\x9F\x03\x02\x02\x02\x05\xA1\x03\x02" + - "\x02\x02\x05\xA3\x03\x02\x02\x02\x05\xA5\x03\x02\x02\x02\x05\xA7\x03\x02" + - "\x02\x02\x05\xAB\x03\x02\x02\x02\x05\xAD\x03\x02\x02\x02\x05\xAF\x03\x02" + - "\x02\x02\x05\xB1\x03\x02\x02\x02\x06\xB3\x03\x02\x02\x02\x06\xB5\x03\x02" + - "\x02\x02\x06\xB7\x03\x02\x02\x02\x06\xB9\x03\x02\x02\x02\x06\xBB\x03\x02" + - "\x02\x02\x06\xBD\x03\x02\x02\x02\x06\xBF\x03\x02\x02\x02\x06\xC3\x03\x02" + - "\x02\x02\x06\xC5\x03\x02\x02\x02\x06\xC7\x03\x02\x02\x02\x06\xC9\x03\x02" + - "\x02\x02\x07\xFF\x03\x02\x02\x02\t\u0109\x03\x02\x02\x02\v\u0110\x03\x02" + - "\x02\x02\r\u0117\x03\x02\x02\x02\x0F\u0121\x03\x02\x02\x02\x11\u0128\x03" + - "\x02\x02\x02\x13\u012E\x03\x02\x02\x02\x15\u0136\x03\x02\x02\x02\x17\u013E" + - "\x03\x02\x02\x02\x19\u0145\x03\x02\x02\x02\x1B\u0151\x03\x02\x02\x02\x1D" + - "\u0159\x03\x02\x02\x02\x1F\u0163\x03\x02\x02\x02!\u016A\x03\x02\x02\x02" + - "#\u0173\x03\x02\x02\x02%\u017A\x03\x02\x02\x02\'\u0183\x03\x02\x02\x02" + - ")\u018A\x03\x02\x02\x02+\u019B\x03\x02\x02\x02-\u01AB\x03\x02\x02\x02" + - "/\u01B1\x03\x02\x02\x021\u01B6\x03\x02\x02\x023\u01BB\x03\x02\x02\x02" + - "5\u01BF\x03\x02\x02\x027\u01C3\x03\x02\x02\x029\u01C7\x03\x02\x02\x02" + - ";\u01CB\x03\x02\x02\x02=\u01CD\x03\x02\x02\x02?\u01CF\x03\x02\x02\x02" + - "A\u01D2\x03\x02\x02\x02C\u01D4\x03\x02\x02\x02E\u01FA\x03\x02\x02\x02" + - "G\u01FD\x03\x02\x02\x02I\u022B\x03\x02\x02\x02K\u022D\x03\x02\x02\x02" + - "M\u028E\x03\x02\x02\x02O\u0290\x03\x02\x02\x02Q\u0294\x03\x02\x02\x02" + - "S\u0296\x03\x02\x02\x02U\u0298\x03\x02\x02\x02W\u029A\x03\x02\x02\x02" + - "Y\u029C\x03\x02\x02\x02[\u02A1\x03\x02\x02\x02]\u02A6\x03\x02\x02\x02" + - "_\u02AA\x03\x02\x02\x02a\u02AF\x03\x02\x02\x02c\u02B5\x03\x02\x02\x02" + - "e\u02B8\x03\x02\x02\x02g\u02BB\x03\x02\x02\x02i\u02BE\x03\x02\x02\x02" + - "k\u02C3\x03\x02\x02\x02m\u02C6\x03\x02\x02\x02o\u02C8\x03\x02\x02\x02" + - "q\u02CA\x03\x02\x02\x02s\u02CF\x03\x02\x02\x02u\u02E2\x03\x02\x02\x02" + - "w\u02EE\x03\x02\x02\x02y\u02F0\x03\x02\x02\x02{\u02F2\x03\x02\x02\x02" + - "}\u02F4\x03\x02\x02\x02\x7F\u02F6\x03\x02\x02\x02\x81\u02F8\x03\x02\x02" + - "\x02\x83\u02FA\x03\x02\x02\x02\x85\u0304\x03\x02\x02\x02\x87\u0306\x03" + - "\x02\x02\x02\x89\u0315\x03\x02\x02\x02\x8B\u04C5\x03\x02\x02\x02\x8D\u055E" + - "\x03\x02\x02\x02\x8F\u0560\x03\x02\x02\x02\x91\u057E\x03\x02\x02\x02\x93" + - "\u0580\x03\x02\x02\x02\x95\u058B\x03\x02\x02\x02\x97\u058F\x03\x02\x02" + - "\x02\x99\u0593\x03\x02\x02\x02\x9B\u0597\x03\x02\x02\x02\x9D\u059C\x03" + - "\x02\x02\x02\x9F\u05A2\x03\x02\x02\x02\xA1\u05A8\x03\x02\x02\x02\xA3\u05AC" + - "\x03\x02\x02\x02\xA5\u05B0\x03\x02\x02\x02\xA7\u05BA\x03\x02\x02\x02\xA9" + - "\u05C5\x03\x02\x02\x02\xAB\u05C7\x03\x02\x02\x02\xAD\u05C9\x03\x02\x02" + - "\x02\xAF\u05CD\x03\x02\x02\x02\xB1\u05D1\x03\x02\x02\x02\xB3\u05D5\x03" + - "\x02\x02\x02\xB5\u05D8\x03\x02\x02\x02\xB7\u05DD\x03\x02\x02\x02\xB9\u05E2" + - "\x03\x02\x02\x02\xBB\u05E8\x03\x02\x02\x02\xBD\u05EC\x03\x02\x02\x02\xBF" + - "\u05F1\x03"; + "[[{{\x04\x02\\\\||\x02\u0330\x02\x05\x03\x02\x02\x02\x02\x07\x03\x02\x02" + + "\x02\x02\t\x03\x02\x02\x02\x02\v\x03\x02\x02\x02\x02\r\x03\x02\x02\x02" + + "\x02\x0F\x03\x02\x02\x02\x02\x11\x03\x02\x02\x02\x02\x13\x03\x02\x02\x02" + + "\x02\x15\x03\x02\x02\x02\x02\x17\x03\x02\x02\x02\x02\x19\x03\x02\x02\x02" + + "\x02\x1B\x03\x02\x02\x02\x02\x1D\x03\x02\x02\x02\x02\x1F\x03\x02\x02\x02" + + "\x02!\x03\x02\x02\x02\x02#\x03\x02\x02\x02\x02%\x03\x02\x02\x02\x02\'" + + "\x03\x02\x02\x02\x02)\x03\x02\x02\x02\x02+\x03\x02\x02\x02\x03-\x03\x02" + + "\x02\x02\x039\x03\x02\x02\x02\x03;\x03\x02\x02\x02\x03=\x03\x02\x02\x02" + + "\x03?\x03\x02\x02\x02\x03A\x03\x02\x02\x02\x03C\x03\x02\x02\x02\x03E\x03" + + "\x02\x02\x02\x03G\x03\x02\x02\x02\x03I\x03\x02\x02\x02\x03K\x03\x02\x02" + + "\x02\x03M\x03\x02\x02\x02\x03O\x03\x02\x02\x02\x03Q\x03\x02\x02\x02\x03" + + "S\x03\x02\x02\x02\x03U\x03\x02\x02\x02\x03W\x03\x02\x02\x02\x03Y\x03\x02" + + "\x02\x02\x03[\x03\x02\x02\x02\x03]\x03\x02\x02\x02\x03_\x03\x02\x02\x02" + + "\x03a\x03\x02\x02\x02\x03c\x03\x02\x02\x02\x03e\x03\x02\x02\x02\x03g\x03" + + "\x02\x02\x02\x03i\x03\x02\x02\x02\x03k\x03\x02\x02\x02\x03m\x03\x02\x02" + + "\x02\x03o\x03\x02\x02\x02\x03q\x03\x02\x02\x02\x03s\x03\x02\x02\x02\x03" + + "u\x03\x02\x02\x02\x03w\x03\x02\x02\x02\x03y\x03\x02\x02\x02\x03{\x03\x02" + + "\x02\x02\x03}\x03\x02\x02\x02\x03\x7F\x03\x02\x02\x02\x03\x81\x03\x02" + + "\x02\x02\x03\x83\x03\x02\x02\x02\x03\x85\x03\x02\x02\x02\x03\x87\x03\x02" + + "\x02\x02\x03\x89\x03\x02\x02\x02\x03\x8B\x03\x02\x02\x02\x03\x8D\x03\x02" + + "\x02\x02\x03\x8F\x03\x02\x02\x02\x03\x91\x03\x02\x02\x02\x03\x93\x03\x02" + + "\x02\x02\x04\x95\x03\x02\x02\x02\x04\x97\x03\x02\x02\x02\x04\x99\x03\x02" + + "\x02\x02\x04\x9B\x03\x02\x02\x02\x04\x9D\x03\x02\x02\x02\x04\x9F\x03\x02" + + "\x02\x02\x04\xA1\x03\x02\x02\x02\x04\xA3\x03\x02\x02\x02\x04\xA5\x03\x02" + + "\x02\x02\x04\xA7\x03\x02\x02\x02\x04\xAB\x03\x02\x02\x02\x04\xAD\x03\x02" + + "\x02\x02\x04\xAF\x03\x02\x02\x02\x04\xB1\x03\x02\x02\x02\x05\xE7\x03\x02" + + "\x02\x02\x07\xF1\x03\x02\x02\x02\t\xF8\x03\x02\x02\x02\v\u0101\x03\x02" + + "\x02\x02\r\u0108\x03\x02\x02\x02\x0F\u010F\x03\x02\x02\x02\x11\u0116\x03" + + "\x02\x02\x02\x13\u011D\x03\x02\x02\x02\x15\u0125\x03\x02\x02\x02\x17\u0131" + + "\x03\x02\x02\x02\x19\u013B\x03\x02\x02\x02\x1B\u0144\x03\x02\x02\x02\x1D" + + "\u014A\x03\x02\x02\x02\x1F\u0151\x03\x02\x02\x02!\u0158\x03\x02\x02\x02" + + "#\u0160\x03\x02\x02\x02%\u0169\x03\x02\x02\x02\'\u016F\x03\x02\x02\x02" + + ")\u0180\x03\x02\x02\x02+\u0190\x03\x02\x02\x02-\u0196\x03\x02\x02\x02" + + "/\u019A\x03\x02\x02\x021\u019C\x03\x02\x02\x023\u019E\x03\x02\x02\x02" + + "5\u01A1\x03\x02\x02\x027\u01A3\x03\x02\x02\x029\u01C9\x03\x02\x02\x02" + + ";\u01CC\x03\x02\x02\x02=\u01FA\x03\x02\x02\x02?\u01FC\x03\x02\x02\x02" + + "A\u01FF\x03\x02\x02\x02C\u0203\x03\x02\x02\x02E\u0207\x03\x02\x02\x02" + + "G\u0209\x03\x02\x02\x02I\u020B\x03\x02\x02\x02K\u0210\x03\x02\x02\x02" + + "M\u0212\x03\x02\x02\x02O\u0218\x03\x02\x02\x02Q\u021E\x03\x02\x02\x02" + + "S\u0223\x03\x02\x02\x02U\u0225\x03\x02\x02\x02W\u0228\x03\x02\x02\x02" + + "Y\u022B\x03\x02\x02\x02[\u0230\x03\x02\x02\x02]\u0234\x03\x02\x02\x02" + + "_\u0239\x03\x02\x02\x02a\u023F\x03\x02\x02\x02c\u0242\x03\x02\x02\x02" + + "e\u0244\x03\x02\x02\x02g\u024A\x03\x02\x02\x02i\u024C\x03\x02\x02\x02" + + "k\u0251\x03\x02\x02\x02m\u0256\x03\x02\x02\x02o\u0260\x03\x02\x02\x02" + + "q\u0262\x03\x02\x02\x02s\u0265\x03\x02\x02\x02u\u0268\x03\x02\x02\x02" + + "w\u026A\x03\x02\x02\x02y\u026D\x03\x02\x02\x02{\u026F\x03\x02\x02\x02" + + "}\u0272\x03\x02\x02\x02\x7F\u0274\x03\x02\x02\x02\x81\u0276\x03\x02\x02" + + "\x02\x83\u0278\x03\x02\x02\x02\x85\u027A\x03\x02\x02\x02\x87\u027C\x03" + + "\x02\x02\x02\x89\u0281\x03\x02\x02\x02\x8B\u0297\x03\x02\x02\x02\x8D\u0299" + + "\x03\x02\x02\x02\x8F\u02A4\x03\x02\x02\x02\x91\u02A8\x03\x02\x02\x02\x93" + + "\u02AC\x03\x02\x02\x02\x95\u02B0\x03\x02\x02\x02\x97\u02B5\x03\x02\x02" + + "\x02\x99\u02BB\x03\x02\x02\x02\x9B\u02C1\x03\x02\x02\x02\x9D\u02C5\x03" + + "\x02\x02\x02\x9F\u02C9\x03\x02\x02\x02\xA1\u02CC\x03\x02\x02\x02\xA3\u02D5" + + "\x03\x02\x02\x02\xA5\u02D8\x03\x02\x02\x02\xA7\u02DE\x03\x02\x02\x02\xA9" + + "\u02E9\x03\x02\x02\x02\xAB\u02EB\x03\x02\x02\x02\xAD\u02ED\x03\x02\x02" + + "\x02\xAF\u02F1\x03\x02\x02\x02\xB1\u02F5\x03\x02\x02\x02\xB3\u02F9\x03" + + "\x02\x02\x02\xB5\u02FB\x03\x02\x02\x02\xB7\u02FD\x03\x02\x02\x02\xB9\u02FF" + + "\x03\x02\x02\x02\xBB\u0301\x03\x02\x02\x02\xBD\u0303\x03\x02\x02\x02\xBF" + + "\u0305\x03\x02\x02\x02\xC1\u0307\x03\x02\x02\x02\xC3\u0309\x03\x02\x02" + + "\x02\xC5\u030B\x03\x02\x02\x02\xC7\u030D\x03\x02\x02\x02\xC9\u030F\x03" + + "\x02\x02\x02\xCB\u0311\x03\x02\x02\x02\xCD\u0313\x03\x02\x02\x02\xCF\u0315" + + "\x03\x02\x02\x02\xD1\u0317\x03\x02\x02\x02\xD3\u0319\x03\x02\x02\x02\xD5" + + "\u031B\x03\x02\x02\x02\xD7\u031D\x03\x02\x02\x02\xD9\u031F\x03\x02\x02" + + "\x02\xDB\u0321\x03\x02\x02\x02\xDD\u0323\x03\x02\x02\x02\xDF\u0325\x03" + + "\x02\x02\x02\xE1\u0327\x03\x02\x02\x02\xE3\u0329\x03\x02\x02\x02\xE5\u032B" + + "\x03\x02\x02\x02\xE7\xE8\x05\xB9\\\x02\xE8\xE9\x05\xC3a\x02\xE9\xEA\x05" + + "\xD7k\x02\xEA\xEB\x05\xD7k\x02\xEB\xEC\x05\xBB]\x02\xEC\xED\x05\xB7[\x02" + + "\xED\xEE\x05\xD9l\x02\xEE\xEF\x03\x02\x02\x02\xEF\xF0\b\x02\x02\x02\xF0" + + "\x06\x03\x02\x02\x02\xF1\xF2\x05\xB9\\\x02\xF2\xF3\x05\xD5j\x02\xF3\xF4" + + "\x05\xCFg\x02\xF4\xF5\x05\xD1h\x02\xF5\xF6\x03\x02\x02\x02\xF6\xF7\b\x03" + + "\x03\x02\xF7\b\x03\x02\x02\x02\xF8\xF9\x05\xBB]\x02\xF9\xFA\x05\xCDf\x02" + + "\xFA\xFB\x05\xD5j\x02\xFB\xFC\x05\xC3a\x02\xFC\xFD\x05\xB7[\x02\xFD\xFE" + + "\x05\xC1`\x02\xFE\xFF\x03\x02\x02\x02\xFF\u0100\b\x04\x03\x02\u0100\n" + + "\x03\x02\x02\x02\u0101\u0102\x05\xBB]\x02\u0102\u0103\x05\xDDn\x02\u0103" + + "\u0104\x05\xB3Y\x02\u0104\u0105\x05\xC9d\x02\u0105\u0106\x03\x02\x02\x02" + + "\u0106\u0107\b\x05\x02\x02\u0107\f\x03\x02\x02\x02\u0108\u0109\x05\xBD" + + "^\x02\u0109\u010A\x05\xD5j\x02\u010A\u010B\x05\xCFg\x02\u010B\u010C\x05" + + "\xCBe\x02\u010C\u010D\x03\x02\x02\x02\u010D\u010E\b\x06\x03\x02\u010E" + + "\x0E\x03\x02\x02\x02\u010F\u0110\x05\xBF_\x02\u0110\u0111\x05\xD5j\x02" + + "\u0111\u0112\x05\xCFg\x02\u0112\u0113\x05\xC7c\x02\u0113\u0114\x03\x02" + + "\x02\x02\u0114\u0115\b\x07\x02\x02\u0115\x10\x03\x02\x02\x02\u0116\u0117" + + "\x05\xC7c\x02\u0117\u0118\x05\xBB]\x02\u0118\u0119\x05\xBB]\x02\u0119" + + "\u011A\x05\xD1h\x02\u011A\u011B\x03\x02\x02\x02\u011B\u011C\b\b\x03\x02" + + "\u011C\x12\x03\x02\x02\x02\u011D\u011E\x05\xC9d\x02\u011E\u011F\x05\xC3" + + "a\x02\u011F\u0120\x05\xCBe\x02\u0120\u0121\x05\xC3a\x02\u0121\u0122\x05" + + "\xD9l\x02\u0122\u0123\x03\x02\x02\x02\u0123\u0124\b\t\x02\x02\u0124\x14" + + "\x03\x02\x02\x02\u0125\u0126\x05\xCBe\x02\u0126\u0127\x05\xDDn\x02\u0127" + + "\u0128\x05o7\x02\u0128\u0129\x05\xBB]\x02\u0129\u012A\x05\xE1p\x02\u012A" + + "\u012B\x05\xD1h\x02\u012B\u012C\x05\xB3Y\x02\u012C\u012D\x05\xCDf\x02" + + "\u012D\u012E\x05\xB9\\\x02\u012E\u012F\x03\x02\x02\x02\u012F\u0130\b\n" + + "\x03\x02\u0130\x16\x03\x02\x02\x02\u0131\u0132\x05\xD1h\x02\u0132\u0133" + + "\x05\xD5j\x02\u0133\u0134\x05\xCFg\x02\u0134\u0135\x05\xC5b\x02\u0135" + + "\u0136\x05\xBB]\x02\u0136\u0137\x05\xB7[\x02\u0137\u0138\x05\xD9l\x02" + + "\u0138\u0139\x03\x02\x02\x02\u0139\u013A\b\v\x03\x02\u013A\x18\x03\x02" + + "\x02\x02\u013B\u013C\x05\xD5j\x02\u013C\u013D\x05\xBB]\x02\u013D\u013E" + + "\x05\xCDf\x02\u013E\u013F\x05\xB3Y\x02\u013F\u0140\x05\xCBe\x02\u0140" + + "\u0141\x05\xBB]\x02\u0141\u0142\x03\x02\x02\x02\u0142\u0143\b\f\x03\x02" + + "\u0143\x1A\x03\x02\x02\x02\u0144\u0145\x05\xD5j\x02\u0145\u0146\x05\xCF" + + "g\x02\u0146\u0147\x05\xDFo\x02\u0147\u0148\x03\x02\x02\x02\u0148\u0149" + + "\b\r\x02\x02\u0149\x1C\x03\x02\x02\x02\u014A\u014B\x05\xD7k\x02\u014B" + + "\u014C\x05\xC1`\x02\u014C\u014D\x05\xCFg\x02\u014D\u014E\x05\xDFo\x02" + + "\u014E\u014F\x03\x02\x02\x02\u014F\u0150\b\x0E\x02\x02\u0150\x1E\x03\x02" + + "\x02\x02\u0151\u0152\x05\xD7k\x02\u0152\u0153\x05\xCFg\x02\u0153\u0154" + + "\x05\xD5j\x02\u0154\u0155\x05\xD9l\x02\u0155\u0156\x03\x02\x02\x02\u0156" + + "\u0157\b\x0F\x02\x02\u0157 \x03\x02\x02\x02\u0158\u0159\x05\xD7k\x02\u0159" + + "\u015A\x05\xD9l\x02\u015A\u015B\x05\xB3Y\x02\u015B\u015C\x05\xD9l\x02" + + "\u015C\u015D\x05\xD7k\x02\u015D\u015E\x03\x02\x02\x02\u015E\u015F\b\x10" + + "\x02\x02\u015F\"\x03\x02\x02\x02\u0160\u0161\x05\xDFo\x02\u0161\u0162" + + "\x05\xC1`\x02\u0162\u0163\x05\xBB]\x02\u0163\u0164\x05\xD5j\x02\u0164" + + "\u0165\x05\xBB]\x02\u0165\u0166\x03\x02\x02\x02\u0166\u0167\b\x11\x02" + + "\x02\u0167$\x03\x02\x02\x02\u0168\u016A\n\x02\x02\x02\u0169\u0168\x03" + + "\x02\x02\x02\u016A\u016B\x03\x02\x02\x02\u016B\u0169\x03\x02\x02\x02\u016B" + + "\u016C\x03\x02\x02\x02\u016C\u016D\x03\x02\x02\x02\u016D\u016E\b\x12\x02" + + "\x02\u016E&\x03\x02\x02\x02\u016F\u0170\x071\x02\x02\u0170\u0171\x071" + + "\x02\x02\u0171\u0175\x03\x02\x02\x02\u0172\u0174\n\x03\x02\x02\u0173\u0172" + + "\x03\x02\x02\x02\u0174\u0177\x03\x02\x02\x02\u0175\u0173\x03\x02\x02\x02" + + "\u0175\u0176\x03\x02\x02\x02\u0176\u0179\x03\x02\x02\x02\u0177\u0175\x03" + + "\x02\x02\x02\u0178\u017A\x07\x0F\x02\x02\u0179\u0178\x03\x02\x02\x02\u0179" + + "\u017A\x03\x02\x02\x02\u017A\u017C\x03\x02\x02\x02\u017B\u017D\x07\f\x02" + + "\x02\u017C\u017B\x03\x02\x02\x02\u017C\u017D\x03\x02\x02\x02\u017D\u017E" + + "\x03\x02\x02\x02\u017E\u017F\b\x13\x04\x02\u017F(\x03\x02\x02\x02\u0180" + + "\u0181\x071\x02\x02\u0181\u0182\x07,\x02\x02\u0182\u0187\x03\x02\x02\x02" + + "\u0183\u0186\x05)\x14\x02\u0184\u0186\v\x02\x02\x02\u0185\u0183\x03\x02" + + "\x02\x02\u0185\u0184\x03\x02\x02\x02\u0186\u0189\x03\x02\x02\x02\u0187" + + "\u0188\x03\x02\x02\x02\u0187\u0185\x03\x02\x02\x02\u0188\u018A\x03\x02" + + "\x02\x02\u0189\u0187\x03\x02\x02\x02\u018A\u018B\x07,\x02\x02\u018B\u018C" + + "\x071\x02\x02\u018C\u018D\x03\x02\x02\x02\u018D\u018E\b\x14\x04\x02\u018E" + + "*\x03\x02\x02\x02\u018F\u0191\t\x04\x02\x02\u0190\u018F\x03\x02\x02\x02" + + "\u0191\u0192\x03\x02\x02\x02\u0192\u0190\x03\x02\x02\x02\u0192\u0193\x03" + + "\x02\x02\x02\u0193\u0194\x03\x02\x02\x02\u0194\u0195\b\x15\x04\x02\u0195" + + ",\x03\x02\x02\x02\u0196\u0197\x07~\x02\x02\u0197\u0198\x03\x02\x02\x02" + + "\u0198\u0199\b\x16\x05\x02\u0199.\x03\x02\x02\x02\u019A\u019B\t\x05\x02" + + "\x02\u019B0\x03\x02\x02\x02\u019C\u019D\t\x06\x02\x02\u019D2\x03\x02\x02" + + "\x02\u019E\u019F\x07^\x02\x02\u019F\u01A0\t\x07\x02\x02\u01A04\x03\x02" + + "\x02\x02\u01A1\u01A2\n\b\x02\x02\u01A26\x03\x02\x02\x02\u01A3\u01A5\t" + + "\t\x02\x02\u01A4\u01A6\t\n\x02\x02\u01A5\u01A4\x03\x02\x02\x02\u01A5\u01A6" + + "\x03\x02\x02\x02\u01A6\u01A8\x03\x02\x02\x02\u01A7\u01A9\x05/\x17\x02" + + "\u01A8\u01A7\x03\x02\x02\x02\u01A9\u01AA\x03\x02\x02\x02\u01AA\u01A8\x03" + + "\x02\x02\x02\u01AA\u01AB\x03\x02\x02\x02\u01AB8\x03\x02\x02\x02\u01AC" + + "\u01B1\x07$\x02\x02\u01AD\u01B0\x053\x19\x02\u01AE\u01B0\x055\x1A\x02" + + "\u01AF\u01AD\x03\x02\x02\x02\u01AF\u01AE\x03\x02\x02\x02\u01B0\u01B3\x03" + + "\x02\x02\x02\u01B1\u01AF\x03\x02\x02\x02\u01B1\u01B2\x03\x02\x02\x02\u01B2" + + "\u01B4\x03\x02\x02\x02\u01B3\u01B1\x03\x02\x02\x02\u01B4\u01CA\x07$\x02" + + "\x02\u01B5\u01B6\x07$\x02\x02\u01B6\u01B7\x07$\x02\x02\u01B7\u01B8\x07" + + "$\x02\x02\u01B8\u01BC\x03\x02\x02\x02\u01B9\u01BB\n\x03\x02\x02\u01BA" + + "\u01B9\x03\x02\x02\x02\u01BB\u01BE\x03\x02\x02\x02\u01BC\u01BD\x03\x02" + + "\x02\x02\u01BC\u01BA\x03\x02\x02\x02\u01BD\u01BF\x03\x02\x02\x02\u01BE" + + "\u01BC\x03\x02\x02\x02\u01BF\u01C0\x07$\x02\x02\u01C0\u01C1\x07$\x02\x02" + + "\u01C1\u01C2\x07$\x02\x02\u01C2\u01C4\x03\x02\x02\x02\u01C3\u01C5\x07" + + "$\x02\x02\u01C4\u01C3\x03\x02\x02\x02\u01C4\u01C5\x03\x02\x02\x02\u01C5" + + "\u01C7\x03\x02\x02\x02\u01C6\u01C8\x07$\x02\x02\u01C7\u01C6\x03\x02\x02" + + "\x02\u01C7\u01C8\x03\x02\x02\x02\u01C8\u01CA\x03\x02\x02\x02\u01C9\u01AC" + + "\x03\x02\x02\x02\u01C9\u01B5\x03\x02\x02\x02\u01CA:\x03\x02\x02\x02\u01CB" + + "\u01CD\x05/\x17\x02\u01CC\u01CB\x03\x02\x02\x02\u01CD\u01CE\x03\x02\x02" + + "\x02\u01CE\u01CC\x03\x02\x02\x02\u01CE\u01CF\x03\x02\x02\x02\u01CF<\x03" + + "\x02\x02\x02\u01D0\u01D2\x05/\x17\x02\u01D1\u01D0\x03\x02\x02\x02\u01D2" + + "\u01D3\x03\x02\x02\x02\u01D3\u01D1\x03\x02\x02\x02\u01D3\u01D4\x03\x02" + + "\x02\x02\u01D4\u01D5\x03\x02\x02\x02\u01D5\u01D9\x05K%\x02\u01D6\u01D8" + + "\x05/\x17\x02"; private static readonly _serializedATNSegment1: string = - "\x02\x02\x02\xC1\u05FC\x03\x02\x02\x02\xC3\u05FE\x03\x02\x02\x02\xC5\u0600" + - "\x03\x02\x02\x02\xC7\u0604\x03\x02\x02\x02\xC9\u0608\x03\x02\x02\x02\xCB" + - "\u060C\x03\x02\x02\x02\xCD\u060E\x03\x02\x02\x02\xCF\u0610\x03\x02\x02" + - "\x02\xD1\u0612\x03\x02\x02\x02\xD3\u0614\x03\x02\x02\x02\xD5\u0616\x03" + - "\x02\x02\x02\xD7\u0618\x03\x02\x02\x02\xD9\u061A\x03\x02\x02\x02\xDB\u061C" + - "\x03\x02\x02\x02\xDD\u061E\x03\x02\x02\x02\xDF\u0620\x03\x02\x02\x02\xE1" + - "\u0622\x03\x02\x02\x02\xE3\u0624\x03\x02\x02\x02\xE5\u0626\x03\x02\x02" + - "\x02\xE7\u0628\x03\x02\x02\x02\xE9\u062A\x03\x02\x02\x02\xEB\u062C\x03" + - "\x02\x02\x02\xED\u062E\x03\x02\x02\x02\xEF\u0630\x03\x02\x02\x02\xF1\u0632" + - "\x03\x02\x02\x02\xF3\u0634\x03\x02\x02\x02\xF5\u0636\x03\x02\x02\x02\xF7" + - "\u0638\x03\x02\x02\x02\xF9\u063A\x03\x02\x02\x02\xFB\u063C\x03\x02\x02" + - "\x02\xFD\u063E\x03\x02\x02\x02\xFF\u0100\x05\xD1g\x02\u0100\u0101\x05" + - "\xDBl\x02\u0101\u0102\x05\xEFv\x02\u0102\u0103\x05\xEFv\x02\u0103\u0104" + - "\x05\xD3h\x02\u0104\u0105\x05\xCFf\x02\u0105\u0106\x05\xF1w\x02\u0106" + - "\u0107\x03\x02\x02\x02\u0107\u0108\b\x02\x02\x02\u0108\b\x03\x02\x02\x02" + - "\u0109\u010A\x05\xD7j\x02\u010A\u010B\x05\xEDu\x02\u010B\u010C\x05\xE7" + - "r\x02\u010C\u010D\x05\xDFn\x02\u010D\u010E\x03\x02\x02\x02\u010E\u010F" + - "\b\x03\x02\x02\u010F\n\x03\x02\x02\x02\u0110\u0111\x05\xD3h\x02\u0111" + - "\u0112\x05\xF5y\x02\u0112\u0113\x05\xCBd\x02\u0113\u0114\x05\xE1o\x02" + - "\u0114\u0115\x03\x02\x02\x02\u0115\u0116\b\x04\x02\x02\u0116\f\x03\x02" + - "\x02\x02\u0117\u0118\x05\xD3h\x02\u0118\u0119\x05\xF9{\x02\u0119\u011A" + - "\x05\xE9s\x02\u011A\u011B\x05\xE1o\x02\u011B\u011C\x05\xCBd\x02\u011C" + - "\u011D\x05\xDBl\x02\u011D\u011E\x05\xE5q\x02\u011E\u011F\x03\x02\x02\x02" + - "\u011F\u0120\b\x05\x03\x02\u0120\x0E\x03\x02\x02\x02\u0121\u0122\x05\xD5" + - "i\x02\u0122\u0123\x05\xEDu\x02\u0123\u0124\x05\xE7r\x02\u0124\u0125\x05" + - "\xE3p\x02\u0125\u0126\x03\x02\x02\x02\u0126\u0127\b\x06\x04\x02\u0127" + - "\x10\x03\x02\x02\x02\u0128\u0129\x05\xEDu\x02\u0129\u012A\x05\xE7r\x02" + - "\u012A\u012B\x05\xF7z\x02\u012B\u012C\x03\x02\x02\x02\u012C\u012D\b\x07" + - "\x02\x02\u012D\x12\x03\x02\x02\x02\u012E\u012F\x05\xEFv\x02\u012F\u0130" + - "\x05\xF1w\x02\u0130\u0131\x05\xCBd\x02\u0131\u0132\x05\xF1w\x02\u0132" + - "\u0133\x05\xEFv\x02\u0133\u0134\x03\x02\x02\x02\u0134\u0135\b\b\x02\x02" + - "\u0135\x14\x03\x02\x02\x02\u0136\u0137\x05\xF7z\x02\u0137\u0138\x05\xD9" + - "k\x02\u0138\u0139\x05\xD3h\x02\u0139\u013A\x05\xEDu\x02\u013A\u013B\x05" + - "\xD3h\x02\u013B\u013C\x03\x02\x02\x02\u013C\u013D\b\t\x02\x02\u013D\x16" + - "\x03\x02\x02\x02\u013E\u013F\x05\xEFv\x02\u013F\u0140\x05\xE7r\x02\u0140" + - "\u0141\x05\xEDu\x02\u0141\u0142\x05\xF1w\x02\u0142\u0143\x03\x02\x02\x02" + - "\u0143\u0144\b\n\x02\x02\u0144\x18\x03\x02\x02\x02\u0145\u0146\x05\xE3" + - "p\x02\u0146\u0147\x05\xF5y\x02\u0147\u0148\x05o6\x02\u0148\u0149\x05\xD3" + - "h\x02\u0149\u014A\x05\xF9{\x02\u014A\u014B\x05\xE9s\x02\u014B\u014C\x05" + - "\xCBd\x02\u014C\u014D\x05\xE5q\x02\u014D\u014E\x05\xD1g\x02\u014E\u014F" + - "\x03\x02\x02\x02\u014F\u0150\b\v\x02\x02\u0150\x1A\x03\x02\x02\x02\u0151" + - "\u0152\x05\xE1o\x02\u0152\u0153\x05\xDBl\x02\u0153\u0154\x05\xE3p\x02" + - "\u0154\u0155\x05\xDBl\x02\u0155\u0156\x05\xF1w\x02\u0156\u0157\x03\x02" + - "\x02\x02\u0157\u0158\b\f\x02\x02\u0158\x1C\x03\x02\x02\x02\u0159\u015A" + - "\x05\xE9s\x02\u015A\u015B\x05\xEDu\x02\u015B\u015C\x05\xE7r\x02\u015C" + - "\u015D\x05\xDDm\x02\u015D\u015E\x05\xD3h\x02\u015E\u015F\x05\xCFf\x02" + - "\u015F\u0160\x05\xF1w\x02\u0160\u0161\x03\x02\x02\x02\u0161\u0162\b\r" + - "\x02\x02\u0162\x1E\x03\x02\x02\x02\u0163\u0164\x05\xD1g\x02\u0164\u0165" + - "\x05\xEDu\x02\u0165\u0166\x05\xE7r\x02\u0166\u0167\x05\xE9s\x02\u0167" + - "\u0168\x03\x02\x02\x02\u0168\u0169\b\x0E\x02\x02\u0169 \x03\x02\x02\x02" + - "\u016A\u016B\x05\xEDu\x02\u016B\u016C\x05\xD3h\x02\u016C\u016D\x05\xE5" + - "q\x02\u016D\u016E\x05\xCBd\x02\u016E\u016F\x05\xE3p\x02\u016F\u0170\x05" + - "\xD3h\x02\u0170\u0171\x03\x02\x02\x02\u0171\u0172\b\x0F\x02\x02\u0172" + - "\"\x03\x02\x02\x02\u0173\u0174\x05\xEFv\x02\u0174\u0175\x05\xD9k\x02\u0175" + - "\u0176\x05\xE7r\x02\u0176\u0177\x05\xF7z\x02\u0177\u0178\x03\x02\x02\x02" + - "\u0178\u0179\b\x10\x02\x02\u0179$\x03\x02\x02\x02\u017A\u017B\x05\xD3" + - "h\x02\u017B\u017C\x05\xE5q\x02\u017C\u017D\x05\xEDu\x02\u017D\u017E\x05" + - "\xDBl\x02\u017E\u017F\x05\xCFf\x02\u017F\u0180\x05\xD9k\x02\u0180\u0181" + - "\x03\x02\x02\x02\u0181\u0182\b\x11\x05\x02\u0182&\x03\x02\x02\x02\u0183" + - "\u0184\x05\xDFn\x02\u0184\u0185\x05\xD3h\x02\u0185\u0186\x05\xD3h\x02" + - "\u0186\u0187\x05\xE9s\x02\u0187\u0188\x03\x02\x02\x02\u0188\u0189\b\x12" + - "\x02\x02\u0189(\x03\x02\x02\x02\u018A\u018B\x071\x02\x02\u018B\u018C\x07" + - "1\x02\x02\u018C\u0190\x03\x02\x02\x02\u018D\u018F\n\x02\x02\x02\u018E" + - "\u018D\x03\x02\x02\x02\u018F\u0192\x03\x02\x02\x02\u0190\u018E\x03\x02" + - "\x02\x02\u0190\u0191\x03\x02\x02\x02\u0191\u0194\x03\x02\x02\x02\u0192" + - "\u0190\x03\x02\x02\x02\u0193\u0195\x07\x0F\x02\x02\u0194\u0193\x03\x02" + - "\x02\x02\u0194\u0195\x03\x02\x02\x02\u0195\u0197\x03\x02\x02\x02\u0196" + - "\u0198\x07\f\x02\x02\u0197\u0196\x03\x02\x02\x02\u0197\u0198\x03\x02\x02" + - "\x02\u0198\u0199\x03\x02\x02\x02\u0199\u019A\b\x13\x06\x02\u019A*\x03" + - "\x02\x02\x02\u019B\u019C\x071\x02\x02\u019C\u019D\x07,\x02\x02\u019D\u01A2" + - "\x03\x02\x02\x02\u019E\u01A1\x05+\x14\x02\u019F\u01A1\v\x02\x02\x02\u01A0" + - "\u019E\x03\x02\x02\x02\u01A0\u019F\x03\x02\x02\x02\u01A1\u01A4\x03\x02" + - "\x02\x02\u01A2\u01A3\x03\x02\x02\x02\u01A2\u01A0\x03\x02\x02\x02\u01A3" + - "\u01A5\x03\x02\x02\x02\u01A4\u01A2\x03\x02\x02\x02\u01A5\u01A6\x07,\x02" + - "\x02\u01A6\u01A7\x071\x02\x02\u01A7\u01A8\x03\x02\x02\x02\u01A8\u01A9" + - "\b\x14\x06\x02\u01A9,\x03\x02\x02\x02\u01AA\u01AC\t\x03\x02\x02\u01AB" + - "\u01AA\x03\x02\x02\x02\u01AC\u01AD\x03\x02\x02\x02\u01AD\u01AB\x03\x02" + - "\x02\x02\u01AD\u01AE\x03\x02\x02\x02\u01AE\u01AF\x03\x02\x02\x02\u01AF" + - "\u01B0\b\x15\x06\x02\u01B0.\x03\x02\x02\x02\u01B1\u01B2\x07]\x02\x02\u01B2" + - "\u01B3\x03\x02\x02\x02\u01B3\u01B4\b\x16\x07\x02\u01B4\u01B5\b\x16\b\x02" + - "\u01B50\x03\x02\x02\x02\u01B6\u01B7\x07~\x02\x02\u01B7\u01B8\x03\x02\x02" + - "\x02\u01B8\u01B9\b\x17\t\x02\u01B9\u01BA\b\x17\n\x02\u01BA2\x03\x02\x02" + - "\x02\u01BB\u01BC\x05-\x15\x02\u01BC\u01BD\x03\x02\x02\x02\u01BD\u01BE" + - "\b\x18\x06\x02\u01BE4\x03\x02\x02\x02\u01BF\u01C0\x05)\x13\x02\u01C0\u01C1" + - "\x03\x02\x02\x02\u01C1\u01C2\b\x19\x06\x02\u01C26\x03\x02\x02\x02\u01C3" + - "\u01C4\x05+\x14\x02\u01C4\u01C5\x03\x02\x02\x02\u01C5\u01C6\b\x1A\x06" + - "\x02\u01C68\x03\x02\x02\x02\u01C7\u01C8\x07~\x02\x02\u01C8\u01C9\x03\x02" + - "\x02\x02\u01C9\u01CA\b\x1B\n\x02\u01CA:\x03\x02\x02\x02\u01CB\u01CC\t" + - "\x04\x02\x02\u01CC<\x03\x02\x02\x02\u01CD\u01CE\t\x05\x02\x02\u01CE>\x03" + - "\x02\x02\x02\u01CF\u01D0\x07^\x02\x02\u01D0\u01D1\t\x06\x02\x02\u01D1" + - "@\x03\x02\x02\x02\u01D2\u01D3\n\x07\x02\x02\u01D3B\x03\x02\x02\x02\u01D4" + - "\u01D6\t\b\x02\x02\u01D5\u01D7\t\t\x02\x02\u01D6\u01D5\x03\x02\x02\x02" + - "\u01D6\u01D7\x03\x02\x02\x02\u01D7\u01D9\x03\x02\x02\x02\u01D8\u01DA\x05" + - ";\x1C\x02\u01D9\u01D8\x03\x02\x02\x02\u01DA\u01DB\x03\x02\x02\x02\u01DB" + - "\u01D9\x03\x02\x02\x02\u01DB\u01DC\x03\x02\x02\x02\u01DCD\x03\x02\x02" + - "\x02\u01DD\u01E2\x07$\x02\x02\u01DE\u01E1\x05?\x1E\x02\u01DF\u01E1\x05" + - "A\x1F\x02\u01E0\u01DE\x03\x02\x02\x02\u01E0\u01DF\x03\x02\x02\x02\u01E1" + - "\u01E4\x03\x02\x02\x02\u01E2\u01E0\x03\x02\x02\x02\u01E2\u01E3\x03\x02" + - "\x02\x02\u01E3\u01E5\x03\x02\x02\x02\u01E4\u01E2\x03\x02\x02\x02\u01E5" + - "\u01FB\x07$\x02\x02\u01E6\u01E7\x07$\x02\x02\u01E7\u01E8\x07$\x02\x02" + - "\u01E8\u01E9\x07$\x02\x02\u01E9\u01ED\x03\x02\x02\x02\u01EA\u01EC\n\x02" + - "\x02\x02\u01EB\u01EA\x03\x02\x02\x02\u01EC\u01EF\x03\x02\x02\x02\u01ED" + - "\u01EE\x03\x02\x02\x02\u01ED\u01EB\x03\x02\x02\x02\u01EE\u01F0\x03\x02" + - "\x02\x02\u01EF\u01ED\x03\x02\x02\x02\u01F0\u01F1\x07$\x02\x02\u01F1\u01F2" + - "\x07$\x02\x02\u01F2\u01F3\x07$\x02\x02\u01F3\u01F5\x03\x02\x02\x02\u01F4" + - "\u01F6\x07$\x02\x02\u01F5\u01F4\x03\x02\x02\x02\u01F5\u01F6\x03\x02\x02" + - "\x02\u01F6\u01F8\x03\x02\x02\x02\u01F7\u01F9\x07$\x02\x02\u01F8\u01F7" + - "\x03\x02\x02\x02\u01F8\u01F9\x03\x02\x02\x02\u01F9\u01FB\x03\x02\x02\x02" + - "\u01FA\u01DD\x03\x02\x02\x02\u01FA\u01E6\x03\x02\x02\x02\u01FBF\x03\x02" + - "\x02\x02\u01FC\u01FE\x05;\x1C\x02\u01FD\u01FC\x03\x02\x02\x02\u01FE\u01FF" + - "\x03\x02\x02\x02\u01FF\u01FD\x03\x02\x02\x02\u01FF\u0200\x03\x02\x02\x02" + - "\u0200H\x03\x02\x02\x02\u0201\u0203\x05;\x1C\x02\u0202\u0201\x03\x02\x02" + - "\x02\u0203\u0204\x03\x02\x02\x02\u0204\u0202\x03\x02\x02\x02\u0204\u0205" + - "\x03\x02\x02\x02\u0205\u0206\x03\x02\x02\x02\u0206\u020A\x05U)\x02\u0207" + - "\u0209\x05;\x1C\x02\u0208\u0207\x03\x02\x02\x02\u0209\u020C\x03\x02\x02" + - "\x02\u020A\u0208\x03\x02\x02\x02\u020A\u020B\x03\x02\x02\x02\u020B\u022C" + - "\x03\x02\x02\x02\u020C\u020A\x03\x02\x02\x02\u020D\u020F\x05U)\x02\u020E" + - "\u0210\x05;\x1C\x02\u020F\u020E\x03\x02\x02\x02\u0210\u0211\x03\x02\x02" + - "\x02\u0211\u020F\x03\x02\x02\x02\u0211\u0212\x03\x02\x02\x02\u0212\u022C" + - "\x03\x02\x02\x02\u0213\u0215\x05;\x1C\x02\u0214\u0213\x03\x02\x02\x02" + - "\u0215\u0216\x03\x02\x02\x02\u0216\u0214\x03\x02\x02\x02\u0216\u0217\x03" + - "\x02\x02\x02\u0217\u021F\x03\x02\x02\x02\u0218\u021C\x05U)\x02\u0219\u021B" + - "\x05;\x1C\x02\u021A\u0219\x03\x02\x02\x02\u021B\u021E\x03\x02\x02\x02" + - "\u021C\u021A\x03\x02\x02\x02\u021C\u021D\x03\x02\x02\x02\u021D\u0220\x03" + - "\x02\x02\x02\u021E\u021C\x03\x02\x02\x02\u021F\u0218\x03\x02\x02\x02\u021F" + - "\u0220\x03\x02\x02\x02\u0220\u0221\x03\x02\x02\x02\u0221\u0222\x05C \x02" + - "\u0222\u022C\x03\x02\x02\x02\u0223\u0225\x05U)\x02\u0224\u0226\x05;\x1C" + - "\x02\u0225\u0224\x03\x02\x02\x02\u0226\u0227\x03\x02\x02\x02\u0227\u0225" + - "\x03\x02\x02\x02\u0227\u0228\x03\x02\x02\x02\u0228\u0229\x03\x02\x02\x02" + - "\u0229\u022A\x05C \x02\u022A\u022C\x03\x02\x02\x02\u022B\u0202\x03\x02" + - "\x02\x02\u022B\u020D\x03\x02\x02\x02\u022B\u0214\x03\x02\x02\x02\u022B" + - "\u0223\x03\x02\x02\x02\u022CJ\x03\x02\x02\x02\u022D\u022E\x07d\x02\x02" + - "\u022E\u022F\x07{\x02\x02\u022FL\x03\x02\x02\x02\u0230\u0231\x07{\x02" + - "\x02\u0231\u0232\x07g\x02\x02\u0232\u0233\x07c\x02\x02\u0233\u028F\x07" + - "t\x02\x02\u0234\u0235\x07o\x02\x02\u0235\u0236\x07q\x02\x02\u0236\u0237" + - "\x07p\x02\x02\u0237\u0238\x07v\x02\x02\u0238\u028F\x07j\x02\x02\u0239" + - "\u023A\x07f\x02\x02\u023A\u023B\x07c\x02\x02\u023B\u028F\x07{\x02\x02" + - "\u023C\u023D\x07u\x02\x02\u023D\u023E\x07g\x02\x02\u023E\u023F\x07e\x02" + - "\x02\u023F\u0240\x07q\x02\x02\u0240\u0241\x07p\x02\x02\u0241\u028F\x07" + - "f\x02\x02\u0242\u0243\x07o\x02\x02\u0243\u0244\x07k\x02\x02\u0244\u0245" + - "\x07p\x02\x02\u0245\u0246\x07w\x02\x02\u0246\u0247\x07v\x02\x02\u0247" + - "\u028F\x07g\x02\x02\u0248\u0249\x07j\x02\x02\u0249\u024A\x07q\x02\x02" + - "\u024A\u024B\x07w\x02\x02\u024B\u028F\x07t\x02\x02\u024C\u024D\x07y\x02" + - "\x02\u024D\u024E\x07g\x02\x02\u024E\u024F\x07g\x02\x02\u024F\u028F\x07" + - "m\x02\x02\u0250\u0251\x07o\x02\x02\u0251\u0252\x07k\x02\x02\u0252\u0253" + - "\x07n\x02\x02\u0253\u0254\x07n\x02\x02\u0254\u0255\x07k\x02\x02\u0255" + - "\u0256\x07u\x02\x02\u0256\u0257\x07g\x02\x02\u0257\u0258\x07e\x02\x02" + - "\u0258\u0259\x07q\x02\x02\u0259\u025A\x07p\x02\x02\u025A\u028F\x07f\x02" + - "\x02\u025B\u025C\x07{\x02\x02\u025C\u025D\x07g\x02\x02\u025D\u025E\x07" + - "c\x02\x02\u025E\u025F\x07t\x02\x02\u025F\u028F\x07u\x02\x02\u0260\u0261" + - "\x07o\x02\x02\u0261\u0262\x07q\x02\x02\u0262\u0263\x07p\x02\x02\u0263" + - "\u0264\x07v\x02\x02\u0264\u0265\x07j\x02\x02\u0265\u028F\x07u\x02\x02" + - "\u0266\u0267\x07f\x02\x02\u0267\u0268\x07c\x02\x02\u0268\u0269\x07{\x02" + - "\x02\u0269\u028F\x07u\x02\x02\u026A\u026B\x07u\x02\x02\u026B\u026C\x07" + - "g\x02\x02\u026C\u026D\x07e\x02\x02\u026D\u026E\x07q\x02\x02\u026E\u026F" + - "\x07p\x02\x02\u026F\u0270\x07f\x02\x02\u0270\u028F\x07u\x02\x02\u0271" + - "\u0272\x07o\x02\x02\u0272\u0273\x07k\x02\x02\u0273\u0274\x07p\x02\x02" + - "\u0274\u0275\x07w\x02\x02\u0275\u0276\x07v\x02\x02\u0276\u0277\x07g\x02" + - "\x02\u0277\u028F\x07u\x02\x02\u0278\u0279\x07j\x02\x02\u0279\u027A\x07" + - "q\x02\x02\u027A\u027B\x07w\x02\x02\u027B\u027C\x07t\x02\x02\u027C\u028F" + - "\x07u\x02\x02\u027D\u027E\x07y\x02\x02\u027E\u027F\x07g\x02\x02\u027F" + - "\u0280\x07g\x02\x02\u0280\u0281\x07m\x02\x02\u0281\u028F\x07u\x02\x02" + - "\u0282\u0283\x07o\x02\x02\u0283\u0284\x07k\x02\x02\u0284\u0285\x07n\x02" + - "\x02\u0285\u0286\x07n\x02\x02\u0286\u0287\x07k\x02\x02\u0287\u0288\x07" + - "u\x02\x02\u0288\u0289\x07g\x02\x02\u0289\u028A\x07e\x02\x02\u028A\u028B" + - "\x07q\x02\x02\u028B\u028C\x07p\x02\x02\u028C\u028D\x07f\x02\x02\u028D" + - "\u028F\x07u\x02\x02\u028E\u0230\x03\x02\x02\x02\u028E\u0234\x03\x02\x02" + - "\x02\u028E\u0239\x03\x02\x02\x02\u028E\u023C\x03\x02\x02\x02\u028E\u0242" + - "\x03\x02\x02\x02\u028E\u0248\x03\x02\x02\x02\u028E\u024C\x03\x02\x02\x02" + - "\u028E\u0250\x03\x02\x02\x02\u028E\u025B\x03\x02\x02\x02\u028E\u0260\x03" + - "\x02\x02\x02\u028E\u0266\x03\x02\x02\x02\u028E\u026A\x03\x02\x02\x02\u028E" + - "\u0271\x03\x02\x02\x02\u028E\u0278\x03\x02\x02\x02\u028E\u027D\x03\x02" + - "\x02\x02\u028E\u0282\x03\x02\x02\x02\u028FN\x03\x02\x02\x02\u0290\u0291" + - "\x07c\x02\x02\u0291\u0292\x07p\x02\x02\u0292\u0293\x07f\x02\x02\u0293" + - "P\x03\x02\x02\x02\u0294\u0295\x07?\x02\x02\u0295R\x03\x02\x02\x02\u0296" + - "\u0297\x07.\x02\x02\u0297T\x03\x02\x02\x02\u0298\u0299\x070\x02\x02\u0299" + - "V\x03\x02\x02\x02\u029A\u029B\x07*\x02\x02\u029BX\x03\x02\x02\x02\u029C" + - "\u029D\x07]\x02\x02\u029D\u029E\x03\x02\x02\x02\u029E\u029F\b+\x02\x02" + - "\u029F\u02A0\b+\x02\x02\u02A0Z\x03\x02\x02\x02\u02A1\u02A2\x07_\x02\x02" + - "\u02A2\u02A3\x03\x02\x02\x02\u02A3\u02A4\b,\n\x02\u02A4\u02A5\b,\n\x02" + - "\u02A5\\\x03\x02\x02\x02\u02A6\u02A7\x05\xE5q\x02\u02A7\u02A8\x05\xE7" + - "r\x02\u02A8\u02A9\x05\xF1w\x02\u02A9^\x03\x02\x02\x02\u02AA\u02AB\x05" + - "\xE1o\x02\u02AB\u02AC\x05\xDBl\x02\u02AC\u02AD\x05\xDFn\x02\u02AD\u02AE" + - "\x05\xD3h\x02\u02AE`\x03\x02\x02\x02\u02AF\u02B0\x05\xEDu\x02\u02B0\u02B1" + - "\x05\xE1o\x02\u02B1\u02B2\x05\xDBl\x02\u02B2\u02B3\x05\xDFn\x02\u02B3" + - "\u02B4\x05\xD3h\x02\u02B4b\x03\x02\x02\x02\u02B5\u02B6\x05\xDBl\x02\u02B6" + - "\u02B7\x05\xE5q\x02\u02B7d\x03\x02\x02\x02\u02B8\u02B9\x05\xDBl\x02\u02B9" + - "\u02BA\x05\xEFv\x02\u02BAf\x03\x02\x02\x02\u02BB\u02BC\x05\xCBd\x02\u02BC" + - "\u02BD\x05\xEFv\x02\u02BDh\x03\x02\x02\x02\u02BE\u02BF\x05\xE5q\x02\u02BF" + - "\u02C0\x05\xF3x\x02\u02C0\u02C1\x05\xE1o\x02\u02C1\u02C2\x05\xE1o\x02" + - "\u02C2j\x03\x02\x02\x02\u02C3\u02C4\x07q\x02\x02\u02C4\u02C5\x07t\x02" + - "\x02\u02C5l\x03\x02\x02\x02\u02C6\u02C7\x07+\x02\x02\u02C7n\x03\x02\x02" + - "\x02\u02C8\u02C9\x07a\x02\x02\u02C9p\x03\x02\x02\x02\u02CA\u02CB\x07k" + - "\x02\x02\u02CB\u02CC\x07p\x02\x02\u02CC\u02CD\x07h\x02\x02\u02CD\u02CE" + - "\x07q\x02\x02\u02CEr\x03\x02\x02\x02\u02CF\u02D0\x07h\x02\x02\u02D0\u02D1" + - "\x07w\x02\x02\u02D1\u02D2\x07p\x02\x02\u02D2\u02D3\x07e\x02\x02\u02D3" + - "\u02D4\x07v\x02\x02\u02D4\u02D5\x07k\x02\x02\u02D5\u02D6\x07q\x02\x02" + - "\u02D6\u02D7\x07p\x02\x02\u02D7\u02D8\x07u\x02\x02\u02D8t\x03\x02\x02" + - "\x02\u02D9\u02DA\x07v\x02\x02\u02DA\u02DB\x07t\x02\x02\u02DB\u02DC\x07" + - "w\x02\x02\u02DC\u02E3\x07g\x02\x02\u02DD\u02DE\x07h\x02\x02\u02DE\u02DF" + - "\x07c\x02\x02\u02DF\u02E0\x07n\x02\x02\u02E0\u02E1\x07u\x02\x02\u02E1" + - "\u02E3\x07g\x02\x02\u02E2\u02D9\x03\x02\x02\x02\u02E2\u02DD\x03\x02\x02" + - "\x02\u02E3v\x03\x02\x02\x02\u02E4\u02E5\x07?\x02\x02\u02E5\u02EF\x07?" + - "\x02\x02\u02E6\u02E7\x07#\x02\x02\u02E7\u02EF\x07?\x02\x02\u02E8\u02EF" + - "\x07>\x02\x02\u02E9\u02EA\x07>\x02\x02\u02EA\u02EF\x07?\x02\x02\u02EB" + - "\u02EF\x07@\x02\x02\u02EC\u02ED\x07@\x02\x02\u02ED\u02EF\x07?\x02\x02" + - "\u02EE\u02E4\x03\x02\x02\x02\u02EE\u02E6\x03\x02\x02\x02\u02EE\u02E8\x03" + - "\x02\x02\x02\u02EE\u02E9\x03\x02\x02\x02\u02EE\u02EB\x03\x02\x02\x02\u02EE" + - "\u02EC\x03\x02\x02\x02\u02EFx\x03\x02\x02\x02\u02F0\u02F1\x07-\x02\x02" + - "\u02F1z\x03\x02\x02\x02\u02F2\u02F3\x07/\x02\x02\u02F3|\x03\x02\x02\x02" + - "\u02F4\u02F5\x07,\x02\x02\u02F5~\x03\x02\x02\x02\u02F6\u02F7\x071\x02" + - "\x02\u02F7\x80\x03\x02\x02\x02\u02F8\u02F9\x07\'\x02\x02\u02F9\x82\x03" + - "\x02\x02\x02\u02FA\u02FB\x073\x02\x02\u02FB\u02FC\x072\x02\x02\u02FC\x84" + - "\x03\x02\x02\x02\u02FD\u02FE\x07c\x02\x02\u02FE\u02FF\x07u\x02\x02\u02FF" + - "\u0305\x07e\x02\x02\u0300\u0301\x07f\x02\x02\u0301\u0302\x07g\x02\x02" + - "\u0302\u0303\x07u\x02\x02\u0303\u0305\x07e\x02\x02\u0304\u02FD\x03\x02" + - "\x02\x02\u0304\u0300\x03\x02\x02\x02\u0305\x86\x03\x02\x02\x02\u0306\u0307" + - "\x07p\x02\x02\u0307\u0308\x07w\x02\x02\u0308\u0309\x07n\x02\x02\u0309" + - "\u030A\x07n\x02\x02\u030A\u030B\x07u\x02\x02\u030B\x88\x03\x02\x02\x02" + - "\u030C\u030D\x07h\x02\x02\u030D\u030E\x07k\x02\x02\u030E\u030F\x07t\x02" + - "\x02\u030F\u0310\x07u\x02\x02\u0310\u0316\x07v\x02\x02\u0311\u0312\x07" + - "n\x02\x02\u0312\u0313\x07c\x02\x02\u0313\u0314\x07u\x02\x02\u0314\u0316" + - "\x07v\x02\x02\u0315\u030C\x03\x02\x02\x02\u0315\u0311\x03\x02\x02\x02" + - "\u0316\x8A\x03\x02\x02\x02\u0317\u0318\x05\xEDu\x02\u0318\u0319\x05\xE7" + - "r\x02\u0319\u031A\x05\xF3x\x02\u031A\u031B\x05\xE5q\x02\u031B\u031C\x05" + - "\xD1g\x02\u031C\u04C6\x03\x02\x02\x02\u031D\u031E\x05\xCBd\x02\u031E\u031F" + - "\x05\xCDe\x02\u031F\u0320\x05\xEFv\x02\u0320\u04C6\x03\x02\x02\x02\u0321" + - "\u0322\x05\xE9s\x02\u0322\u0323\x05\xE7r\x02\u0323\u0324\x05\xF7z\x02" + - "\u0324\u04C6\x03\x02\x02\x02\u0325\u0326\x05\xE1o\x02\u0326\u0327\x05" + - "\xE7r\x02\u0327\u0328\x05\xD7j\x02\u0328\u0329\x05\x83@\x02\u0329\u04C6" + - "\x03\x02\x02\x02\u032A\u032B\x05\xE9s\x02\u032B\u032C\x05\xDBl\x02\u032C" + - "\u04C6\x03\x02\x02\x02\u032D\u032E\x05\xF1w\x02\u032E\u032F\x05\xCBd\x02" + - "\u032F\u0330\x05\xF3x\x02\u0330\u04C6\x03\x02\x02\x02\u0331\u04C6\x05" + - "\xD3h\x02\u0332\u0333\x05\xEFv\x02\u0333\u0334\x05\xF3x\x02\u0334\u0335" + - "\x05\xCDe\x02\u0335\u0336\x05\xEFv\x02\u0336\u0337\x05\xF1w\x02\u0337" + - "\u0338\x05\xEDu\x02\u0338\u0339\x05\xDBl\x02\u0339\u033A\x05\xE5q\x02" + - "\u033A\u033B\x05\xD7j\x02\u033B\u04C6\x03\x02\x02\x02\u033C\u033D\x05" + - "\xF1w\x02\u033D\u033E\x05\xEDu\x02\u033E\u033F\x05\xDBl\x02\u033F\u0340" + - "\x05\xE3p\x02\u0340\u04C6\x03\x02\x02\x02\u0341\u0342\x05\xCFf\x02\u0342" + - "\u0343\x05\xE7r\x02\u0343\u0344\x05\xE5q\x02\u0344\u0345\x05\xCFf\x02" + - "\u0345\u0346\x05\xCBd\x02\u0346\u0347\x05\xF1w\x02\u0347\u04C6\x03\x02" + - "\x02\x02\u0348\u0349\x05\xCFf\x02\u0349\u034A\x05\xE7r\x02\u034A\u034B" + - "\x05\xCBd\x02\u034B\u034C\x05\xE1o\x02\u034C\u034D\x05\xD3h\x02\u034D" + - "\u034E\x05\xEFv\x02\u034E\u034F\x05\xCFf\x02\u034F\u0350\x05\xD3h\x02" + - "\u0350\u04C6\x03\x02\x02\x02\u0351\u0352\x05\xD7j\x02\u0352\u0353\x05" + - "\xEDu\x02\u0353\u0354\x05\xD3h\x02\u0354\u0355\x05\xCBd\x02\u0355\u0356" + - "\x05\xF1w\x02\u0356\u0357\x05\xD3h\x02\u0357\u0358\x05\xEFv\x02\u0358" + - "\u0359\x05\xF1w\x02\u0359\u04C6\x03\x02\x02\x02\u035A\u035B\x05\xE1o\x02" + - "\u035B\u035C\x05\xD3h\x02\u035C\u035D\x05\xD5i\x02\u035D\u035E\x05\xF1" + - "w\x02\u035E\u04C6\x03\x02\x02\x02\u035F\u0360\x05\xE5q\x02\u0360\u0361" + - "\x05\xE7r\x02\u0361\u0362\x05\xF7z\x02\u0362\u04C6\x03\x02\x02\x02\u0363" + - "\u0364\x05\xEDu\x02\u0364\u0365\x05\xDBl\x02\u0365\u0366\x05\xD7j\x02" + - "\u0366\u0367\x05\xD9k\x02\u0367\u0368\x05\xF1w\x02\u0368\u04C6\x03\x02" + - "\x02\x02\u0369\u036A\x05\xEFv\x02\u036A\u036B\x05\xF1w\x02\u036B\u036C" + - "\x05\xCBd\x02\u036C\u036D\x05\xEDu\x02\u036D\u036E\x05\xF1w\x02\u036E" + - "\u036F\x05\xEFv\x02\u036F\u0370\x05o6\x02\u0370\u0371\x05\xF7z\x02\u0371" + - "\u0372\x05\xDBl\x02\u0372\u0373\x05\xF1w\x02\u0373\u0374\x05\xD9k\x02" + - "\u0374\u04C6\x03\x02\x02\x02\u0375\u0376\x05\xD1g\x02\u0376\u0377\x05" + - "\xCBd\x02\u0377\u0378\x05\xF1w\x02\u0378\u0379\x05\xD3h\x02\u0379\u037A" + - "\x05o6\x02\u037A\u037B\x05\xD5i\x02\u037B\u037C\x05\xE7r\x02\u037C\u037D" + - "\x05\xEDu\x02\u037D\u037E\x05\xE3p\x02\u037E\u037F\x05\xCBd\x02\u037F" + - "\u0380\x05\xF1w\x02\u0380\u04C6\x03\x02\x02\x02\u0381\u0382\x05\xD1g\x02" + - "\u0382\u0383\x05\xCBd\x02\u0383\u0384\x05\xF1w\x02\u0384\u0385\x05\xD3" + - "h\x02\u0385\u0386\x05o6\x02\u0386\u0387\x05\xF1w\x02\u0387\u0388\x05\xED" + - "u\x02\u0388\u0389\x05\xF3x\x02\u0389\u038A\x05\xE5q\x02\u038A\u038B\x05" + - "\xCFf\x02\u038B\u04C6\x03\x02\x02\x02\u038C\u038D\x05\xD1g\x02\u038D\u038E" + - "\x05\xCBd\x02\u038E\u038F\x05\xF1w\x02\u038F\u0390\x05\xD3h\x02\u0390" + - "\u0391\x05o6\x02\u0391\u0392\x05\xE9s\x02\u0392\u0393\x05\xCBd\x02\u0393" + - "\u0394\x05\xEDu\x02\u0394\u0395\x05\xEFv\x02\u0395\u0396\x05\xD3h\x02" + - "\u0396\u04C6\x03\x02\x02\x02\u0397\u0398\x05\xCBd\x02\u0398\u0399\x05" + - "\xF3x\x02\u0399\u039A\x05\xF1w\x02\u039A\u039B\x05\xE7r\x02\u039B\u039C" + - "\x05o6\x02\u039C\u039D\x05\xCDe\x02\u039D\u039E\x05\xF3x\x02\u039E\u039F" + - "\x05\xCFf\x02\u039F\u03A0\x05\xDFn\x02\u03A0\u03A1\x05\xD3h\x02\u03A1" + - "\u03A2\x05\xF1w\x02\u03A2\u04C6\x03\x02\x02\x02\u03A3\u03A4\x05\xD1g\x02" + - "\u03A4\u03A5\x05\xCBd\x02\u03A5\u03A6\x05\xF1w\x02\u03A6\u03A7\x05\xD3" + - "h\x02\u03A7\u03A8\x05o6\x02\u03A8\u03A9\x05\xD3h\x02\u03A9\u03AA\x05\xF9" + - "{\x02\u03AA\u03AB\x05\xF1w\x02\u03AB\u03AC\x05\xEDu\x02\u03AC\u03AD\x05" + - "\xCBd\x02\u03AD\u03AE\x05\xCFf\x02\u03AE\u03AF\x05\xF1w\x02\u03AF\u04C6" + - "\x03\x02\x02\x02\u03B0\u03B1\x05\xDBl\x02\u03B1\u03B2\x05\xEFv\x02\u03B2" + - "\u03B3\x05o6\x02\u03B3\u03B4\x05\xD5i\x02\u03B4\u03B5\x05\xDBl\x02\u03B5" + - "\u03B6\x05\xE5q\x02\u03B6\u03B7\x05\xDBl\x02\u03B7\u03B8\x05\xF1w\x02" + - "\u03B8\u03B9\x05\xD3h\x02\u03B9\u04C6\x03\x02\x02\x02\u03BA\u03BB\x05" + - "\xDBl\x02\u03BB\u03BC\x05\xEFv\x02\u03BC\u03BD\x05o6\x02\u03BD\u03BE\x05" + - "\xDBl\x02\u03BE\u03BF\x05\xE5q\x02\u03BF\u03C0\x05\xD5i\x02\u03C0\u03C1" + - "\x05\xDBl\x02\u03C1\u03C2\x05\xE5q\x02\u03C2\u03C3\x05\xDBl\x02\u03C3" + - "\u03C4\x05\xF1w\x02\u03C4\u03C5\x05\xD3h\x02\u03C5\u04C6\x03\x02\x02\x02" + - "\u03C6\u03C7\x05\xCFf\x02\u03C7\u03C8\x05\xCBd\x02\u03C8\u03C9\x05\xEF" + - "v\x02\u03C9\u03CA\x05\xD3h\x02\u03CA\u04C6\x03\x02\x02\x02\u03CB\u03CC" + - "\x05\xE1o\x02\u03CC\u03CD\x05\xD3h\x02\u03CD\u03CE\x05\xE5q\x02\u03CE" + - "\u03CF\x05\xD7j\x02\u03CF\u03D0\x05\xF1w\x02\u03D0\u03D1\x05\xD9k\x02" + - "\u03D1\u04C6\x03\x02\x02\x02\u03D2\u03D3\x05\xE3p\x02\u03D3\u03D4\x05" + - "\xF5y\x02\u03D4\u03D5\x05o6\x02\u03D5\u03D6\x05\xE3p\x02\u03D6\u03D7\x05" + - "\xCBd\x02\u03D7\u03D8\x05\xF9{\x02\u03D8\u04C6\x03\x02\x02\x02\u03D9\u03DA" + - "\x05\xE3p\x02\u03DA\u03DB\x05\xF5y\x02\u03DB\u03DC\x05o6\x02\u03DC\u03DD" + - "\x05\xE3p\x02\u03DD\u03DE\x05\xDBl\x02\u03DE\u03DF\x05\xE5q\x02\u03DF" + - "\u04C6\x03\x02\x02\x02\u03E0\u03E1\x05\xE3p\x02\u03E1\u03E2\x05\xF5y\x02" + - "\u03E2\u03E3\x05o6\x02\u03E3\u03E4\x05\xCBd\x02\u03E4\u03E5\x05\xF5y\x02" + - "\u03E5\u03E6\x05\xD7j\x02\u03E6\u04C6\x03\x02\x02\x02\u03E7\u03E8\x05" + - "\xE3p\x02\u03E8\u03E9\x05\xF5y\x02\u03E9\u03EA\x05o6\x02\u03EA\u03EB\x05" + - "\xEFv\x02\u03EB\u03EC\x05\xF3x\x02\u03EC\u03ED\x05\xE3p\x02\u03ED\u04C6" + - "\x03\x02\x02\x02\u03EE\u03EF\x05\xE3p\x02\u03EF\u03F0\x05\xF5y\x02\u03F0" + - "\u03F1\x05o6"; - private static readonly _serializedATNSegment2: string = - "\x02\u03F1\u03F2\x05\xCFf\x02\u03F2\u03F3\x05\xE7r\x02\u03F3\u03F4\x05" + - "\xF3x\x02\u03F4\u03F5\x05\xE5q\x02\u03F5\u03F6\x05\xF1w\x02\u03F6\u04C6" + - "\x03\x02\x02\x02\u03F7\u03F8\x05\xE3p\x02\u03F8\u03F9\x05\xF5y\x02\u03F9" + - "\u03FA\x05o6\x02\u03FA\u03FB\x05\xCFf\x02\u03FB\u03FC\x05\xE7r\x02\u03FC" + - "\u03FD\x05\xE5q\x02\u03FD\u03FE\x05\xCFf\x02\u03FE\u03FF\x05\xCBd\x02" + - "\u03FF\u0400\x05\xF1w\x02\u0400\u04C6\x03\x02\x02\x02\u0401\u0402\x05" + - "\xE3p\x02\u0402\u0403\x05\xF5y\x02\u0403\u0404\x05o6\x02\u0404\u0405\x05" + - "\xDDm\x02\u0405\u0406\x05\xE7r\x02\u0406\u0407\x05\xDBl\x02\u0407\u0408" + - "\x05\xE5q\x02\u0408\u04C6\x03\x02\x02\x02\u0409\u040A\x05\xE3p\x02\u040A" + - "\u040B\x05\xF5y\x02\u040B\u040C\x05o6\x02\u040C\u040D\x05\xE3p\x02\u040D" + - "\u040E\x05\xD3h\x02\u040E\u040F\x05\xD1g\x02\u040F\u0410\x05\xDBl\x02" + - "\u0410\u0411\x05\xCBd\x02\u0411\u0412\x05\xE5q\x02\u0412\u04C6\x03\x02" + - "\x02\x02\u0413\u0414\x05\xE3p\x02\u0414\u0415\x05\xF5y\x02\u0415\u0416" + - "\x05o6\x02\u0416\u0417\x05\xD1g\x02\u0417\u0418\x05\xD3h\x02\u0418\u0419" + - "\x05\xD1g\x02\u0419\u041A\x05\xF3x\x02\u041A\u041B\x05\xE9s\x02\u041B" + - "\u041C\x05\xD3h\x02\u041C\u04C6\x03\x02\x02\x02\u041D\u041E\x05\xE3p\x02" + - "\u041E\u041F\x05\xD3h\x02\u041F\u0420\x05\xF1w\x02\u0420\u0421\x05\xCB" + - "d\x02\u0421\u0422\x05\xD1g\x02\u0422\u0423\x05\xCBd\x02\u0423\u0424\x05" + - "\xF1w\x02\u0424\u0425\x05\xCBd\x02\u0425\u04C6\x03\x02\x02\x02\u0426\u0427" + - "\x05\xEFv\x02\u0427\u0428\x05\xE9s\x02\u0428\u0429\x05\xE1o\x02\u0429" + - "\u042A\x05\xDBl\x02\u042A\u042B\x05\xF1w\x02\u042B\u04C6\x03\x02\x02\x02" + - "\u042C\u042D\x05\xF1w\x02\u042D\u042E\x05\xE7r\x02\u042E\u042F\x05o6\x02" + - "\u042F\u0430\x05\xEFv\x02\u0430\u0431\x05\xF1w\x02\u0431\u0432\x05\xED" + - "u\x02\u0432\u0433\x05\xDBl\x02\u0433\u0434\x05\xE5q\x02\u0434\u0435\x05" + - "\xD7j\x02\u0435\u04C6\x03\x02\x02\x02\u0436\u0437\x05\xF1w\x02\u0437\u0438" + - "\x05\xE7r\x02\u0438\u0439\x05o6\x02\u0439\u043A\x05\xEFv\x02\u043A\u043B" + - "\x05\xF1w\x02\u043B\u043C\x05\xEDu\x02\u043C\u04C6\x03\x02\x02\x02\u043D" + - "\u043E\x05\xF1w\x02\u043E\u043F\x05\xE7r\x02\u043F\u0440\x05o6\x02\u0440" + - "\u0441\x05\xCDe\x02\u0441\u0442\x05\xE7r\x02\u0442\u0443\x05\xE7r\x02" + - "\u0443\u0444\x05\xE1o\x02\u0444\u04C6\x03\x02\x02\x02\u0445\u0446\x05" + - "\xF1w\x02\u0446\u0447\x05\xE7r\x02\u0447\u0448\x05o6\x02\u0448\u0449\x05" + - "\xCDe\x02\u0449\u044A\x05\xE7r\x02\u044A\u044B\x05\xE7r\x02\u044B\u044C" + - "\x05\xE1o\x02\u044C\u044D\x05\xD3h\x02\u044D\u044E\x05\xCBd\x02\u044E" + - "\u044F\x05\xE5q\x02\u044F\u04C6\x03\x02\x02\x02\u0450\u0451\x05\xF1w\x02" + - "\u0451\u0452\x05\xE7r\x02\u0452\u0453\x05o6\x02\u0453\u0454\x05\xD1g\x02" + - "\u0454\u0455\x05\xCBd\x02\u0455\u0456\x05\xF1w\x02\u0456\u0457\x05\xD3" + - "h\x02\u0457\u0458\x05\xF1w\x02\u0458\u0459\x05\xDBl\x02\u0459\u045A\x05" + - "\xE3p\x02\u045A\u045B\x05\xD3h\x02\u045B\u04C6\x03\x02\x02\x02\u045C\u045D" + - "\x05\xF1w\x02\u045D\u045E\x05\xE7r\x02\u045E\u045F\x05o6\x02\u045F\u0460" + - "\x05\xD1g\x02\u0460\u0461\x05\xF1w\x02\u0461\u04C6\x03\x02\x02\x02\u0462" + - "\u0463\x05\xF1w\x02\u0463\u0464\x05\xE7r\x02\u0464\u0465\x05o6\x02\u0465" + - "\u0466\x05\xD1g\x02\u0466\u0467\x05\xCDe\x02\u0467\u0468\x05\xE1o\x02" + - "\u0468\u04C6\x03\x02\x02\x02\u0469\u046A\x05\xF1w\x02\u046A\u046B\x05" + - "\xE7r\x02\u046B\u046C\x05o6\x02\u046C\u046D\x05\xD1g\x02\u046D\u046E\x05" + - "\xE7r\x02\u046E\u046F\x05\xF3x\x02\u046F\u0470\x05\xCDe\x02\u0470\u0471" + - "\x05\xE1o\x02\u0471\u0472\x05\xD3h\x02\u0472\u04C6\x03\x02\x02\x02\u0473" + - "\u0474\x05\xF1w\x02\u0474\u0475\x05\xE7r\x02\u0475\u0476\x05o6\x02\u0476" + - "\u0477\x05\xD1g\x02\u0477\u0478\x05\xD3h\x02\u0478\u0479\x05\xD7j\x02" + - "\u0479\u047A\x05\xEDu\x02\u047A\u047B\x05\xD3h\x02\u047B\u047C\x05\xD3" + - "h\x02\u047C\u047D\x05\xEFv\x02\u047D\u04C6\x03\x02\x02\x02\u047E\u047F" + - "\x05\xF1w\x02\u047F\u0480\x05\xE7r\x02\u0480\u0481\x05o6\x02\u0481\u0482" + - "\x05\xDBl\x02\u0482\u0483\x05\xE5q\x02\u0483\u0484\x05\xF1w\x02\u0484" + - "\u04C6\x03\x02\x02\x02\u0485\u0486\x05\xF1w\x02\u0486\u0487\x05\xE7r\x02" + - "\u0487\u0488\x05o6\x02\u0488\u0489\x05\xDBl\x02\u0489\u048A\x05\xE5q\x02" + - "\u048A\u048B\x05\xF1w\x02\u048B\u048C\x05\xD3h\x02\u048C\u048D\x05\xD7" + - "j\x02\u048D\u048E\x05\xD3h\x02\u048E\u048F\x05\xEDu\x02\u048F\u04C6\x03" + - "\x02\x02\x02\u0490\u0491\x05\xF1w\x02\u0491\u0492\x05\xE7r\x02\u0492\u0493" + - "\x05o6\x02\u0493\u0494\x05\xDBl\x02\u0494\u0495\x05\xE9s\x02\u0495\u04C6" + - "\x03\x02\x02\x02\u0496\u0497\x05\xF1w\x02\u0497\u0498\x05\xE7r\x02\u0498" + - "\u0499\x05o6\x02\u0499\u049A\x05\xE1o\x02\u049A\u049B\x05\xE7r\x02\u049B" + - "\u049C\x05\xE5q\x02\u049C\u049D\x05\xD7j\x02\u049D\u04C6\x03\x02\x02\x02" + - "\u049E\u049F\x05\xF1w\x02\u049F\u04A0\x05\xE7r\x02\u04A0\u04A1\x05o6\x02" + - "\u04A1\u04A2\x05\xEDu\x02\u04A2\u04A3\x05\xCBd\x02\u04A3\u04A4\x05\xD1" + - "g\x02\u04A4\u04A5\x05\xDBl\x02\u04A5\u04A6\x05\xCBd\x02\u04A6\u04A7\x05" + - "\xE5q\x02\u04A7\u04A8\x05\xEFv\x02\u04A8\u04C6\x03\x02\x02\x02\u04A9\u04AA" + - "\x05\xF1w\x02\u04AA\u04AB\x05\xE7r\x02\u04AB\u04AC\x05o6\x02\u04AC\u04AD" + - "\x05\xF5y\x02\u04AD\u04AE\x05\xD3h\x02\u04AE\u04AF\x05\xEDu\x02\u04AF" + - "\u04B0\x05\xEFv\x02\u04B0\u04B1\x05\xDBl\x02\u04B1\u04B2\x05\xE7r\x02" + - "\u04B2\u04B3\x05\xE5q\x02\u04B3\u04C6\x03\x02\x02\x02\u04B4\u04B5\x05" + - "\xF1w\x02\u04B5\u04B6\x05\xE7r\x02\u04B6\u04B7\x05o6\x02\u04B7\u04B8\x05" + - "\xF3x\x02\u04B8\u04B9\x05\xE5q\x02\u04B9\u04BA\x05\xEFv\x02\u04BA\u04BB" + - "\x05\xDBl\x02\u04BB\u04BC\x05\xD7j\x02\u04BC\u04BD\x05\xE5q\x02\u04BD" + - "\u04BE\x05\xD3h\x02\u04BE\u04BF\x05\xD1g\x02\u04BF\u04C0\x05o6\x02\u04C0" + - "\u04C1\x05\xE1o\x02\u04C1\u04C2\x05\xE7r\x02\u04C2\u04C3\x05\xE5q\x02" + - "\u04C3\u04C4\x05\xD7j\x02\u04C4\u04C6\x03\x02\x02\x02\u04C5\u0317\x03" + - "\x02\x02\x02\u04C5\u031D\x03\x02\x02\x02\u04C5\u0321\x03\x02\x02\x02\u04C5" + - "\u0325\x03\x02\x02\x02\u04C5\u032A\x03\x02\x02\x02\u04C5\u032D\x03\x02" + - "\x02\x02\u04C5\u0331\x03\x02\x02\x02\u04C5\u0332\x03\x02\x02\x02\u04C5" + - "\u033C\x03\x02\x02\x02\u04C5\u0341\x03\x02\x02\x02\u04C5\u0348\x03\x02" + - "\x02\x02\u04C5\u0351\x03\x02\x02\x02\u04C5\u035A\x03\x02\x02\x02\u04C5" + - "\u035F\x03\x02\x02\x02\u04C5\u0363\x03\x02\x02\x02\u04C5\u0369\x03\x02" + - "\x02\x02\u04C5\u0375\x03\x02\x02\x02\u04C5\u0381\x03\x02\x02\x02\u04C5" + - "\u038C\x03\x02\x02\x02\u04C5\u0397\x03\x02\x02\x02\u04C5\u03A3\x03\x02" + - "\x02\x02\u04C5\u03B0\x03\x02\x02\x02\u04C5\u03BA\x03\x02\x02\x02\u04C5" + - "\u03C6\x03\x02\x02\x02\u04C5\u03CB\x03\x02\x02\x02\u04C5\u03D2\x03\x02" + - "\x02\x02\u04C5\u03D9\x03\x02\x02\x02\u04C5\u03E0\x03\x02\x02\x02\u04C5" + - "\u03E7\x03\x02\x02\x02\u04C5\u03EE\x03\x02\x02\x02\u04C5\u03F7\x03\x02" + - "\x02\x02\u04C5\u0401\x03\x02\x02\x02\u04C5\u0409\x03\x02\x02\x02\u04C5" + - "\u0413\x03\x02\x02\x02\u04C5\u041D\x03\x02\x02\x02\u04C5\u0426\x03\x02" + - "\x02\x02\u04C5\u042C\x03\x02\x02\x02\u04C5\u0436\x03\x02\x02\x02\u04C5" + - "\u043D\x03\x02\x02\x02\u04C5\u0445\x03\x02\x02\x02\u04C5\u0450\x03\x02" + - "\x02\x02\u04C5\u045C\x03\x02\x02\x02\u04C5\u0462\x03\x02\x02\x02\u04C5" + - "\u0469\x03\x02\x02\x02\u04C5\u0473\x03\x02\x02\x02\u04C5\u047E\x03\x02" + - "\x02\x02\u04C5\u0485\x03\x02\x02\x02\u04C5\u0490\x03\x02\x02\x02\u04C5" + - "\u0496\x03\x02\x02\x02\u04C5\u049E\x03\x02\x02\x02\u04C5\u04A9\x03\x02" + - "\x02\x02\u04C5\u04B4\x03\x02\x02\x02\u04C6\x8C\x03\x02\x02\x02\u04C7\u04C8" + - "\x05\xCBd\x02\u04C8\u04C9\x05\xF5y\x02\u04C9\u04CA\x05\xD7j\x02\u04CA" + - "\u055F\x03\x02\x02\x02\u04CB\u04CC\x05\xE3p\x02\u04CC\u04CD\x05\xDBl\x02" + - "\u04CD\u04CE\x05\xE5q\x02\u04CE\u055F\x03\x02\x02\x02\u04CF\u04D0\x05" + - "\xE3p\x02\u04D0\u04D1\x05\xCBd\x02\u04D1\u04D2\x05\xF9{\x02\u04D2\u055F" + - "\x03\x02\x02\x02\u04D3\u04D4\x05\xEFv\x02\u04D4\u04D5\x05\xF3x\x02\u04D5" + - "\u04D6\x05\xE3p\x02\u04D6\u055F\x03\x02\x02\x02\u04D7\u04D8\x05\xCFf\x02" + - "\u04D8\u04D9\x05\xE7r\x02\u04D9\u04DA\x05\xF3x\x02\u04DA\u04DB\x05\xE5" + - "q\x02\u04DB\u04DC\x05\xF1w\x02\u04DC\u055F\x03\x02\x02\x02\u04DD\u04DE" + - "\x05\xCFf\x02\u04DE\u04DF\x05\xE7r\x02\u04DF\u04E0\x05\xF3x\x02\u04E0" + - "\u04E1\x05\xE5q\x02\u04E1\u04E2\x05\xF1w\x02\u04E2\u04E3\x05o6\x02\u04E3" + - "\u04E4\x05\xD1g\x02\u04E4\u04E5\x05\xDBl\x02\u04E5\u04E6\x05\xEFv\x02" + - "\u04E6\u04E7\x05\xF1w\x02\u04E7\u04E8\x05\xDBl\x02\u04E8\u04E9\x05\xE5" + - "q\x02\u04E9\u04EA\x05\xCFf\x02\u04EA\u04EB\x05\xF1w\x02\u04EB\u055F\x03" + - "\x02\x02\x02\u04EC\u04ED\x05\xE9s\x02\u04ED\u04EE\x05\xD3h\x02\u04EE\u04EF" + - "\x05\xEDu\x02\u04EF\u04F0\x05\xCFf\x02\u04F0\u04F1\x05\xD3h\x02\u04F1" + - "\u04F2\x05\xE5q\x02\u04F2\u04F3\x05\xF1w\x02\u04F3\u04F4\x05\xDBl\x02" + - "\u04F4\u04F5\x05\xE1o\x02\u04F5\u04F6\x05\xD3h\x02\u04F6\u055F\x03\x02" + - "\x02\x02\u04F7\u04F8\x05\xE3p\x02\u04F8\u04F9\x05\xD3h\x02\u04F9\u04FA" + - "\x05\xD1g\x02\u04FA\u04FB\x05\xDBl\x02\u04FB\u04FC\x05\xCBd\x02\u04FC" + - "\u04FD\x05\xE5q\x02\u04FD\u055F\x03\x02\x02\x02\u04FE\u04FF\x05\xE3p\x02" + - "\u04FF\u0500\x05\xD3h\x02\u0500\u0501\x05\xD1g\x02\u0501\u0502\x05\xDB" + - "l\x02\u0502\u0503\x05\xCBd\x02\u0503\u0504\x05\xE5q\x02\u0504\u0505\x05" + - "o6\x02\u0505\u0506\x05\xCBd\x02\u0506\u0507\x05\xCDe\x02\u0507\u0508\x05" + - "\xEFv\x02\u0508\u0509\x05\xE7r\x02\u0509\u050A\x05\xE1o\x02\u050A\u050B" + - "\x05\xF3x\x02\u050B\u050C\x05\xF1w\x02\u050C\u050D\x05\xD3h\x02\u050D" + - "\u050E\x05o6\x02\u050E\u050F\x05\xD1g\x02\u050F\u0510\x05\xD3h\x02\u0510" + - "\u0511\x05\xF5y\x02\u0511\u0512\x05\xDBl\x02\u0512\u0513\x05\xCBd\x02" + - "\u0513\u0514\x05\xF1w\x02\u0514\u0515\x05\xDBl\x02\u0515\u0516\x05\xE7" + - "r\x02\u0516\u0517\x05\xE5q\x02\u0517\u055F\x03\x02\x02\x02\u0518\u0519" + - "\x05\xCBd\x02\u0519\u051A\x05\xCFf\x02\u051A\u051B\x05\xE7r\x02\u051B" + - "\u051C\x05\xEFv\x02\u051C\u055F\x03\x02\x02\x02\u051D\u051E\x05\xCBd\x02" + - "\u051E\u051F\x05\xEFv\x02\u051F\u0520\x05\xDBl\x02\u0520\u0521\x05\xE5" + - "q\x02\u0521\u055F\x03\x02\x02\x02\u0522\u0523\x05\xCBd\x02\u0523\u0524" + - "\x05\xF1w\x02\u0524\u0525\x05\xCBd\x02\u0525\u0526\x05\xE5q\x02\u0526" + - "\u055F\x03\x02\x02\x02\u0527\u0528\x05\xCBd\x02\u0528\u0529\x05\xF1w\x02" + - "\u0529\u052A\x05\xCBd\x02\u052A\u052B\x05\xE5q\x02\u052B\u052C\x074\x02" + - "\x02\u052C\u055F\x03\x02\x02\x02\u052D\u052E\x05\xCFf\x02\u052E\u052F" + - "\x05\xD3h\x02\u052F\u0530\x05\xDBl\x02\u0530\u0531\x05\xE1o\x02\u0531" + - "\u055F\x03\x02\x02\x02\u0532\u0533\x05\xCFf\x02\u0533\u0534\x05\xE7r\x02" + - "\u0534\u0535\x05\xEFv\x02\u0535\u055F\x03\x02\x02\x02\u0536\u0537\x05" + - "\xCFf\x02\u0537\u0538\x05\xE7r\x02\u0538\u0539\x05\xEFv\x02\u0539\u053A" + - "\x05\xD9k\x02\u053A\u055F\x03\x02\x02\x02\u053B\u053C\x05\xD5i\x02\u053C" + - "\u053D\x05\xE1o\x02\u053D\u053E\x05\xE7r\x02\u053E\u053F\x05\xE7r\x02" + - "\u053F\u0540\x05\xEDu\x02\u0540\u055F\x03\x02\x02\x02\u0541\u0542\x05" + - "\xE1o\x02\u0542\u0543\x05\xF1w\x02\u0543\u0544\x05\xEDu\x02\u0544\u0545" + - "\x05\xDBl\x02\u0545\u0546\x05\xE3p\x02\u0546\u055F\x03\x02\x02\x02\u0547" + - "\u0548\x05\xEFv\x02\u0548\u0549\x05\xDBl\x02\u0549\u054A\x05\xE5q\x02" + - "\u054A\u055F\x03\x02\x02\x02\u054B\u054C\x05\xEFv\x02\u054C\u054D\x05" + - "\xDBl\x02\u054D\u054E\x05\xE5q\x02\u054E\u054F\x05\xD9k\x02\u054F\u055F" + - "\x03\x02\x02\x02\u0550\u0551\x05\xEFv\x02\u0551\u0552\x05\xEBt\x02\u0552" + - "\u0553\x05\xEDu\x02\u0553\u0554\x05\xF1w\x02\u0554\u055F\x03\x02\x02\x02" + - "\u0555\u0556\x05\xF1w\x02\u0556\u0557\x05\xCBd\x02\u0557\u0558\x05\xE5" + - "q\x02\u0558\u055F\x03\x02\x02\x02\u0559\u055A\x05\xF1w\x02\u055A\u055B" + - "\x05\xCBd\x02\u055B\u055C\x05\xE5q\x02\u055C\u055D\x05\xD9k\x02\u055D" + - "\u055F\x03\x02\x02\x02\u055E\u04C7\x03\x02\x02\x02\u055E\u04CB\x03\x02" + - "\x02\x02\u055E\u04CF\x03\x02\x02\x02\u055E\u04D3\x03\x02\x02\x02\u055E" + - "\u04D7\x03\x02\x02\x02\u055E\u04DD\x03\x02\x02\x02\u055E\u04EC\x03\x02" + - "\x02\x02\u055E\u04F7\x03\x02\x02\x02\u055E\u04FE\x03\x02\x02\x02\u055E" + - "\u0518\x03\x02\x02\x02\u055E\u051D\x03\x02\x02\x02\u055E\u0522\x03\x02" + - "\x02\x02\u055E\u0527\x03\x02\x02\x02\u055E\u052D\x03\x02\x02\x02\u055E" + - "\u0532\x03\x02\x02\x02\u055E\u0536\x03\x02\x02\x02\u055E\u053B\x03\x02" + - "\x02\x02\u055E\u0541\x03\x02\x02\x02\u055E\u0547\x03\x02\x02\x02\u055E" + - "\u054B\x03\x02\x02\x02\u055E\u0550\x03\x02\x02\x02\u055E\u0555\x03\x02" + - "\x02\x02\u055E\u0559\x03\x02\x02\x02\u055F\x8E\x03\x02\x02\x02\u0560\u0561" + - "\x05\xCFf\x02\u0561\u0562\x05\xDBl\x02\u0562\u0563\x05\xD1g\x02\u0563" + - "\u0564\x05\xEDu\x02\u0564\u0565\x05o6\x02\u0565\u0566\x05\xE3p\x02\u0566" + - "\u0567\x05\xCBd\x02\u0567\u0568\x05\xF1w\x02\u0568\u0569\x05\xCFf\x02" + - "\u0569\u056A\x05\xD9k\x02\u056A\x90\x03\x02\x02\x02\u056B\u0572\x05=\x1D" + - "\x02\u056C\u0571\x05=\x1D\x02\u056D\u0571\x05;\x1C\x02\u056E\u0571\x07" + - "a\x02\x02\u056F\u0571\x05}=\x02\u0570\u056C\x03\x02\x02\x02\u0570\u056D" + - "\x03\x02\x02\x02\u0570\u056E\x03\x02\x02\x02\u0570\u056F\x03\x02\x02\x02" + - "\u0571\u0574\x03\x02\x02\x02\u0572\u0570\x03\x02\x02\x02\u0572\u0573\x03" + - "\x02\x02\x02\u0573\u057F\x03\x02\x02\x02\u0574\u0572\x03\x02\x02\x02\u0575" + - "\u057A\t\n\x02\x02\u0576\u057B\x05=\x1D\x02\u0577\u057B\x05;\x1C\x02\u0578" + - "\u057B\x07a\x02\x02\u0579\u057B\x05}=\x02\u057A\u0576\x03\x02\x02\x02" + - "\u057A\u0577\x03\x02\x02\x02\u057A\u0578\x03\x02\x02\x02\u057A\u0579\x03" + - "\x02\x02\x02\u057B\u057C\x03\x02\x02\x02\u057C\u057A\x03\x02\x02\x02\u057C" + - "\u057D\x03\x02\x02\x02\u057D\u057F\x03\x02\x02\x02\u057E\u056B\x03\x02" + - "\x02\x02\u057E\u0575\x03\x02\x02\x02\u057F\x92\x03\x02\x02\x02\u0580\u0586" + - "\x07b\x02\x02\u0581\u0585\n\v\x02\x02\u0582\u0583\x07b\x02\x02\u0583\u0585" + - "\x07b\x02\x02\u0584\u0581\x03\x02\x02\x02\u0584\u0582\x03\x02\x02\x02" + - "\u0585\u0588\x03\x02\x02\x02\u0586\u0584\x03\x02\x02\x02\u0586\u0587\x03" + - "\x02\x02\x02\u0587\u0589\x03\x02\x02\x02\u0588\u0586\x03\x02\x02\x02\u0589" + - "\u058A\x07b\x02\x02\u058A\x94\x03\x02\x02\x02\u058B\u058C\x05)\x13\x02" + - "\u058C\u058D\x03\x02\x02\x02\u058D\u058E\bI\x06\x02\u058E\x96\x03\x02" + - "\x02\x02\u058F\u0590\x05+\x14\x02\u0590\u0591\x03\x02\x02\x02\u0591\u0592" + - "\bJ\x06\x02\u0592\x98\x03\x02\x02\x02\u0593\u0594\x05-\x15\x02\u0594\u0595" + - "\x03\x02\x02\x02\u0595\u0596\bK\x06\x02\u0596\x9A\x03\x02\x02\x02\u0597" + - "\u0598\x07~\x02\x02\u0598\u0599\x03\x02\x02\x02\u0599\u059A\bL\t\x02\u059A" + - "\u059B\bL\n\x02\u059B\x9C\x03\x02\x02\x02\u059C\u059D\x07]\x02\x02\u059D" + - "\u059E\x03\x02\x02\x02\u059E\u059F\bM\x07\x02\u059F\u05A0\bM\x04\x02\u05A0" + - "\u05A1\bM\x04\x02\u05A1\x9E\x03\x02\x02\x02\u05A2\u05A3\x07_\x02\x02\u05A3" + - "\u05A4\x03\x02\x02\x02\u05A4\u05A5\bN\n\x02\u05A5\u05A6\bN\n\x02\u05A6" + - "\u05A7\bN\v\x02\u05A7\xA0\x03\x02\x02\x02\u05A8\u05A9\x07.\x02\x02\u05A9" + - "\u05AA\x03\x02\x02\x02\u05AA\u05AB\bO\f\x02\u05AB\xA2\x03\x02\x02\x02" + - "\u05AC\u05AD\x07?\x02\x02\u05AD\u05AE\x03\x02\x02\x02\u05AE\u05AF\bP\r" + - "\x02\u05AF\xA4\x03\x02\x02\x02\u05B0\u05B1\x05\xE3p\x02\u05B1\u05B2\x05" + - "\xD3h\x02\u05B2\u05B3\x05\xF1w\x02\u05B3\u05B4\x05\xCBd\x02\u05B4\u05B5" + - "\x05\xD1g\x02\u05B5\u05B6\x05\xCBd\x02\u05B6\u05B7\x05\xF1w\x02\u05B7" + - "\u05B8\x05\xCBd\x02\u05B8\xA6\x03\x02\x02\x02\u05B9\u05BB\x05\xA9S\x02" + - "\u05BA\u05B9\x03\x02\x02\x02\u05BB\u05BC\x03\x02\x02\x02\u05BC\u05BA\x03" + - "\x02\x02\x02\u05BC\u05BD\x03\x02\x02\x02\u05BD\xA8\x03\x02\x02\x02\u05BE" + - "\u05C0\n\f\x02\x02\u05BF\u05BE\x03\x02\x02\x02\u05C0\u05C1\x03\x02\x02" + - "\x02\u05C1\u05BF\x03\x02\x02\x02\u05C1\u05C2\x03\x02\x02\x02\u05C2\u05C6" + - "\x03\x02\x02\x02\u05C3\u05C4\x071\x02\x02\u05C4\u05C6\n\r\x02\x02\u05C5" + - "\u05BF\x03\x02\x02\x02\u05C5\u05C3\x03\x02\x02\x02\u05C6\xAA\x03\x02\x02" + - "\x02\u05C7\u05C8\x05\x93H\x02\u05C8\xAC\x03\x02\x02\x02\u05C9\u05CA\x05" + - ")\x13\x02\u05CA\u05CB\x03\x02\x02\x02\u05CB\u05CC\bU\x06\x02\u05CC\xAE" + - "\x03\x02\x02\x02\u05CD\u05CE\x05+\x14\x02\u05CE\u05CF\x03\x02\x02\x02" + - "\u05CF\u05D0\bV\x06\x02\u05D0\xB0\x03\x02\x02\x02\u05D1\u05D2\x05-\x15" + - "\x02\u05D2\u05D3\x03\x02\x02\x02\u05D3\u05D4\bW\x06\x02\u05D4\xB2\x03" + - "\x02\x02\x02\u05D5\u05D6\x05\xE7r\x02\u05D6\u05D7\x05\xE5q\x02\u05D7\xB4" + - "\x03\x02\x02\x02\u05D8\u05D9\x05\xF7z\x02\u05D9\u05DA\x05\xDBl\x02\u05DA" + - "\u05DB\x05\xF1w\x02\u05DB\u05DC\x05\xD9k\x02\u05DC\xB6\x03\x02\x02\x02" + - "\u05DD\u05DE\x07~\x02\x02\u05DE\u05DF\x03\x02\x02\x02\u05DF\u05E0\bZ\t" + - "\x02\u05E0\u05E1\bZ\n\x02\u05E1\xB8\x03\x02\x02\x02\u05E2\u05E3\x07_\x02" + - "\x02\u05E3\u05E4\x03\x02\x02\x02\u05E4\u05E5\b[\n\x02\u05E5\u05E6\b[\n" + - "\x02\u05E6\u05E7\b[\v\x02\u05E7\xBA\x03\x02\x02\x02\u05E8\u05E9\x07.\x02" + - "\x02\u05E9\u05EA\x03\x02\x02\x02\u05EA\u05EB\b\\\f\x02\u05EB\xBC\x03\x02" + - "\x02\x02\u05EC\u05ED\x07?\x02\x02\u05ED\u05EE\x03\x02\x02\x02\u05EE\u05EF" + - "\b]\r\x02\u05EF\xBE\x03\x02\x02\x02\u05F0\u05F2\x05\xC1_\x02\u05F1\u05F0" + - "\x03\x02\x02\x02\u05F2\u05F3\x03\x02\x02\x02\u05F3\u05F1\x03\x02\x02\x02" + - "\u05F3\u05F4\x03\x02\x02\x02\u05F4\xC0\x03\x02\x02\x02\u05F5\u05F7\n\f" + - "\x02\x02\u05F6\u05F5\x03\x02\x02\x02\u05F7\u05F8\x03\x02\x02\x02\u05F8" + - "\u05F6\x03\x02\x02\x02\u05F8\u05F9\x03\x02\x02\x02\u05F9\u05FD\x03\x02" + - "\x02\x02\u05FA\u05FB\x071\x02\x02\u05FB\u05FD\n\r\x02\x02\u05FC\u05F6" + - "\x03\x02\x02\x02\u05FC\u05FA\x03\x02\x02\x02\u05FD\xC2\x03\x02\x02\x02" + - "\u05FE\u05FF\x05\x93H\x02\u05FF\xC4\x03\x02\x02\x02\u0600\u0601\x05)\x13" + - "\x02\u0601\u0602\x03\x02\x02\x02\u0602\u0603\ba\x06\x02\u0603\xC6\x03" + - "\x02\x02\x02\u0604\u0605\x05+\x14\x02\u0605\u0606\x03\x02\x02\x02\u0606" + - "\u0607\bb\x06\x02\u0607\xC8\x03\x02\x02\x02\u0608\u0609\x05-\x15\x02\u0609" + - "\u060A\x03\x02\x02\x02\u060A\u060B\bc\x06\x02\u060B\xCA\x03\x02\x02\x02" + - "\u060C\u060D\t\x0E\x02\x02\u060D\xCC\x03\x02\x02\x02\u060E\u060F\t\x0F" + - "\x02\x02\u060F\xCE\x03\x02\x02\x02\u0610\u0611\t\x10\x02\x02\u0611\xD0" + - "\x03\x02\x02\x02\u0612\u0613\t\x11\x02\x02\u0613\xD2\x03\x02\x02\x02\u0614" + - "\u0615\t\b\x02\x02\u0615\xD4\x03\x02\x02\x02\u0616\u0617\t\x12\x02\x02" + - "\u0617\xD6\x03\x02\x02\x02\u0618\u0619\t\x13\x02\x02\u0619\xD8\x03\x02" + - "\x02\x02\u061A\u061B\t\x14\x02\x02\u061B\xDA\x03\x02\x02\x02\u061C\u061D" + - "\t\x15\x02\x02\u061D\xDC\x03\x02\x02\x02\u061E\u061F\t\x16\x02\x02\u061F" + - "\xDE\x03\x02\x02\x02\u0620\u0621\t\x17\x02\x02\u0621\xE0\x03\x02\x02\x02" + - "\u0622\u0623\t\x18\x02\x02\u0623\xE2\x03\x02\x02\x02\u0624\u0625\t\x19" + - "\x02\x02\u0625\xE4\x03\x02\x02\x02\u0626\u0627\t\x1A\x02\x02\u0627\xE6" + - "\x03\x02\x02\x02\u0628\u0629\t\x1B\x02\x02\u0629\xE8\x03\x02\x02\x02\u062A" + - "\u062B\t\x1C\x02\x02\u062B\xEA\x03\x02\x02\x02\u062C\u062D\t\x1D\x02\x02" + - "\u062D\xEC\x03\x02\x02\x02\u062E\u062F\t\x1E\x02\x02\u062F\xEE\x03\x02" + - "\x02\x02\u0630\u0631\t\x1F\x02\x02\u0631\xF0\x03\x02\x02\x02\u0632\u0633" + - "\t \x02\x02\u0633\xF2\x03\x02\x02\x02\u0634\u0635\t!\x02\x02\u0635\xF4" + - "\x03\x02\x02\x02\u0636\u0637\t\"\x02\x02\u0637\xF6\x03\x02\x02\x02\u0638" + - "\u0639\t#\x02\x02\u0639\xF8\x03\x02\x02\x02\u063A\u063B\t$\x02\x02\u063B" + - "\xFA\x03\x02\x02\x02\u063C\u063D\t%\x02\x02\u063D\xFC\x03\x02\x02\x02" + - "\u063E\u063F\t&\x02\x02\u063F\xFE\x03\x02\x02\x022\x02\x03\x04\x05\x06" + - "\u0190\u0194\u0197\u01A0\u01A2\u01AD\u01D6\u01DB\u01E0\u01E2\u01ED\u01F5" + - "\u01F8\u01FA\u01FF\u0204\u020A\u0211\u0216\u021C\u021F\u0227\u022B\u028E" + - "\u02E2\u02EE\u0304\u0315\u04C5\u055E\u0570\u0572\u057A\u057C\u057E\u0584" + - "\u0586\u05BC\u05C1\u05C5\u05F3\u05F8\u05FC\x0E\x07\x04\x02\x07\x03\x02" + - "\x07\x05\x02\x07\x06\x02\x02\x03\x02\t%\x02\x07\x02\x02\t\x1A\x02\x06" + - "\x02\x02\t&\x02\t\"\x02\t!\x02"; + "\u01D7\u01D6\x03\x02\x02\x02\u01D8\u01DB\x03\x02\x02\x02\u01D9\u01D7\x03" + + "\x02\x02\x02\u01D9\u01DA\x03\x02\x02\x02\u01DA\u01FB\x03\x02\x02\x02\u01DB" + + "\u01D9\x03\x02\x02\x02\u01DC\u01DE\x05K%\x02\u01DD\u01DF\x05/\x17\x02" + + "\u01DE\u01DD\x03\x02\x02\x02\u01DF\u01E0\x03\x02\x02\x02\u01E0\u01DE\x03" + + "\x02\x02\x02\u01E0\u01E1\x03\x02\x02\x02\u01E1\u01FB\x03\x02\x02\x02\u01E2" + + "\u01E4\x05/\x17\x02\u01E3\u01E2\x03\x02\x02\x02\u01E4\u01E5\x03\x02\x02" + + "\x02\u01E5\u01E3\x03\x02\x02\x02\u01E5\u01E6\x03\x02\x02\x02\u01E6\u01EE" + + "\x03\x02\x02\x02\u01E7\u01EB\x05K%\x02\u01E8\u01EA\x05/\x17\x02\u01E9" + + "\u01E8\x03\x02\x02\x02\u01EA\u01ED\x03\x02\x02\x02\u01EB\u01E9\x03\x02" + + "\x02\x02\u01EB\u01EC\x03\x02\x02\x02\u01EC\u01EF\x03\x02\x02\x02\u01ED" + + "\u01EB\x03\x02\x02\x02\u01EE\u01E7\x03\x02\x02\x02\u01EE\u01EF\x03\x02" + + "\x02\x02\u01EF\u01F0\x03\x02\x02\x02\u01F0\u01F1\x057\x1B\x02\u01F1\u01FB" + + "\x03\x02\x02\x02\u01F2\u01F4\x05K%\x02\u01F3\u01F5\x05/\x17\x02\u01F4" + + "\u01F3\x03\x02\x02\x02\u01F5\u01F6\x03\x02\x02\x02\u01F6\u01F4\x03\x02" + + "\x02\x02\u01F6\u01F7\x03\x02\x02\x02\u01F7\u01F8\x03\x02\x02\x02\u01F8" + + "\u01F9\x057\x1B\x02\u01F9\u01FB\x03\x02\x02\x02\u01FA\u01D1\x03\x02\x02" + + "\x02\u01FA\u01DC\x03\x02\x02\x02\u01FA\u01E3\x03\x02\x02\x02\u01FA\u01F2" + + "\x03\x02\x02\x02\u01FB>\x03\x02\x02\x02\u01FC\u01FD\x05\xB5Z\x02\u01FD" + + "\u01FE\x05\xE3q\x02\u01FE@\x03\x02\x02\x02\u01FF\u0200\x05\xB3Y\x02\u0200" + + "\u0201\x05\xCDf\x02\u0201\u0202\x05\xB9\\\x02\u0202B\x03\x02\x02\x02\u0203" + + "\u0204\x05\xB3Y\x02\u0204\u0205\x05\xD7k\x02\u0205\u0206\x05\xB7[\x02" + + "\u0206D\x03\x02\x02\x02\u0207\u0208\x07?\x02\x02\u0208F\x03\x02\x02\x02" + + "\u0209\u020A\x07.\x02\x02\u020AH\x03\x02\x02\x02\u020B\u020C\x05\xB9\\" + + "\x02\u020C\u020D\x05\xBB]\x02\u020D\u020E\x05\xD7k\x02\u020E\u020F\x05" + + "\xB7[\x02\u020FJ\x03\x02\x02\x02\u0210\u0211\x070\x02\x02\u0211L\x03\x02" + + "\x02\x02\u0212\u0213\x05\xBD^\x02\u0213\u0214\x05\xB3Y\x02\u0214\u0215" + + "\x05\xC9d\x02\u0215\u0216\x05\xD7k\x02\u0216\u0217\x05\xBB]\x02\u0217" + + "N\x03\x02\x02\x02\u0218\u0219\x05\xBD^\x02\u0219\u021A\x05\xC3a\x02\u021A" + + "\u021B\x05\xD5j\x02\u021B\u021C\x05\xD7k\x02\u021C\u021D\x05\xD9l\x02" + + "\u021DP\x03\x02\x02\x02\u021E\u021F\x05\xC9d\x02\u021F\u0220\x05\xB3Y" + + "\x02\u0220\u0221\x05\xD7k\x02\u0221\u0222\x05\xD9l\x02\u0222R\x03\x02" + + "\x02\x02\u0223\u0224\x07*\x02\x02\u0224T\x03\x02\x02\x02\u0225\u0226\x05" + + "\xC3a\x02\u0226\u0227\x05\xCDf\x02\u0227V\x03\x02\x02\x02\u0228\u0229" + + "\x05\xC3a\x02\u0229\u022A\x05\xD7k\x02\u022AX\x03\x02\x02\x02\u022B\u022C" + + "\x05\xC9d\x02\u022C\u022D\x05\xC3a\x02\u022D\u022E\x05\xC7c\x02\u022E" + + "\u022F\x05\xBB]\x02\u022FZ\x03\x02\x02\x02\u0230\u0231\x05\xCDf\x02\u0231" + + "\u0232\x05\xCFg\x02\u0232\u0233\x05\xD9l\x02\u0233\\\x03\x02\x02\x02\u0234" + + "\u0235\x05\xCDf\x02\u0235\u0236\x05\xDBm\x02\u0236\u0237\x05\xC9d\x02" + + "\u0237\u0238\x05\xC9d\x02\u0238^\x03\x02\x02\x02\u0239\u023A\x05\xCDf" + + "\x02\u023A\u023B\x05\xDBm\x02\u023B\u023C\x05\xC9d\x02\u023C\u023D\x05" + + "\xC9d\x02\u023D\u023E\x05\xD7k\x02\u023E`\x03\x02\x02\x02\u023F\u0240" + + "\x05\xCFg\x02\u0240\u0241\x05\xD5j\x02\u0241b\x03\x02\x02\x02\u0242\u0243" + + "\x07A\x02\x02\u0243d\x03\x02\x02\x02\u0244\u0245\x05\xD5j\x02\u0245\u0246" + + "\x05\xC9d\x02\u0246\u0247\x05\xC3a\x02\u0247\u0248\x05\xC7c\x02\u0248" + + "\u0249\x05\xBB]\x02\u0249f\x03\x02\x02\x02\u024A\u024B\x07+\x02\x02\u024B" + + "h\x03\x02\x02\x02\u024C\u024D\x05\xD9l\x02\u024D\u024E\x05\xD5j\x02\u024E" + + "\u024F\x05\xDBm\x02\u024F\u0250\x05\xBB]\x02\u0250j\x03\x02\x02\x02\u0251" + + "\u0252\x05\xC3a\x02\u0252\u0253\x05\xCDf\x02\u0253\u0254\x05\xBD^\x02" + + "\u0254\u0255\x05\xCFg\x02\u0255l\x03\x02\x02\x02\u0256\u0257\x05\xBD^" + + "\x02\u0257\u0258\x05\xDBm\x02\u0258\u0259\x05\xCDf\x02\u0259\u025A\x05" + + "\xB7[\x02\u025A\u025B\x05\xD9l\x02\u025B\u025C\x05\xC3a\x02\u025C\u025D" + + "\x05\xCFg\x02\u025D\u025E\x05\xCDf\x02\u025E\u025F\x05\xD7k\x02\u025F" + + "n\x03\x02\x02\x02\u0260\u0261\x07a\x02\x02\u0261p\x03\x02\x02\x02\u0262" + + "\u0263\x07?\x02\x02\u0263\u0264\x07?\x02\x02\u0264r\x03\x02\x02\x02\u0265" + + "\u0266\x07#\x02\x02\u0266\u0267\x07?\x02\x02\u0267t\x03\x02\x02\x02\u0268" + + "\u0269\x07>\x02\x02\u0269v\x03\x02\x02\x02\u026A\u026B\x07>\x02\x02\u026B" + + "\u026C\x07?\x02\x02\u026Cx\x03\x02\x02\x02\u026D\u026E\x07@\x02\x02\u026E" + + "z\x03\x02\x02\x02\u026F\u0270\x07@\x02\x02\u0270\u0271\x07?\x02\x02\u0271" + + "|\x03\x02\x02\x02\u0272\u0273\x07-\x02\x02\u0273~\x03\x02\x02\x02\u0274" + + "\u0275\x07/\x02\x02\u0275\x80\x03\x02\x02\x02\u0276\u0277\x07,\x02\x02" + + "\u0277\x82\x03\x02\x02\x02\u0278\u0279\x071\x02\x02\u0279\x84\x03\x02" + + "\x02\x02\u027A\u027B\x07\'\x02\x02\u027B\x86\x03\x02\x02\x02\u027C\u027D" + + "\x07]\x02\x02\u027D\u027E\x03\x02\x02\x02\u027E\u027F\bC\x02\x02\u027F" + + "\u0280\bC\x02\x02\u0280\x88\x03\x02\x02\x02\u0281\u0282\x07_\x02\x02\u0282" + + "\u0283\x03\x02\x02\x02\u0283\u0284\bD\x05\x02\u0284\u0285\bD\x05\x02\u0285" + + "\x8A\x03\x02\x02\x02\u0286\u028C\x051\x18\x02\u0287\u028B\x051\x18\x02" + + "\u0288\u028B\x05/\x17\x02\u0289\u028B\x07a\x02\x02\u028A\u0287\x03\x02" + + "\x02\x02\u028A\u0288\x03\x02\x02\x02\u028A\u0289\x03\x02\x02\x02\u028B" + + "\u028E\x03\x02\x02\x02\u028C\u028A\x03\x02\x02\x02\u028C\u028D\x03\x02" + + "\x02\x02\u028D\u0298\x03\x02\x02\x02\u028E\u028C\x03\x02\x02\x02\u028F" + + "\u0293\t\v\x02\x02\u0290\u0294\x051\x18\x02\u0291\u0294\x05/\x17\x02\u0292" + + "\u0294\x07a\x02\x02\u0293\u0290\x03\x02\x02\x02\u0293\u0291\x03\x02\x02" + + "\x02\u0293\u0292\x03\x02\x02\x02\u0294\u0295\x03\x02\x02\x02\u0295\u0293" + + "\x03\x02\x02\x02\u0295\u0296\x03\x02\x02\x02\u0296\u0298\x03\x02\x02\x02" + + "\u0297\u0286\x03\x02\x02\x02\u0297\u028F\x03\x02\x02\x02\u0298\x8C\x03" + + "\x02\x02\x02\u0299\u029F\x07b\x02\x02\u029A\u029E\n\f\x02\x02\u029B\u029C" + + "\x07b\x02\x02\u029C\u029E\x07b\x02\x02\u029D\u029A\x03\x02\x02\x02\u029D" + + "\u029B\x03\x02\x02\x02\u029E\u02A1\x03\x02\x02\x02\u029F\u029D\x03\x02" + + "\x02\x02\u029F\u02A0\x03\x02\x02\x02\u02A0\u02A2\x03\x02\x02\x02\u02A1" + + "\u029F\x03\x02\x02\x02\u02A2\u02A3\x07b\x02\x02\u02A3\x8E\x03\x02\x02" + + "\x02\u02A4\u02A5\x05\'\x13\x02\u02A5\u02A6\x03\x02\x02\x02\u02A6\u02A7" + + "\bG\x04\x02\u02A7\x90\x03\x02\x02\x02\u02A8\u02A9\x05)\x14\x02\u02A9\u02AA" + + "\x03\x02\x02\x02\u02AA\u02AB\bH\x04\x02\u02AB\x92\x03\x02\x02\x02\u02AC" + + "\u02AD\x05+\x15\x02\u02AD\u02AE\x03\x02\x02\x02\u02AE\u02AF\bI\x04\x02" + + "\u02AF\x94\x03\x02\x02\x02\u02B0\u02B1\x07~\x02\x02\u02B1\u02B2\x03\x02" + + "\x02\x02\u02B2\u02B3\bJ\x06\x02\u02B3\u02B4\bJ\x05\x02\u02B4\x96\x03\x02" + + "\x02\x02\u02B5\u02B6\x07]\x02\x02\u02B6\u02B7\x03\x02\x02\x02\u02B7\u02B8" + + "\bK\x07\x02\u02B8\u02B9\bK\x03\x02\u02B9\u02BA\bK\x03\x02\u02BA\x98\x03" + + "\x02\x02\x02\u02BB\u02BC\x07_\x02\x02\u02BC\u02BD\x03\x02\x02\x02\u02BD" + + "\u02BE\bL\x05\x02\u02BE\u02BF\bL\x05\x02\u02BF\u02C0\bL\b\x02\u02C0\x9A" + + "\x03\x02\x02\x02\u02C1\u02C2\x07.\x02\x02\u02C2\u02C3\x03\x02\x02\x02" + + "\u02C3\u02C4\bM\t\x02\u02C4\x9C\x03\x02\x02\x02\u02C5\u02C6\x07?\x02\x02" + + "\u02C6\u02C7\x03\x02\x02\x02\u02C7\u02C8\bN\n\x02\u02C8\x9E\x03\x02\x02" + + "\x02\u02C9\u02CA\x05\xB3Y\x02\u02CA\u02CB\x05\xD7k\x02\u02CB\xA0\x03\x02" + + "\x02\x02\u02CC\u02CD\x05\xCBe\x02\u02CD\u02CE\x05\xBB]\x02\u02CE\u02CF" + + "\x05\xD9l\x02\u02CF\u02D0\x05\xB3Y\x02\u02D0\u02D1\x05\xB9\\\x02\u02D1" + + "\u02D2\x05\xB3Y\x02\u02D2\u02D3\x05\xD9l\x02\u02D3\u02D4\x05\xB3Y\x02" + + "\u02D4\xA2\x03\x02\x02\x02\u02D5\u02D6\x05\xCFg\x02\u02D6\u02D7\x05\xCD" + + "f\x02\u02D7\xA4\x03\x02\x02\x02\u02D8\u02D9\x05\xDFo\x02\u02D9\u02DA\x05" + + "\xC3a\x02\u02DA\u02DB\x05\xD9l\x02\u02DB\u02DC\x05\xC1`\x02\u02DC\xA6" + + "\x03\x02\x02\x02\u02DD\u02DF\x05\xA9T\x02\u02DE\u02DD\x03\x02\x02\x02" + + "\u02DF\u02E0\x03\x02\x02\x02\u02E0\u02DE\x03\x02\x02\x02\u02E0\u02E1\x03" + + "\x02\x02\x02\u02E1\xA8\x03\x02\x02\x02\u02E2\u02E4\n\r\x02\x02\u02E3\u02E2" + + "\x03\x02\x02\x02\u02E4\u02E5\x03\x02\x02\x02\u02E5\u02E3\x03\x02\x02\x02" + + "\u02E5\u02E6\x03\x02\x02\x02\u02E6\u02EA\x03\x02\x02\x02\u02E7\u02E8\x07" + + "1\x02\x02\u02E8\u02EA\n\x0E\x02\x02\u02E9\u02E3\x03\x02\x02\x02\u02E9" + + "\u02E7\x03\x02\x02\x02\u02EA\xAA\x03\x02\x02\x02\u02EB\u02EC\x05\x8DF" + + "\x02\u02EC\xAC\x03\x02\x02\x02\u02ED\u02EE\x05\'\x13\x02\u02EE\u02EF\x03" + + "\x02\x02\x02\u02EF\u02F0\bV\x04\x02\u02F0\xAE\x03\x02\x02\x02\u02F1\u02F2" + + "\x05)\x14\x02\u02F2\u02F3\x03\x02\x02\x02\u02F3\u02F4\bW\x04\x02\u02F4" + + "\xB0\x03\x02\x02\x02\u02F5\u02F6\x05+\x15\x02\u02F6\u02F7\x03\x02\x02" + + "\x02\u02F7\u02F8\bX\x04\x02\u02F8\xB2\x03\x02\x02\x02\u02F9\u02FA\t\x0F" + + "\x02\x02\u02FA\xB4\x03\x02\x02\x02\u02FB\u02FC\t\x10\x02\x02\u02FC\xB6" + + "\x03\x02\x02\x02\u02FD\u02FE\t\x11\x02\x02\u02FE\xB8\x03\x02\x02\x02\u02FF" + + "\u0300\t\x12\x02\x02\u0300\xBA\x03\x02\x02\x02\u0301\u0302\t\t\x02\x02" + + "\u0302\xBC\x03\x02\x02\x02\u0303\u0304\t\x13\x02\x02\u0304\xBE\x03\x02" + + "\x02\x02\u0305\u0306\t\x14\x02\x02\u0306\xC0\x03\x02\x02\x02\u0307\u0308" + + "\t\x15\x02\x02\u0308\xC2\x03\x02\x02\x02\u0309\u030A\t\x16\x02\x02\u030A" + + "\xC4\x03\x02\x02\x02\u030B\u030C\t\x17\x02\x02\u030C\xC6\x03\x02\x02\x02" + + "\u030D\u030E\t\x18\x02\x02\u030E\xC8\x03\x02\x02\x02\u030F\u0310\t\x19" + + "\x02\x02\u0310\xCA\x03\x02\x02\x02\u0311\u0312\t\x1A\x02\x02\u0312\xCC" + + "\x03\x02\x02\x02\u0313\u0314\t\x1B\x02\x02\u0314\xCE\x03\x02\x02\x02\u0315" + + "\u0316\t\x1C\x02\x02\u0316\xD0\x03\x02\x02\x02\u0317\u0318\t\x1D\x02\x02" + + "\u0318\xD2\x03\x02\x02\x02\u0319\u031A\t\x1E\x02\x02\u031A\xD4\x03\x02" + + "\x02\x02\u031B\u031C\t\x1F\x02\x02\u031C\xD6\x03\x02\x02\x02\u031D\u031E" + + "\t \x02\x02\u031E\xD8\x03\x02\x02\x02\u031F\u0320\t!\x02\x02\u0320\xDA" + + "\x03\x02\x02\x02\u0321\u0322\t\"\x02\x02\u0322\xDC\x03\x02\x02\x02\u0323" + + "\u0324\t#\x02\x02\u0324\xDE\x03\x02\x02\x02\u0325\u0326\t$\x02\x02\u0326" + + "\xE0\x03\x02\x02\x02\u0327\u0328\t%\x02\x02\u0328\xE2\x03\x02\x02\x02" + + "\u0329\u032A\t&\x02\x02\u032A\xE4\x03\x02\x02\x02\u032B\u032C\t\'\x02" + + "\x02\u032C\xE6\x03\x02\x02\x02\'\x02\x03\x04\u016B\u0175\u0179\u017C\u0185" + + "\u0187\u0192\u01A5\u01AA\u01AF\u01B1\u01BC\u01C4\u01C7\u01C9\u01CE\u01D3" + + "\u01D9\u01E0\u01E5\u01EB\u01EE\u01F6\u01FA\u028A\u028C\u0293\u0295\u0297" + + "\u029D\u029F\u02E0\u02E5\u02E9\v\x07\x03\x02\x07\x04\x02\x02\x03\x02\x06" + + "\x02\x02\t\x17\x02\t?\x02\t@\x02\t\x1F\x02\t\x1E\x02"; public static readonly _serializedATN: string = Utils.join( [ esql_lexer._serializedATNSegment0, esql_lexer._serializedATNSegment1, - esql_lexer._serializedATNSegment2, ], "", ); diff --git a/packages/kbn-monaco/src/esql/antlr/esql_parser.g4 b/packages/kbn-monaco/src/esql/antlr/esql_parser.g4 index af48024e56cc9..aa2e86b6979e1 100644 --- a/packages/kbn-monaco/src/esql/antlr/esql_parser.g4 +++ b/packages/kbn-monaco/src/esql/antlr/esql_parser.g4 @@ -20,8 +20,7 @@ query ; sourceCommand - : explainCommand - | fromCommand + : fromCommand | rowCommand | showCommand ; @@ -29,51 +28,30 @@ sourceCommand processingCommand : evalCommand | limitCommand - | projectCommand | keepCommand - | renameCommand - | dropCommand - | dissectCommand - | grokCommand | sortCommand | statsCommand | whereCommand - | mvExpandCommand + | dropCommand + | renameCommand + | dissectCommand + | grokCommand | enrichCommand - ; - -enrichCommand - : ENRICH policyName=enrichIdentifier (ON matchField=enrichFieldIdentifier)? (WITH enrichWithClause (COMMA enrichWithClause)*)? - ; - -enrichWithClause - : (newName=enrichFieldIdentifier ASSIGN)? enrichField=enrichFieldIdentifier - ; - -mvExpandCommand - : MV_EXPAND qualifiedNames + | mvExpandCommand ; whereCommand - : WHERE whereBooleanExpression - ; - -whereBooleanExpression - : NOT whereBooleanExpression - | valueExpression - | regexBooleanExpression - | left=whereBooleanExpression operator=AND right=whereBooleanExpression - | left=whereBooleanExpression operator=OR right=whereBooleanExpression - | valueExpression (NOT)? IN LP valueExpression (COMMA valueExpression)* RP - | (NOT)? WHERE_FUNCTIONS LP qualifiedName ((COMMA functionExpressionArgument)*)? RP - | valueExpression IS NOT? NULL + : WHERE booleanExpression ; booleanExpression - : NOT booleanExpression - | valueExpression - | left=booleanExpression operator=AND right=booleanExpression - | left=booleanExpression operator=OR right=booleanExpression + : NOT booleanExpression #logicalNot + | valueExpression #booleanDefault + | regexBooleanExpression #regexExpression + | left=booleanExpression operator=AND right=booleanExpression #logicalBinary + | left=booleanExpression operator=OR right=booleanExpression #logicalBinary + | valueExpression (NOT)? IN LP valueExpression (COMMA valueExpression)* RP #logicalIn + | valueExpression IS NOT? NULL #isNull ; regexBooleanExpression @@ -82,41 +60,26 @@ regexBooleanExpression ; valueExpression - : operatorExpression - | comparison + : operatorExpression #valueExpressionDefault + | left=operatorExpression comparisonOperator right=operatorExpression #comparison ; -comparison - : left=operatorExpression comparisonOperator right=operatorExpression - ; - -mathFn - : functionIdentifier LP (functionExpressionArgument (COMMA functionExpressionArgument)*)? RP - ; - -mathEvalFn - : mathFunctionIdentifier LP (mathFunctionExpressionArgument (COMMA mathFunctionExpressionArgument)*)? RP - ; - -dateExpression - : quantifier=number DATE_LITERAL - ; - operatorExpression - : primaryExpression - | mathFn - | mathEvalFn - | operator=(MINUS | PLUS) operatorExpression - | left=operatorExpression operator=(ASTERISK | SLASH | PERCENT) right=operatorExpression - | left=operatorExpression operator=(PLUS | MINUS) right=operatorExpression + : primaryExpression #operatorExpressionDefault + | operator=(MINUS | PLUS) operatorExpression #arithmeticUnary + | left=operatorExpression operator=(ASTERISK | SLASH | PERCENT) right=operatorExpression #arithmeticBinary + | left=operatorExpression operator=(PLUS | MINUS) right=operatorExpression #arithmeticBinary ; primaryExpression - : constant - | qualifiedName - | dateExpression - | LP booleanExpression RP - | identifier LP (booleanExpression (COMMA booleanExpression)*)? RP + : constant #constantDefault + | qualifiedName #dereference + | functionExpression #function + | LP booleanExpression RP #parenthesizedExpression + ; + +functionExpression + : identifier LP (ASTERISK | (booleanExpression (COMMA booleanExpression)*))? RP ; rowCommand @@ -129,18 +92,9 @@ fields field : booleanExpression - | userVariable ASSIGN booleanExpression + | qualifiedName ASSIGN booleanExpression ; -enrichFieldIdentifier - : ENR_UNQUOTED_IDENTIFIER - | ENR_QUOTED_IDENTIFIER - ; - -userVariable - : identifier - ; - fromCommand : FROM sourceIdentifier (COMMA sourceIdentifier)* metadata? ; @@ -154,7 +108,11 @@ evalCommand ; statsCommand - : STATS fields? (BY qualifiedNames)? + : STATS fields? (BY grouping)? + ; + +grouping + : qualifiedName (COMMA qualifiedName)* ; sourceIdentifier @@ -162,61 +120,26 @@ sourceIdentifier | SRC_QUOTED_IDENTIFIER ; -enrichIdentifier - : ENR_UNQUOTED_IDENTIFIER - | ENR_QUOTED_IDENTIFIER - ; - -functionExpressionArgument - : qualifiedName - | string - | number - ; - -mathFunctionExpressionArgument - : qualifiedName - | string - | number - | operatorExpression - | dateExpression - | comparison - ; - qualifiedName : identifier (DOT identifier)* ; -qualifiedNames - : qualifiedName (COMMA qualifiedName)* - ; - identifier : UNQUOTED_IDENTIFIER | QUOTED_IDENTIFIER - | ASTERISK - ; - -mathFunctionIdentifier - : MATH_FUNCTION - ; - -functionIdentifier - : UNARY_FUNCTION ; constant - : NULL - | numericValue - | booleanValue - | string - | OPENING_BRACKET numericValue (COMMA numericValue)* CLOSING_BRACKET - | OPENING_BRACKET booleanValue (COMMA booleanValue)* CLOSING_BRACKET - | OPENING_BRACKET string (COMMA string)* CLOSING_BRACKET - ; - -numericValue - : decimalValue - | integerValue + : NULL #nullLiteral + | integerValue UNQUOTED_IDENTIFIER #qualifiedIntegerLiteral + | decimalValue #decimalLiteral + | integerValue #integerLiteral + | booleanValue #booleanLiteral + | PARAM #inputParam + | string #stringLiteral + | OPENING_BRACKET numericValue (COMMA numericValue)* CLOSING_BRACKET #numericArrayLiteral + | OPENING_BRACKET booleanValue (COMMA booleanValue)* CLOSING_BRACKET #booleanArrayLiteral + | OPENING_BRACKET string (COMMA string)* CLOSING_BRACKET #stringArrayLiteral ; limitCommand @@ -228,40 +151,36 @@ sortCommand ; orderExpression - : booleanExpression (ORDERING)? (NULLS_ORDERING (NULLS_ORDERING_DIRECTION))? - ; - -projectCommand - : PROJECT qualifiedNames + : booleanExpression ordering=(ASC | DESC)? (NULLS nullOrdering=(FIRST | LAST))? ; keepCommand - : KEEP qualifiedNames + : KEEP sourceIdentifier (COMMA sourceIdentifier)* + | PROJECT sourceIdentifier (COMMA sourceIdentifier)* ; - dropCommand - : DROP qualifiedNames + : DROP sourceIdentifier (COMMA sourceIdentifier)* ; -renameVariable - : identifier (DOT identifier)* - ; - renameCommand : RENAME renameClause (COMMA renameClause)* ; -renameClause - : qualifiedName AS renameVariable +renameClause: + oldName=sourceIdentifier AS newName=sourceIdentifier ; dissectCommand - : DISSECT qualifiedNames string commandOptions? + : DISSECT primaryExpression string commandOptions? ; grokCommand - : GROK qualifiedNames string + : GROK primaryExpression string + ; + +mvExpandCommand + : MV_EXPAND sourceIdentifier ; commandOptions @@ -273,20 +192,20 @@ commandOption ; booleanValue - : BOOLEAN_VALUE + : TRUE | FALSE ; -number - : DECIMAL_LITERAL #decimalLiteral - | INTEGER_LITERAL #integerLiteral +numericValue + : decimalValue + | integerValue ; decimalValue - : DECIMAL_LITERAL + : (PLUS | MINUS)? DECIMAL_LITERAL ; integerValue - : INTEGER_LITERAL + : (PLUS | MINUS)? INTEGER_LITERAL ; string @@ -294,18 +213,18 @@ string ; comparisonOperator - : COMPARISON_OPERATOR + : EQ | NEQ | LT | LTE | GT | GTE ; -explainCommand - : EXPLAIN subqueryExpression +showCommand + : SHOW INFO #showInfo + | SHOW FUNCTIONS #showFunctions ; -subqueryExpression - : OPENING_BRACKET query CLOSING_BRACKET +enrichCommand + : ENRICH policyName=sourceIdentifier (ON matchField=sourceIdentifier)? (WITH enrichWithClause (COMMA enrichWithClause)*)? ; -showCommand - : SHOW INFO - | SHOW FUNCTIONS - ; +enrichWithClause + : (newName=sourceIdentifier ASSIGN)? enrichField=sourceIdentifier + ; \ No newline at end of file diff --git a/packages/kbn-monaco/src/esql/antlr/esql_parser.interp b/packages/kbn-monaco/src/esql/antlr/esql_parser.interp index 378d85247dc13..649902a25536b 100644 --- a/packages/kbn-monaco/src/esql/antlr/esql_parser.interp +++ b/packages/kbn-monaco/src/esql/antlr/esql_parser.interp @@ -27,46 +27,41 @@ null null null null -'by' null -'and' null null '.' +null +null +null '(' null -']' null null null null null null +'?' null -'or' ')' -'_' -'info' -'functions' null null +null +'_' +'==' +'!=' +'<' +'<=' +'>' +'>=' '+' '-' '*' '/' '%' -'10' -null -'nulls' -null -null -null -null -null -null -null -null null +']' null null null @@ -85,149 +80,128 @@ null token symbolic names: null DISSECT -GROK +DROP +ENRICH EVAL -EXPLAIN FROM -ROW -STATS -WHERE -SORT -MV_EXPAND +GROK +KEEP LIMIT +MV_EXPAND PROJECT -DROP RENAME +ROW SHOW -ENRICH -KEEP +SORT +STATS +WHERE +UNKNOWN_CMD LINE_COMMENT MULTILINE_COMMENT WS -EXPLAIN_WS -EXPLAIN_LINE_COMMENT -EXPLAIN_MULTILINE_COMMENT PIPE STRING INTEGER_LITERAL DECIMAL_LITERAL BY -DATE_LITERAL AND +ASC ASSIGN COMMA +DESC DOT +FALSE +FIRST +LAST LP -OPENING_BRACKET -CLOSING_BRACKET -NOT -LIKE -RLIKE IN IS -AS +LIKE +NOT NULL +NULLS OR +PARAM +RLIKE RP -UNDERSCORE +TRUE INFO FUNCTIONS -BOOLEAN_VALUE -COMPARISON_OPERATOR +UNDERSCORE +EQ +NEQ +LT +LTE +GT +GTE PLUS MINUS ASTERISK SLASH PERCENT -TEN -ORDERING -NULLS_ORDERING -NULLS_ORDERING_DIRECTION -MATH_FUNCTION -UNARY_FUNCTION -WHERE_FUNCTIONS +OPENING_BRACKET +CLOSING_BRACKET UNQUOTED_IDENTIFIER QUOTED_IDENTIFIER EXPR_LINE_COMMENT EXPR_MULTILINE_COMMENT EXPR_WS +AS METADATA +ON +WITH SRC_UNQUOTED_IDENTIFIER SRC_QUOTED_IDENTIFIER SRC_LINE_COMMENT SRC_MULTILINE_COMMENT SRC_WS -ON -WITH -ENR_UNQUOTED_IDENTIFIER -ENR_QUOTED_IDENTIFIER -ENR_LINE_COMMENT -ENR_MULTILINE_COMMENT -ENR_WS -EXPLAIN_PIPE rule names: singleStatement query sourceCommand processingCommand -enrichCommand -enrichWithClause -mvExpandCommand whereCommand -whereBooleanExpression booleanExpression regexBooleanExpression valueExpression -comparison -mathFn -mathEvalFn -dateExpression operatorExpression primaryExpression +functionExpression rowCommand fields field -enrichFieldIdentifier -userVariable fromCommand metadata evalCommand statsCommand +grouping sourceIdentifier -enrichIdentifier -functionExpressionArgument -mathFunctionExpressionArgument qualifiedName -qualifiedNames identifier -mathFunctionIdentifier -functionIdentifier constant -numericValue limitCommand sortCommand orderExpression -projectCommand keepCommand dropCommand -renameVariable renameCommand renameClause dissectCommand grokCommand +mvExpandCommand commandOptions commandOption booleanValue -number +numericValue decimalValue integerValue string comparisonOperator -explainCommand -subqueryExpression showCommand +enrichCommand +enrichWithClause atn: -[3, 51485, 51898, 1421, 44986, 20307, 1543, 60043, 49729, 3, 83, 598, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4, 18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 4, 22, 9, 22, 4, 23, 9, 23, 4, 24, 9, 24, 4, 25, 9, 25, 4, 26, 9, 26, 4, 27, 9, 27, 4, 28, 9, 28, 4, 29, 9, 29, 4, 30, 9, 30, 4, 31, 9, 31, 4, 32, 9, 32, 4, 33, 9, 33, 4, 34, 9, 34, 4, 35, 9, 35, 4, 36, 9, 36, 4, 37, 9, 37, 4, 38, 9, 38, 4, 39, 9, 39, 4, 40, 9, 40, 4, 41, 9, 41, 4, 42, 9, 42, 4, 43, 9, 43, 4, 44, 9, 44, 4, 45, 9, 45, 4, 46, 9, 46, 4, 47, 9, 47, 4, 48, 9, 48, 4, 49, 9, 49, 4, 50, 9, 50, 4, 51, 9, 51, 4, 52, 9, 52, 4, 53, 9, 53, 4, 54, 9, 54, 4, 55, 9, 55, 4, 56, 9, 56, 4, 57, 9, 57, 4, 58, 9, 58, 4, 59, 9, 59, 4, 60, 9, 60, 4, 61, 9, 61, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 3, 132, 10, 3, 12, 3, 14, 3, 135, 11, 3, 3, 4, 3, 4, 3, 4, 3, 4, 5, 4, 141, 10, 4, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 5, 5, 156, 10, 5, 3, 6, 3, 6, 3, 6, 3, 6, 5, 6, 162, 10, 6, 3, 6, 3, 6, 3, 6, 3, 6, 7, 6, 168, 10, 6, 12, 6, 14, 6, 171, 11, 6, 5, 6, 173, 10, 6, 3, 7, 3, 7, 3, 7, 5, 7, 178, 10, 7, 3, 7, 3, 7, 3, 8, 3, 8, 3, 8, 3, 9, 3, 9, 3, 9, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 5, 10, 195, 10, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 7, 10, 202, 10, 10, 12, 10, 14, 10, 205, 11, 10, 3, 10, 3, 10, 3, 10, 5, 10, 210, 10, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 7, 10, 217, 10, 10, 12, 10, 14, 10, 220, 11, 10, 5, 10, 222, 10, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 5, 10, 229, 10, 10, 3, 10, 3, 10, 5, 10, 233, 10, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 7, 10, 241, 10, 10, 12, 10, 14, 10, 244, 11, 10, 3, 11, 3, 11, 3, 11, 3, 11, 5, 11, 250, 10, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 7, 11, 258, 10, 11, 12, 11, 14, 11, 261, 11, 11, 3, 12, 3, 12, 5, 12, 265, 10, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 5, 12, 272, 10, 12, 3, 12, 3, 12, 3, 12, 5, 12, 277, 10, 12, 3, 13, 3, 13, 5, 13, 281, 10, 13, 3, 14, 3, 14, 3, 14, 3, 14, 3, 15, 3, 15, 3, 15, 3, 15, 3, 15, 7, 15, 292, 10, 15, 12, 15, 14, 15, 295, 11, 15, 5, 15, 297, 10, 15, 3, 15, 3, 15, 3, 16, 3, 16, 3, 16, 3, 16, 3, 16, 7, 16, 306, 10, 16, 12, 16, 14, 16, 309, 11, 16, 5, 16, 311, 10, 16, 3, 16, 3, 16, 3, 17, 3, 17, 3, 17, 3, 18, 3, 18, 3, 18, 3, 18, 3, 18, 3, 18, 5, 18, 324, 10, 18, 3, 18, 3, 18, 3, 18, 3, 18, 3, 18, 3, 18, 7, 18, 332, 10, 18, 12, 18, 14, 18, 335, 11, 18, 3, 19, 3, 19, 3, 19, 3, 19, 3, 19, 3, 19, 3, 19, 3, 19, 3, 19, 3, 19, 3, 19, 3, 19, 7, 19, 349, 10, 19, 12, 19, 14, 19, 352, 11, 19, 5, 19, 354, 10, 19, 3, 19, 3, 19, 5, 19, 358, 10, 19, 3, 20, 3, 20, 3, 20, 3, 21, 3, 21, 3, 21, 7, 21, 366, 10, 21, 12, 21, 14, 21, 369, 11, 21, 3, 22, 3, 22, 3, 22, 3, 22, 3, 22, 5, 22, 376, 10, 22, 3, 23, 3, 23, 3, 24, 3, 24, 3, 25, 3, 25, 3, 25, 3, 25, 7, 25, 386, 10, 25, 12, 25, 14, 25, 389, 11, 25, 3, 25, 5, 25, 392, 10, 25, 3, 26, 3, 26, 3, 26, 3, 26, 3, 26, 7, 26, 399, 10, 26, 12, 26, 14, 26, 402, 11, 26, 3, 26, 3, 26, 3, 27, 3, 27, 3, 27, 3, 28, 3, 28, 5, 28, 411, 10, 28, 3, 28, 3, 28, 5, 28, 415, 10, 28, 3, 29, 3, 29, 3, 30, 3, 30, 3, 31, 3, 31, 3, 31, 5, 31, 424, 10, 31, 3, 32, 3, 32, 3, 32, 3, 32, 3, 32, 3, 32, 5, 32, 432, 10, 32, 3, 33, 3, 33, 3, 33, 7, 33, 437, 10, 33, 12, 33, 14, 33, 440, 11, 33, 3, 34, 3, 34, 3, 34, 7, 34, 445, 10, 34, 12, 34, 14, 34, 448, 11, 34, 3, 35, 3, 35, 3, 36, 3, 36, 3, 37, 3, 37, 3, 38, 3, 38, 3, 38, 3, 38, 3, 38, 3, 38, 3, 38, 3, 38, 7, 38, 464, 10, 38, 12, 38, 14, 38, 467, 11, 38, 3, 38, 3, 38, 3, 38, 3, 38, 3, 38, 3, 38, 7, 38, 475, 10, 38, 12, 38, 14, 38, 478, 11, 38, 3, 38, 3, 38, 3, 38, 3, 38, 3, 38, 3, 38, 7, 38, 486, 10, 38, 12, 38, 14, 38, 489, 11, 38, 3, 38, 3, 38, 5, 38, 493, 10, 38, 3, 39, 3, 39, 5, 39, 497, 10, 39, 3, 40, 3, 40, 3, 40, 3, 41, 3, 41, 3, 41, 3, 41, 7, 41, 506, 10, 41, 12, 41, 14, 41, 509, 11, 41, 3, 42, 3, 42, 5, 42, 513, 10, 42, 3, 42, 3, 42, 5, 42, 517, 10, 42, 3, 43, 3, 43, 3, 43, 3, 44, 3, 44, 3, 44, 3, 45, 3, 45, 3, 45, 3, 46, 3, 46, 3, 46, 7, 46, 531, 10, 46, 12, 46, 14, 46, 534, 11, 46, 3, 47, 3, 47, 3, 47, 3, 47, 7, 47, 540, 10, 47, 12, 47, 14, 47, 543, 11, 47, 3, 48, 3, 48, 3, 48, 3, 48, 3, 49, 3, 49, 3, 49, 3, 49, 5, 49, 553, 10, 49, 3, 50, 3, 50, 3, 50, 3, 50, 3, 51, 3, 51, 3, 51, 7, 51, 562, 10, 51, 12, 51, 14, 51, 565, 11, 51, 3, 52, 3, 52, 3, 52, 3, 52, 3, 53, 3, 53, 3, 54, 3, 54, 5, 54, 575, 10, 54, 3, 55, 3, 55, 3, 56, 3, 56, 3, 57, 3, 57, 3, 58, 3, 58, 3, 59, 3, 59, 3, 59, 3, 60, 3, 60, 3, 60, 3, 60, 3, 61, 3, 61, 3, 61, 3, 61, 5, 61, 596, 10, 61, 3, 61, 2, 2, 6, 4, 18, 20, 34, 62, 2, 2, 4, 2, 6, 2, 8, 2, 10, 2, 12, 2, 14, 2, 16, 2, 18, 2, 20, 2, 22, 2, 24, 2, 26, 2, 28, 2, 30, 2, 32, 2, 34, 2, 36, 2, 38, 2, 40, 2, 42, 2, 44, 2, 46, 2, 48, 2, 50, 2, 52, 2, 54, 2, 56, 2, 58, 2, 60, 2, 62, 2, 64, 2, 66, 2, 68, 2, 70, 2, 72, 2, 74, 2, 76, 2, 78, 2, 80, 2, 82, 2, 84, 2, 86, 2, 88, 2, 90, 2, 92, 2, 94, 2, 96, 2, 98, 2, 100, 2, 102, 2, 104, 2, 106, 2, 108, 2, 110, 2, 112, 2, 114, 2, 116, 2, 118, 2, 120, 2, 2, 7, 3, 2, 53, 54, 3, 2, 55, 57, 3, 2, 78, 79, 3, 2, 71, 72, 4, 2, 55, 55, 65, 66, 2, 627, 2, 122, 3, 2, 2, 2, 4, 125, 3, 2, 2, 2, 6, 140, 3, 2, 2, 2, 8, 155, 3, 2, 2, 2, 10, 157, 3, 2, 2, 2, 12, 177, 3, 2, 2, 2, 14, 181, 3, 2, 2, 2, 16, 184, 3, 2, 2, 2, 18, 232, 3, 2, 2, 2, 20, 249, 3, 2, 2, 2, 22, 276, 3, 2, 2, 2, 24, 280, 3, 2, 2, 2, 26, 282, 3, 2, 2, 2, 28, 286, 3, 2, 2, 2, 30, 300, 3, 2, 2, 2, 32, 314, 3, 2, 2, 2, 34, 323, 3, 2, 2, 2, 36, 357, 3, 2, 2, 2, 38, 359, 3, 2, 2, 2, 40, 362, 3, 2, 2, 2, 42, 375, 3, 2, 2, 2, 44, 377, 3, 2, 2, 2, 46, 379, 3, 2, 2, 2, 48, 381, 3, 2, 2, 2, 50, 393, 3, 2, 2, 2, 52, 405, 3, 2, 2, 2, 54, 408, 3, 2, 2, 2, 56, 416, 3, 2, 2, 2, 58, 418, 3, 2, 2, 2, 60, 423, 3, 2, 2, 2, 62, 431, 3, 2, 2, 2, 64, 433, 3, 2, 2, 2, 66, 441, 3, 2, 2, 2, 68, 449, 3, 2, 2, 2, 70, 451, 3, 2, 2, 2, 72, 453, 3, 2, 2, 2, 74, 492, 3, 2, 2, 2, 76, 496, 3, 2, 2, 2, 78, 498, 3, 2, 2, 2, 80, 501, 3, 2, 2, 2, 82, 510, 3, 2, 2, 2, 84, 518, 3, 2, 2, 2, 86, 521, 3, 2, 2, 2, 88, 524, 3, 2, 2, 2, 90, 527, 3, 2, 2, 2, 92, 535, 3, 2, 2, 2, 94, 544, 3, 2, 2, 2, 96, 548, 3, 2, 2, 2, 98, 554, 3, 2, 2, 2, 100, 558, 3, 2, 2, 2, 102, 566, 3, 2, 2, 2, 104, 570, 3, 2, 2, 2, 106, 574, 3, 2, 2, 2, 108, 576, 3, 2, 2, 2, 110, 578, 3, 2, 2, 2, 112, 580, 3, 2, 2, 2, 114, 582, 3, 2, 2, 2, 116, 584, 3, 2, 2, 2, 118, 587, 3, 2, 2, 2, 120, 595, 3, 2, 2, 2, 122, 123, 5, 4, 3, 2, 123, 124, 7, 2, 2, 3, 124, 3, 3, 2, 2, 2, 125, 126, 8, 3, 1, 2, 126, 127, 5, 6, 4, 2, 127, 133, 3, 2, 2, 2, 128, 129, 12, 3, 2, 2, 129, 130, 7, 26, 2, 2, 130, 132, 5, 8, 5, 2, 131, 128, 3, 2, 2, 2, 132, 135, 3, 2, 2, 2, 133, 131, 3, 2, 2, 2, 133, 134, 3, 2, 2, 2, 134, 5, 3, 2, 2, 2, 135, 133, 3, 2, 2, 2, 136, 141, 5, 116, 59, 2, 137, 141, 5, 48, 25, 2, 138, 141, 5, 38, 20, 2, 139, 141, 5, 120, 61, 2, 140, 136, 3, 2, 2, 2, 140, 137, 3, 2, 2, 2, 140, 138, 3, 2, 2, 2, 140, 139, 3, 2, 2, 2, 141, 7, 3, 2, 2, 2, 142, 156, 5, 52, 27, 2, 143, 156, 5, 78, 40, 2, 144, 156, 5, 84, 43, 2, 145, 156, 5, 86, 44, 2, 146, 156, 5, 92, 47, 2, 147, 156, 5, 88, 45, 2, 148, 156, 5, 96, 49, 2, 149, 156, 5, 98, 50, 2, 150, 156, 5, 80, 41, 2, 151, 156, 5, 54, 28, 2, 152, 156, 5, 16, 9, 2, 153, 156, 5, 14, 8, 2, 154, 156, 5, 10, 6, 2, 155, 142, 3, 2, 2, 2, 155, 143, 3, 2, 2, 2, 155, 144, 3, 2, 2, 2, 155, 145, 3, 2, 2, 2, 155, 146, 3, 2, 2, 2, 155, 147, 3, 2, 2, 2, 155, 148, 3, 2, 2, 2, 155, 149, 3, 2, 2, 2, 155, 150, 3, 2, 2, 2, 155, 151, 3, 2, 2, 2, 155, 152, 3, 2, 2, 2, 155, 153, 3, 2, 2, 2, 155, 154, 3, 2, 2, 2, 156, 9, 3, 2, 2, 2, 157, 158, 7, 18, 2, 2, 158, 161, 5, 58, 30, 2, 159, 160, 7, 76, 2, 2, 160, 162, 5, 44, 23, 2, 161, 159, 3, 2, 2, 2, 161, 162, 3, 2, 2, 2, 162, 172, 3, 2, 2, 2, 163, 164, 7, 77, 2, 2, 164, 169, 5, 12, 7, 2, 165, 166, 7, 34, 2, 2, 166, 168, 5, 12, 7, 2, 167, 165, 3, 2, 2, 2, 168, 171, 3, 2, 2, 2, 169, 167, 3, 2, 2, 2, 169, 170, 3, 2, 2, 2, 170, 173, 3, 2, 2, 2, 171, 169, 3, 2, 2, 2, 172, 163, 3, 2, 2, 2, 172, 173, 3, 2, 2, 2, 173, 11, 3, 2, 2, 2, 174, 175, 5, 44, 23, 2, 175, 176, 7, 33, 2, 2, 176, 178, 3, 2, 2, 2, 177, 174, 3, 2, 2, 2, 177, 178, 3, 2, 2, 2, 178, 179, 3, 2, 2, 2, 179, 180, 5, 44, 23, 2, 180, 13, 3, 2, 2, 2, 181, 182, 7, 12, 2, 2, 182, 183, 5, 66, 34, 2, 183, 15, 3, 2, 2, 2, 184, 185, 7, 10, 2, 2, 185, 186, 5, 18, 10, 2, 186, 17, 3, 2, 2, 2, 187, 188, 8, 10, 1, 2, 188, 189, 7, 39, 2, 2, 189, 233, 5, 18, 10, 10, 190, 233, 5, 24, 13, 2, 191, 233, 5, 22, 12, 2, 192, 194, 5, 24, 13, 2, 193, 195, 7, 39, 2, 2, 194, 193, 3, 2, 2, 2, 194, 195, 3, 2, 2, 2, 195, 196, 3, 2, 2, 2, 196, 197, 7, 42, 2, 2, 197, 198, 7, 36, 2, 2, 198, 203, 5, 24, 13, 2, 199, 200, 7, 34, 2, 2, 200, 202, 5, 24, 13, 2, 201, 199, 3, 2, 2, 2, 202, 205, 3, 2, 2, 2, 203, 201, 3, 2, 2, 2, 203, 204, 3, 2, 2, 2, 204, 206, 3, 2, 2, 2, 205, 203, 3, 2, 2, 2, 206, 207, 7, 47, 2, 2, 207, 233, 3, 2, 2, 2, 208, 210, 7, 39, 2, 2, 209, 208, 3, 2, 2, 2, 209, 210, 3, 2, 2, 2, 210, 211, 3, 2, 2, 2, 211, 212, 7, 64, 2, 2, 212, 213, 7, 36, 2, 2, 213, 221, 5, 64, 33, 2, 214, 215, 7, 34, 2, 2, 215, 217, 5, 60, 31, 2, 216, 214, 3, 2, 2, 2, 217, 220, 3, 2, 2, 2, 218, 216, 3, 2, 2, 2, 218, 219, 3, 2, 2, 2, 219, 222, 3, 2, 2, 2, 220, 218, 3, 2, 2, 2, 221, 218, 3, 2, 2, 2, 221, 222, 3, 2, 2, 2, 222, 223, 3, 2, 2, 2, 223, 224, 7, 47, 2, 2, 224, 233, 3, 2, 2, 2, 225, 226, 5, 24, 13, 2, 226, 228, 7, 43, 2, 2, 227, 229, 7, 39, 2, 2, 228, 227, 3, 2, 2, 2, 228, 229, 3, 2, 2, 2, 229, 230, 3, 2, 2, 2, 230, 231, 7, 45, 2, 2, 231, 233, 3, 2, 2, 2, 232, 187, 3, 2, 2, 2, 232, 190, 3, 2, 2, 2, 232, 191, 3, 2, 2, 2, 232, 192, 3, 2, 2, 2, 232, 209, 3, 2, 2, 2, 232, 225, 3, 2, 2, 2, 233, 242, 3, 2, 2, 2, 234, 235, 12, 7, 2, 2, 235, 236, 7, 32, 2, 2, 236, 241, 5, 18, 10, 8, 237, 238, 12, 6, 2, 2, 238, 239, 7, 46, 2, 2, 239, 241, 5, 18, 10, 7, 240, 234, 3, 2, 2, 2, 240, 237, 3, 2, 2, 2, 241, 244, 3, 2, 2, 2, 242, 240, 3, 2, 2, 2, 242, 243, 3, 2, 2, 2, 243, 19, 3, 2, 2, 2, 244, 242, 3, 2, 2, 2, 245, 246, 8, 11, 1, 2, 246, 247, 7, 39, 2, 2, 247, 250, 5, 20, 11, 6, 248, 250, 5, 24, 13, 2, 249, 245, 3, 2, 2, 2, 249, 248, 3, 2, 2, 2, 250, 259, 3, 2, 2, 2, 251, 252, 12, 4, 2, 2, 252, 253, 7, 32, 2, 2, 253, 258, 5, 20, 11, 5, 254, 255, 12, 3, 2, 2, 255, 256, 7, 46, 2, 2, 256, 258, 5, 20, 11, 4, 257, 251, 3, 2, 2, 2, 257, 254, 3, 2, 2, 2, 258, 261, 3, 2, 2, 2, 259, 257, 3, 2, 2, 2, 259, 260, 3, 2, 2, 2, 260, 21, 3, 2, 2, 2, 261, 259, 3, 2, 2, 2, 262, 264, 5, 24, 13, 2, 263, 265, 7, 39, 2, 2, 264, 263, 3, 2, 2, 2, 264, 265, 3, 2, 2, 2, 265, 266, 3, 2, 2, 2, 266, 267, 7, 40, 2, 2, 267, 268, 5, 112, 57, 2, 268, 277, 3, 2, 2, 2, 269, 271, 5, 24, 13, 2, 270, 272, 7, 39, 2, 2, 271, 270, 3, 2, 2, 2, 271, 272, 3, 2, 2, 2, 272, 273, 3, 2, 2, 2, 273, 274, 7, 41, 2, 2, 274, 275, 5, 112, 57, 2, 275, 277, 3, 2, 2, 2, 276, 262, 3, 2, 2, 2, 276, 269, 3, 2, 2, 2, 277, 23, 3, 2, 2, 2, 278, 281, 5, 34, 18, 2, 279, 281, 5, 26, 14, 2, 280, 278, 3, 2, 2, 2, 280, 279, 3, 2, 2, 2, 281, 25, 3, 2, 2, 2, 282, 283, 5, 34, 18, 2, 283, 284, 5, 114, 58, 2, 284, 285, 5, 34, 18, 2, 285, 27, 3, 2, 2, 2, 286, 287, 5, 72, 37, 2, 287, 296, 7, 36, 2, 2, 288, 293, 5, 60, 31, 2, 289, 290, 7, 34, 2, 2, 290, 292, 5, 60, 31, 2, 291, 289, 3, 2, 2, 2, 292, 295, 3, 2, 2, 2, 293, 291, 3, 2, 2, 2, 293, 294, 3, 2, 2, 2, 294, 297, 3, 2, 2, 2, 295, 293, 3, 2, 2, 2, 296, 288, 3, 2, 2, 2, 296, 297, 3, 2, 2, 2, 297, 298, 3, 2, 2, 2, 298, 299, 7, 47, 2, 2, 299, 29, 3, 2, 2, 2, 300, 301, 5, 70, 36, 2, 301, 310, 7, 36, 2, 2, 302, 307, 5, 62, 32, 2, 303, 304, 7, 34, 2, 2, 304, 306, 5, 62, 32, 2, 305, 303, 3, 2, 2, 2, 306, 309, 3, 2, 2, 2, 307, 305, 3, 2, 2, 2, 307, 308, 3, 2, 2, 2, 308, 311, 3, 2, 2, 2, 309, 307, 3, 2, 2, 2, 310, 302, 3, 2, 2, 2, 310, 311, 3, 2, 2, 2, 311, 312, 3, 2, 2, 2, 312, 313, 7, 47, 2, 2, 313, 31, 3, 2, 2, 2, 314, 315, 5, 106, 54, 2, 315, 316, 7, 31, 2, 2, 316, 33, 3, 2, 2, 2, 317, 318, 8, 18, 1, 2, 318, 324, 5, 36, 19, 2, 319, 324, 5, 28, 15, 2, 320, 324, 5, 30, 16, 2, 321, 322, 9, 2, 2, 2, 322, 324, 5, 34, 18, 5, 323, 317, 3, 2, 2, 2, 323, 319, 3, 2, 2, 2, 323, 320, 3, 2, 2, 2, 323, 321, 3, 2, 2, 2, 324, 333, 3, 2, 2, 2, 325, 326, 12, 4, 2, 2, 326, 327, 9, 3, 2, 2, 327, 332, 5, 34, 18, 5, 328, 329, 12, 3, 2, 2, 329, 330, 9, 2, 2, 2, 330, 332, 5, 34, 18, 4, 331, 325, 3, 2, 2, 2, 331, 328, 3, 2, 2, 2, 332, 335, 3, 2, 2, 2, 333, 331, 3, 2, 2, 2, 333, 334, 3, 2, 2, 2, 334, 35, 3, 2, 2, 2, 335, 333, 3, 2, 2, 2, 336, 358, 5, 74, 38, 2, 337, 358, 5, 64, 33, 2, 338, 358, 5, 32, 17, 2, 339, 340, 7, 36, 2, 2, 340, 341, 5, 20, 11, 2, 341, 342, 7, 47, 2, 2, 342, 358, 3, 2, 2, 2, 343, 344, 5, 68, 35, 2, 344, 353, 7, 36, 2, 2, 345, 350, 5, 20, 11, 2, 346, 347, 7, 34, 2, 2, 347, 349, 5, 20, 11, 2, 348, 346, 3, 2, 2, 2, 349, 352, 3, 2, 2, 2, 350, 348, 3, 2, 2, 2, 350, 351, 3, 2, 2, 2, 351, 354, 3, 2, 2, 2, 352, 350, 3, 2, 2, 2, 353, 345, 3, 2, 2, 2, 353, 354, 3, 2, 2, 2, 354, 355, 3, 2, 2, 2, 355, 356, 7, 47, 2, 2, 356, 358, 3, 2, 2, 2, 357, 336, 3, 2, 2, 2, 357, 337, 3, 2, 2, 2, 357, 338, 3, 2, 2, 2, 357, 339, 3, 2, 2, 2, 357, 343, 3, 2, 2, 2, 358, 37, 3, 2, 2, 2, 359, 360, 7, 8, 2, 2, 360, 361, 5, 40, 21, 2, 361, 39, 3, 2, 2, 2, 362, 367, 5, 42, 22, 2, 363, 364, 7, 34, 2, 2, 364, 366, 5, 42, 22, 2, 365, 363, 3, 2, 2, 2, 366, 369, 3, 2, 2, 2, 367, 365, 3, 2, 2, 2, 367, 368, 3, 2, 2, 2, 368, 41, 3, 2, 2, 2, 369, 367, 3, 2, 2, 2, 370, 376, 5, 20, 11, 2, 371, 372, 5, 46, 24, 2, 372, 373, 7, 33, 2, 2, 373, 374, 5, 20, 11, 2, 374, 376, 3, 2, 2, 2, 375, 370, 3, 2, 2, 2, 375, 371, 3, 2, 2, 2, 376, 43, 3, 2, 2, 2, 377, 378, 9, 4, 2, 2, 378, 45, 3, 2, 2, 2, 379, 380, 5, 68, 35, 2, 380, 47, 3, 2, 2, 2, 381, 382, 7, 7, 2, 2, 382, 387, 5, 56, 29, 2, 383, 384, 7, 34, 2, 2, 384, 386, 5, 56, 29, 2, 385, 383, 3, 2, 2, 2, 386, 389, 3, 2, 2, 2, 387, 385, 3, 2, 2, 2, 387, 388, 3, 2, 2, 2, 388, 391, 3, 2, 2, 2, 389, 387, 3, 2, 2, 2, 390, 392, 5, 50, 26, 2, 391, 390, 3, 2, 2, 2, 391, 392, 3, 2, 2, 2, 392, 49, 3, 2, 2, 2, 393, 394, 7, 37, 2, 2, 394, 395, 7, 70, 2, 2, 395, 400, 5, 56, 29, 2, 396, 397, 7, 34, 2, 2, 397, 399, 5, 56, 29, 2, 398, 396, 3, 2, 2, 2, 399, 402, 3, 2, 2, 2, 400, 398, 3, 2, 2, 2, 400, 401, 3, 2, 2, 2, 401, 403, 3, 2, 2, 2, 402, 400, 3, 2, 2, 2, 403, 404, 7, 38, 2, 2, 404, 51, 3, 2, 2, 2, 405, 406, 7, 5, 2, 2, 406, 407, 5, 40, 21, 2, 407, 53, 3, 2, 2, 2, 408, 410, 7, 9, 2, 2, 409, 411, 5, 40, 21, 2, 410, 409, 3, 2, 2, 2, 410, 411, 3, 2, 2, 2, 411, 414, 3, 2, 2, 2, 412, 413, 7, 30, 2, 2, 413, 415, 5, 66, 34, 2, 414, 412, 3, 2, 2, 2, 414, 415, 3, 2, 2, 2, 415, 55, 3, 2, 2, 2, 416, 417, 9, 5, 2, 2, 417, 57, 3, 2, 2, 2, 418, 419, 9, 4, 2, 2, 419, 59, 3, 2, 2, 2, 420, 424, 5, 64, 33, 2, 421, 424, 5, 112, 57, 2, 422, 424, 5, 106, 54, 2, 423, 420, 3, 2, 2, 2, 423, 421, 3, 2, 2, 2, 423, 422, 3, 2, 2, 2, 424, 61, 3, 2, 2, 2, 425, 432, 5, 64, 33, 2, 426, 432, 5, 112, 57, 2, 427, 432, 5, 106, 54, 2, 428, 432, 5, 34, 18, 2, 429, 432, 5, 32, 17, 2, 430, 432, 5, 26, 14, 2, 431, 425, 3, 2, 2, 2, 431, 426, 3, 2, 2, 2, 431, 427, 3, 2, 2, 2, 431, 428, 3, 2, 2, 2, 431, 429, 3, 2, 2, 2, 431, 430, 3, 2, 2, 2, 432, 63, 3, 2, 2, 2, 433, 438, 5, 68, 35, 2, 434, 435, 7, 35, 2, 2, 435, 437, 5, 68, 35, 2, 436, 434, 3, 2, 2, 2, 437, 440, 3, 2, 2, 2, 438, 436, 3, 2, 2, 2, 438, 439, 3, 2, 2, 2, 439, 65, 3, 2, 2, 2, 440, 438, 3, 2, 2, 2, 441, 446, 5, 64, 33, 2, 442, 443, 7, 34, 2, 2, 443, 445, 5, 64, 33, 2, 444, 442, 3, 2, 2, 2, 445, 448, 3, 2, 2, 2, 446, 444, 3, 2, 2, 2, 446, 447, 3, 2, 2, 2, 447, 67, 3, 2, 2, 2, 448, 446, 3, 2, 2, 2, 449, 450, 9, 6, 2, 2, 450, 69, 3, 2, 2, 2, 451, 452, 7, 62, 2, 2, 452, 71, 3, 2, 2, 2, 453, 454, 7, 63, 2, 2, 454, 73, 3, 2, 2, 2, 455, 493, 7, 45, 2, 2, 456, 493, 5, 76, 39, 2, 457, 493, 5, 104, 53, 2, 458, 493, 5, 112, 57, 2, 459, 460, 7, 37, 2, 2, 460, 465, 5, 76, 39, 2, 461, 462, 7, 34, 2, 2, 462, 464, 5, 76, 39, 2, 463, 461, 3, 2, 2, 2, 464, 467, 3, 2, 2, 2, 465, 463, 3, 2, 2, 2, 465, 466, 3, 2, 2, 2, 466, 468, 3, 2, 2, 2, 467, 465, 3, 2, 2, 2, 468, 469, 7, 38, 2, 2, 469, 493, 3, 2, 2, 2, 470, 471, 7, 37, 2, 2, 471, 476, 5, 104, 53, 2, 472, 473, 7, 34, 2, 2, 473, 475, 5, 104, 53, 2, 474, 472, 3, 2, 2, 2, 475, 478, 3, 2, 2, 2, 476, 474, 3, 2, 2, 2, 476, 477, 3, 2, 2, 2, 477, 479, 3, 2, 2, 2, 478, 476, 3, 2, 2, 2, 479, 480, 7, 38, 2, 2, 480, 493, 3, 2, 2, 2, 481, 482, 7, 37, 2, 2, 482, 487, 5, 112, 57, 2, 483, 484, 7, 34, 2, 2, 484, 486, 5, 112, 57, 2, 485, 483, 3, 2, 2, 2, 486, 489, 3, 2, 2, 2, 487, 485, 3, 2, 2, 2, 487, 488, 3, 2, 2, 2, 488, 490, 3, 2, 2, 2, 489, 487, 3, 2, 2, 2, 490, 491, 7, 38, 2, 2, 491, 493, 3, 2, 2, 2, 492, 455, 3, 2, 2, 2, 492, 456, 3, 2, 2, 2, 492, 457, 3, 2, 2, 2, 492, 458, 3, 2, 2, 2, 492, 459, 3, 2, 2, 2, 492, 470, 3, 2, 2, 2, 492, 481, 3, 2, 2, 2, 493, 75, 3, 2, 2, 2, 494, 497, 5, 108, 55, 2, 495, 497, 5, 110, 56, 2, 496, 494, 3, 2, 2, 2, 496, 495, 3, 2, 2, 2, 497, 77, 3, 2, 2, 2, 498, 499, 7, 13, 2, 2, 499, 500, 7, 28, 2, 2, 500, 79, 3, 2, 2, 2, 501, 502, 7, 11, 2, 2, 502, 507, 5, 82, 42, 2, 503, 504, 7, 34, 2, 2, 504, 506, 5, 82, 42, 2, 505, 503, 3, 2, 2, 2, 506, 509, 3, 2, 2, 2, 507, 505, 3, 2, 2, 2, 507, 508, 3, 2, 2, 2, 508, 81, 3, 2, 2, 2, 509, 507, 3, 2, 2, 2, 510, 512, 5, 20, 11, 2, 511, 513, 7, 59, 2, 2, 512, 511, 3, 2, 2, 2, 512, 513, 3, 2, 2, 2, 513, 516, 3, 2, 2, 2, 514, 515, 7, 60, 2, 2, 515, 517, 7, 61, 2, 2, 516, 514, 3, 2, 2, 2, 516, 517, 3, 2, 2, 2, 517, 83, 3, 2, 2, 2, 518, 519, 7, 14, 2, 2, 519, 520, 5, 66, 34, 2, 520, 85, 3, 2, 2, 2, 521, 522, 7, 19, 2, 2, 522, 523, 5, 66, 34, 2, 523, 87, 3, 2, 2, 2, 524, 525, 7, 15, 2, 2, 525, 526, 5, 66, 34, 2, 526, 89, 3, 2, 2, 2, 527, 532, 5, 68, 35, 2, 528, 529, 7, 35, 2, 2, 529, 531, 5, 68, 35, 2, 530, 528, 3, 2, 2, 2, 531, 534, 3, 2, 2, 2, 532, 530, 3, 2, 2, 2, 532, 533, 3, 2, 2, 2, 533, 91, 3, 2, 2, 2, 534, 532, 3, 2, 2, 2, 535, 536, 7, 16, 2, 2, 536, 541, 5, 94, 48, 2, 537, 538, 7, 34, 2, 2, 538, 540, 5, 94, 48, 2, 539, 537, 3, 2, 2, 2, 540, 543, 3, 2, 2, 2, 541, 539, 3, 2, 2, 2, 541, 542, 3, 2, 2, 2, 542, 93, 3, 2, 2, 2, 543, 541, 3, 2, 2, 2, 544, 545, 5, 64, 33, 2, 545, 546, 7, 44, 2, 2, 546, 547, 5, 90, 46, 2, 547, 95, 3, 2, 2, 2, 548, 549, 7, 3, 2, 2, 549, 550, 5, 66, 34, 2, 550, 552, 5, 112, 57, 2, 551, 553, 5, 100, 51, 2, 552, 551, 3, 2, 2, 2, 552, 553, 3, 2, 2, 2, 553, 97, 3, 2, 2, 2, 554, 555, 7, 4, 2, 2, 555, 556, 5, 66, 34, 2, 556, 557, 5, 112, 57, 2, 557, 99, 3, 2, 2, 2, 558, 563, 5, 102, 52, 2, 559, 560, 7, 34, 2, 2, 560, 562, 5, 102, 52, 2, 561, 559, 3, 2, 2, 2, 562, 565, 3, 2, 2, 2, 563, 561, 3, 2, 2, 2, 563, 564, 3, 2, 2, 2, 564, 101, 3, 2, 2, 2, 565, 563, 3, 2, 2, 2, 566, 567, 5, 68, 35, 2, 567, 568, 7, 33, 2, 2, 568, 569, 5, 74, 38, 2, 569, 103, 3, 2, 2, 2, 570, 571, 7, 51, 2, 2, 571, 105, 3, 2, 2, 2, 572, 575, 7, 29, 2, 2, 573, 575, 7, 28, 2, 2, 574, 572, 3, 2, 2, 2, 574, 573, 3, 2, 2, 2, 575, 107, 3, 2, 2, 2, 576, 577, 7, 29, 2, 2, 577, 109, 3, 2, 2, 2, 578, 579, 7, 28, 2, 2, 579, 111, 3, 2, 2, 2, 580, 581, 7, 27, 2, 2, 581, 113, 3, 2, 2, 2, 582, 583, 7, 52, 2, 2, 583, 115, 3, 2, 2, 2, 584, 585, 7, 6, 2, 2, 585, 586, 5, 118, 60, 2, 586, 117, 3, 2, 2, 2, 587, 588, 7, 37, 2, 2, 588, 589, 5, 4, 3, 2, 589, 590, 7, 38, 2, 2, 590, 119, 3, 2, 2, 2, 591, 592, 7, 17, 2, 2, 592, 596, 7, 49, 2, 2, 593, 594, 7, 17, 2, 2, 594, 596, 7, 50, 2, 2, 595, 591, 3, 2, 2, 2, 595, 593, 3, 2, 2, 2, 596, 121, 3, 2, 2, 2, 60, 133, 140, 155, 161, 169, 172, 177, 194, 203, 209, 218, 221, 228, 232, 240, 242, 249, 257, 259, 264, 271, 276, 280, 293, 296, 307, 310, 323, 331, 333, 350, 353, 357, 367, 375, 387, 391, 400, 410, 414, 423, 431, 438, 446, 465, 476, 487, 492, 496, 507, 512, 516, 532, 541, 552, 563, 574, 595] \ No newline at end of file +[3, 51485, 51898, 1421, 44986, 20307, 1543, 60043, 49729, 3, 78, 486, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4, 18, 9, 18, 4, 19, 9, 19, 4, 20, 9, 20, 4, 21, 9, 21, 4, 22, 9, 22, 4, 23, 9, 23, 4, 24, 9, 24, 4, 25, 9, 25, 4, 26, 9, 26, 4, 27, 9, 27, 4, 28, 9, 28, 4, 29, 9, 29, 4, 30, 9, 30, 4, 31, 9, 31, 4, 32, 9, 32, 4, 33, 9, 33, 4, 34, 9, 34, 4, 35, 9, 35, 4, 36, 9, 36, 4, 37, 9, 37, 4, 38, 9, 38, 4, 39, 9, 39, 4, 40, 9, 40, 4, 41, 9, 41, 4, 42, 9, 42, 4, 43, 9, 43, 4, 44, 9, 44, 4, 45, 9, 45, 3, 2, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 7, 3, 100, 10, 3, 12, 3, 14, 3, 103, 11, 3, 3, 4, 3, 4, 3, 4, 5, 4, 108, 10, 4, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 3, 5, 5, 5, 122, 10, 5, 3, 6, 3, 6, 3, 6, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 5, 7, 134, 10, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 7, 7, 141, 10, 7, 12, 7, 14, 7, 144, 11, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 5, 7, 151, 10, 7, 3, 7, 3, 7, 5, 7, 155, 10, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 3, 7, 7, 7, 163, 10, 7, 12, 7, 14, 7, 166, 11, 7, 3, 8, 3, 8, 5, 8, 170, 10, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 5, 8, 177, 10, 8, 3, 8, 3, 8, 3, 8, 5, 8, 182, 10, 8, 3, 9, 3, 9, 3, 9, 3, 9, 3, 9, 5, 9, 189, 10, 9, 3, 10, 3, 10, 3, 10, 3, 10, 5, 10, 195, 10, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 3, 10, 7, 10, 203, 10, 10, 12, 10, 14, 10, 206, 11, 10, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 5, 11, 215, 10, 11, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 3, 12, 7, 12, 223, 10, 12, 12, 12, 14, 12, 226, 11, 12, 5, 12, 228, 10, 12, 3, 12, 3, 12, 3, 13, 3, 13, 3, 13, 3, 14, 3, 14, 3, 14, 7, 14, 238, 10, 14, 12, 14, 14, 14, 241, 11, 14, 3, 15, 3, 15, 3, 15, 3, 15, 3, 15, 5, 15, 248, 10, 15, 3, 16, 3, 16, 3, 16, 3, 16, 7, 16, 254, 10, 16, 12, 16, 14, 16, 257, 11, 16, 3, 16, 5, 16, 260, 10, 16, 3, 17, 3, 17, 3, 17, 3, 17, 3, 17, 7, 17, 267, 10, 17, 12, 17, 14, 17, 270, 11, 17, 3, 17, 3, 17, 3, 18, 3, 18, 3, 18, 3, 19, 3, 19, 5, 19, 279, 10, 19, 3, 19, 3, 19, 5, 19, 283, 10, 19, 3, 20, 3, 20, 3, 20, 7, 20, 288, 10, 20, 12, 20, 14, 20, 291, 11, 20, 3, 21, 3, 21, 3, 22, 3, 22, 3, 22, 7, 22, 298, 10, 22, 12, 22, 14, 22, 301, 11, 22, 3, 23, 3, 23, 3, 24, 3, 24, 3, 24, 3, 24, 3, 24, 3, 24, 3, 24, 3, 24, 3, 24, 3, 24, 3, 24, 3, 24, 3, 24, 7, 24, 318, 10, 24, 12, 24, 14, 24, 321, 11, 24, 3, 24, 3, 24, 3, 24, 3, 24, 3, 24, 3, 24, 7, 24, 329, 10, 24, 12, 24, 14, 24, 332, 11, 24, 3, 24, 3, 24, 3, 24, 3, 24, 3, 24, 3, 24, 7, 24, 340, 10, 24, 12, 24, 14, 24, 343, 11, 24, 3, 24, 3, 24, 5, 24, 347, 10, 24, 3, 25, 3, 25, 3, 25, 3, 26, 3, 26, 3, 26, 3, 26, 7, 26, 356, 10, 26, 12, 26, 14, 26, 359, 11, 26, 3, 27, 3, 27, 5, 27, 363, 10, 27, 3, 27, 3, 27, 5, 27, 367, 10, 27, 3, 28, 3, 28, 3, 28, 3, 28, 7, 28, 373, 10, 28, 12, 28, 14, 28, 376, 11, 28, 3, 28, 3, 28, 3, 28, 3, 28, 7, 28, 382, 10, 28, 12, 28, 14, 28, 385, 11, 28, 5, 28, 387, 10, 28, 3, 29, 3, 29, 3, 29, 3, 29, 7, 29, 393, 10, 29, 12, 29, 14, 29, 396, 11, 29, 3, 30, 3, 30, 3, 30, 3, 30, 7, 30, 402, 10, 30, 12, 30, 14, 30, 405, 11, 30, 3, 31, 3, 31, 3, 31, 3, 31, 3, 32, 3, 32, 3, 32, 3, 32, 5, 32, 415, 10, 32, 3, 33, 3, 33, 3, 33, 3, 33, 3, 34, 3, 34, 3, 34, 3, 35, 3, 35, 3, 35, 7, 35, 427, 10, 35, 12, 35, 14, 35, 430, 11, 35, 3, 36, 3, 36, 3, 36, 3, 36, 3, 37, 3, 37, 3, 38, 3, 38, 5, 38, 440, 10, 38, 3, 39, 5, 39, 443, 10, 39, 3, 39, 3, 39, 3, 40, 5, 40, 448, 10, 40, 3, 40, 3, 40, 3, 41, 3, 41, 3, 42, 3, 42, 3, 43, 3, 43, 3, 43, 3, 43, 5, 43, 460, 10, 43, 3, 44, 3, 44, 3, 44, 3, 44, 5, 44, 466, 10, 44, 3, 44, 3, 44, 3, 44, 3, 44, 7, 44, 472, 10, 44, 12, 44, 14, 44, 475, 11, 44, 5, 44, 477, 10, 44, 3, 45, 3, 45, 3, 45, 5, 45, 482, 10, 45, 3, 45, 3, 45, 3, 45, 2, 2, 5, 4, 12, 18, 46, 2, 2, 4, 2, 6, 2, 8, 2, 10, 2, 12, 2, 14, 2, 16, 2, 18, 2, 20, 2, 22, 2, 24, 2, 26, 2, 28, 2, 30, 2, 32, 2, 34, 2, 36, 2, 38, 2, 40, 2, 42, 2, 44, 2, 46, 2, 48, 2, 50, 2, 52, 2, 54, 2, 56, 2, 58, 2, 60, 2, 62, 2, 64, 2, 66, 2, 68, 2, 70, 2, 72, 2, 74, 2, 76, 2, 78, 2, 80, 2, 82, 2, 84, 2, 86, 2, 88, 2, 2, 10, 3, 2, 58, 59, 3, 2, 60, 62, 3, 2, 74, 75, 3, 2, 65, 66, 4, 2, 29, 29, 32, 32, 3, 2, 35, 36, 4, 2, 34, 34, 48, 48, 3, 2, 52, 57, 2, 516, 2, 90, 3, 2, 2, 2, 4, 93, 3, 2, 2, 2, 6, 107, 3, 2, 2, 2, 8, 121, 3, 2, 2, 2, 10, 123, 3, 2, 2, 2, 12, 154, 3, 2, 2, 2, 14, 181, 3, 2, 2, 2, 16, 188, 3, 2, 2, 2, 18, 194, 3, 2, 2, 2, 20, 214, 3, 2, 2, 2, 22, 216, 3, 2, 2, 2, 24, 231, 3, 2, 2, 2, 26, 234, 3, 2, 2, 2, 28, 247, 3, 2, 2, 2, 30, 249, 3, 2, 2, 2, 32, 261, 3, 2, 2, 2, 34, 273, 3, 2, 2, 2, 36, 276, 3, 2, 2, 2, 38, 284, 3, 2, 2, 2, 40, 292, 3, 2, 2, 2, 42, 294, 3, 2, 2, 2, 44, 302, 3, 2, 2, 2, 46, 346, 3, 2, 2, 2, 48, 348, 3, 2, 2, 2, 50, 351, 3, 2, 2, 2, 52, 360, 3, 2, 2, 2, 54, 386, 3, 2, 2, 2, 56, 388, 3, 2, 2, 2, 58, 397, 3, 2, 2, 2, 60, 406, 3, 2, 2, 2, 62, 410, 3, 2, 2, 2, 64, 416, 3, 2, 2, 2, 66, 420, 3, 2, 2, 2, 68, 423, 3, 2, 2, 2, 70, 431, 3, 2, 2, 2, 72, 435, 3, 2, 2, 2, 74, 439, 3, 2, 2, 2, 76, 442, 3, 2, 2, 2, 78, 447, 3, 2, 2, 2, 80, 451, 3, 2, 2, 2, 82, 453, 3, 2, 2, 2, 84, 459, 3, 2, 2, 2, 86, 461, 3, 2, 2, 2, 88, 481, 3, 2, 2, 2, 90, 91, 5, 4, 3, 2, 91, 92, 7, 2, 2, 3, 92, 3, 3, 2, 2, 2, 93, 94, 8, 3, 1, 2, 94, 95, 5, 6, 4, 2, 95, 101, 3, 2, 2, 2, 96, 97, 12, 3, 2, 2, 97, 98, 7, 23, 2, 2, 98, 100, 5, 8, 5, 2, 99, 96, 3, 2, 2, 2, 100, 103, 3, 2, 2, 2, 101, 99, 3, 2, 2, 2, 101, 102, 3, 2, 2, 2, 102, 5, 3, 2, 2, 2, 103, 101, 3, 2, 2, 2, 104, 108, 5, 30, 16, 2, 105, 108, 5, 24, 13, 2, 106, 108, 5, 84, 43, 2, 107, 104, 3, 2, 2, 2, 107, 105, 3, 2, 2, 2, 107, 106, 3, 2, 2, 2, 108, 7, 3, 2, 2, 2, 109, 122, 5, 34, 18, 2, 110, 122, 5, 48, 25, 2, 111, 122, 5, 54, 28, 2, 112, 122, 5, 50, 26, 2, 113, 122, 5, 36, 19, 2, 114, 122, 5, 10, 6, 2, 115, 122, 5, 56, 29, 2, 116, 122, 5, 58, 30, 2, 117, 122, 5, 62, 32, 2, 118, 122, 5, 64, 33, 2, 119, 122, 5, 86, 44, 2, 120, 122, 5, 66, 34, 2, 121, 109, 3, 2, 2, 2, 121, 110, 3, 2, 2, 2, 121, 111, 3, 2, 2, 2, 121, 112, 3, 2, 2, 2, 121, 113, 3, 2, 2, 2, 121, 114, 3, 2, 2, 2, 121, 115, 3, 2, 2, 2, 121, 116, 3, 2, 2, 2, 121, 117, 3, 2, 2, 2, 121, 118, 3, 2, 2, 2, 121, 119, 3, 2, 2, 2, 121, 120, 3, 2, 2, 2, 122, 9, 3, 2, 2, 2, 123, 124, 7, 18, 2, 2, 124, 125, 5, 12, 7, 2, 125, 11, 3, 2, 2, 2, 126, 127, 8, 7, 1, 2, 127, 128, 7, 41, 2, 2, 128, 155, 5, 12, 7, 9, 129, 155, 5, 16, 9, 2, 130, 155, 5, 14, 8, 2, 131, 133, 5, 16, 9, 2, 132, 134, 7, 41, 2, 2, 133, 132, 3, 2, 2, 2, 133, 134, 3, 2, 2, 2, 134, 135, 3, 2, 2, 2, 135, 136, 7, 38, 2, 2, 136, 137, 7, 37, 2, 2, 137, 142, 5, 16, 9, 2, 138, 139, 7, 31, 2, 2, 139, 141, 5, 16, 9, 2, 140, 138, 3, 2, 2, 2, 141, 144, 3, 2, 2, 2, 142, 140, 3, 2, 2, 2, 142, 143, 3, 2, 2, 2, 143, 145, 3, 2, 2, 2, 144, 142, 3, 2, 2, 2, 145, 146, 7, 47, 2, 2, 146, 155, 3, 2, 2, 2, 147, 148, 5, 16, 9, 2, 148, 150, 7, 39, 2, 2, 149, 151, 7, 41, 2, 2, 150, 149, 3, 2, 2, 2, 150, 151, 3, 2, 2, 2, 151, 152, 3, 2, 2, 2, 152, 153, 7, 42, 2, 2, 153, 155, 3, 2, 2, 2, 154, 126, 3, 2, 2, 2, 154, 129, 3, 2, 2, 2, 154, 130, 3, 2, 2, 2, 154, 131, 3, 2, 2, 2, 154, 147, 3, 2, 2, 2, 155, 164, 3, 2, 2, 2, 156, 157, 12, 6, 2, 2, 157, 158, 7, 28, 2, 2, 158, 163, 5, 12, 7, 7, 159, 160, 12, 5, 2, 2, 160, 161, 7, 44, 2, 2, 161, 163, 5, 12, 7, 6, 162, 156, 3, 2, 2, 2, 162, 159, 3, 2, 2, 2, 163, 166, 3, 2, 2, 2, 164, 162, 3, 2, 2, 2, 164, 165, 3, 2, 2, 2, 165, 13, 3, 2, 2, 2, 166, 164, 3, 2, 2, 2, 167, 169, 5, 16, 9, 2, 168, 170, 7, 41, 2, 2, 169, 168, 3, 2, 2, 2, 169, 170, 3, 2, 2, 2, 170, 171, 3, 2, 2, 2, 171, 172, 7, 40, 2, 2, 172, 173, 5, 80, 41, 2, 173, 182, 3, 2, 2, 2, 174, 176, 5, 16, 9, 2, 175, 177, 7, 41, 2, 2, 176, 175, 3, 2, 2, 2, 176, 177, 3, 2, 2, 2, 177, 178, 3, 2, 2, 2, 178, 179, 7, 46, 2, 2, 179, 180, 5, 80, 41, 2, 180, 182, 3, 2, 2, 2, 181, 167, 3, 2, 2, 2, 181, 174, 3, 2, 2, 2, 182, 15, 3, 2, 2, 2, 183, 189, 5, 18, 10, 2, 184, 185, 5, 18, 10, 2, 185, 186, 5, 82, 42, 2, 186, 187, 5, 18, 10, 2, 187, 189, 3, 2, 2, 2, 188, 183, 3, 2, 2, 2, 188, 184, 3, 2, 2, 2, 189, 17, 3, 2, 2, 2, 190, 191, 8, 10, 1, 2, 191, 195, 5, 20, 11, 2, 192, 193, 9, 2, 2, 2, 193, 195, 5, 18, 10, 5, 194, 190, 3, 2, 2, 2, 194, 192, 3, 2, 2, 2, 195, 204, 3, 2, 2, 2, 196, 197, 12, 4, 2, 2, 197, 198, 9, 3, 2, 2, 198, 203, 5, 18, 10, 5, 199, 200, 12, 3, 2, 2, 200, 201, 9, 2, 2, 2, 201, 203, 5, 18, 10, 4, 202, 196, 3, 2, 2, 2, 202, 199, 3, 2, 2, 2, 203, 206, 3, 2, 2, 2, 204, 202, 3, 2, 2, 2, 204, 205, 3, 2, 2, 2, 205, 19, 3, 2, 2, 2, 206, 204, 3, 2, 2, 2, 207, 215, 5, 46, 24, 2, 208, 215, 5, 42, 22, 2, 209, 215, 5, 22, 12, 2, 210, 211, 7, 37, 2, 2, 211, 212, 5, 12, 7, 2, 212, 213, 7, 47, 2, 2, 213, 215, 3, 2, 2, 2, 214, 207, 3, 2, 2, 2, 214, 208, 3, 2, 2, 2, 214, 209, 3, 2, 2, 2, 214, 210, 3, 2, 2, 2, 215, 21, 3, 2, 2, 2, 216, 217, 5, 44, 23, 2, 217, 227, 7, 37, 2, 2, 218, 228, 7, 60, 2, 2, 219, 224, 5, 12, 7, 2, 220, 221, 7, 31, 2, 2, 221, 223, 5, 12, 7, 2, 222, 220, 3, 2, 2, 2, 223, 226, 3, 2, 2, 2, 224, 222, 3, 2, 2, 2, 224, 225, 3, 2, 2, 2, 225, 228, 3, 2, 2, 2, 226, 224, 3, 2, 2, 2, 227, 218, 3, 2, 2, 2, 227, 219, 3, 2, 2, 2, 227, 228, 3, 2, 2, 2, 228, 229, 3, 2, 2, 2, 229, 230, 7, 47, 2, 2, 230, 23, 3, 2, 2, 2, 231, 232, 7, 14, 2, 2, 232, 233, 5, 26, 14, 2, 233, 25, 3, 2, 2, 2, 234, 239, 5, 28, 15, 2, 235, 236, 7, 31, 2, 2, 236, 238, 5, 28, 15, 2, 237, 235, 3, 2, 2, 2, 238, 241, 3, 2, 2, 2, 239, 237, 3, 2, 2, 2, 239, 240, 3, 2, 2, 2, 240, 27, 3, 2, 2, 2, 241, 239, 3, 2, 2, 2, 242, 248, 5, 12, 7, 2, 243, 244, 5, 42, 22, 2, 244, 245, 7, 30, 2, 2, 245, 246, 5, 12, 7, 2, 246, 248, 3, 2, 2, 2, 247, 242, 3, 2, 2, 2, 247, 243, 3, 2, 2, 2, 248, 29, 3, 2, 2, 2, 249, 250, 7, 7, 2, 2, 250, 255, 5, 40, 21, 2, 251, 252, 7, 31, 2, 2, 252, 254, 5, 40, 21, 2, 253, 251, 3, 2, 2, 2, 254, 257, 3, 2, 2, 2, 255, 253, 3, 2, 2, 2, 255, 256, 3, 2, 2, 2, 256, 259, 3, 2, 2, 2, 257, 255, 3, 2, 2, 2, 258, 260, 5, 32, 17, 2, 259, 258, 3, 2, 2, 2, 259, 260, 3, 2, 2, 2, 260, 31, 3, 2, 2, 2, 261, 262, 7, 63, 2, 2, 262, 263, 7, 71, 2, 2, 263, 268, 5, 40, 21, 2, 264, 265, 7, 31, 2, 2, 265, 267, 5, 40, 21, 2, 266, 264, 3, 2, 2, 2, 267, 270, 3, 2, 2, 2, 268, 266, 3, 2, 2, 2, 268, 269, 3, 2, 2, 2, 269, 271, 3, 2, 2, 2, 270, 268, 3, 2, 2, 2, 271, 272, 7, 64, 2, 2, 272, 33, 3, 2, 2, 2, 273, 274, 7, 6, 2, 2, 274, 275, 5, 26, 14, 2, 275, 35, 3, 2, 2, 2, 276, 278, 7, 17, 2, 2, 277, 279, 5, 26, 14, 2, 278, 277, 3, 2, 2, 2, 278, 279, 3, 2, 2, 2, 279, 282, 3, 2, 2, 2, 280, 281, 7, 27, 2, 2, 281, 283, 5, 38, 20, 2, 282, 280, 3, 2, 2, 2, 282, 283, 3, 2, 2, 2, 283, 37, 3, 2, 2, 2, 284, 289, 5, 42, 22, 2, 285, 286, 7, 31, 2, 2, 286, 288, 5, 42, 22, 2, 287, 285, 3, 2, 2, 2, 288, 291, 3, 2, 2, 2, 289, 287, 3, 2, 2, 2, 289, 290, 3, 2, 2, 2, 290, 39, 3, 2, 2, 2, 291, 289, 3, 2, 2, 2, 292, 293, 9, 4, 2, 2, 293, 41, 3, 2, 2, 2, 294, 299, 5, 44, 23, 2, 295, 296, 7, 33, 2, 2, 296, 298, 5, 44, 23, 2, 297, 295, 3, 2, 2, 2, 298, 301, 3, 2, 2, 2, 299, 297, 3, 2, 2, 2, 299, 300, 3, 2, 2, 2, 300, 43, 3, 2, 2, 2, 301, 299, 3, 2, 2, 2, 302, 303, 9, 5, 2, 2, 303, 45, 3, 2, 2, 2, 304, 347, 7, 42, 2, 2, 305, 306, 5, 78, 40, 2, 306, 307, 7, 65, 2, 2, 307, 347, 3, 2, 2, 2, 308, 347, 5, 76, 39, 2, 309, 347, 5, 78, 40, 2, 310, 347, 5, 72, 37, 2, 311, 347, 7, 45, 2, 2, 312, 347, 5, 80, 41, 2, 313, 314, 7, 63, 2, 2, 314, 319, 5, 74, 38, 2, 315, 316, 7, 31, 2, 2, 316, 318, 5, 74, 38, 2, 317, 315, 3, 2, 2, 2, 318, 321, 3, 2, 2, 2, 319, 317, 3, 2, 2, 2, 319, 320, 3, 2, 2, 2, 320, 322, 3, 2, 2, 2, 321, 319, 3, 2, 2, 2, 322, 323, 7, 64, 2, 2, 323, 347, 3, 2, 2, 2, 324, 325, 7, 63, 2, 2, 325, 330, 5, 72, 37, 2, 326, 327, 7, 31, 2, 2, 327, 329, 5, 72, 37, 2, 328, 326, 3, 2, 2, 2, 329, 332, 3, 2, 2, 2, 330, 328, 3, 2, 2, 2, 330, 331, 3, 2, 2, 2, 331, 333, 3, 2, 2, 2, 332, 330, 3, 2, 2, 2, 333, 334, 7, 64, 2, 2, 334, 347, 3, 2, 2, 2, 335, 336, 7, 63, 2, 2, 336, 341, 5, 80, 41, 2, 337, 338, 7, 31, 2, 2, 338, 340, 5, 80, 41, 2, 339, 337, 3, 2, 2, 2, 340, 343, 3, 2, 2, 2, 341, 339, 3, 2, 2, 2, 341, 342, 3, 2, 2, 2, 342, 344, 3, 2, 2, 2, 343, 341, 3, 2, 2, 2, 344, 345, 7, 64, 2, 2, 345, 347, 3, 2, 2, 2, 346, 304, 3, 2, 2, 2, 346, 305, 3, 2, 2, 2, 346, 308, 3, 2, 2, 2, 346, 309, 3, 2, 2, 2, 346, 310, 3, 2, 2, 2, 346, 311, 3, 2, 2, 2, 346, 312, 3, 2, 2, 2, 346, 313, 3, 2, 2, 2, 346, 324, 3, 2, 2, 2, 346, 335, 3, 2, 2, 2, 347, 47, 3, 2, 2, 2, 348, 349, 7, 10, 2, 2, 349, 350, 7, 25, 2, 2, 350, 49, 3, 2, 2, 2, 351, 352, 7, 16, 2, 2, 352, 357, 5, 52, 27, 2, 353, 354, 7, 31, 2, 2, 354, 356, 5, 52, 27, 2, 355, 353, 3, 2, 2, 2, 356, 359, 3, 2, 2, 2, 357, 355, 3, 2, 2, 2, 357, 358, 3, 2, 2, 2, 358, 51, 3, 2, 2, 2, 359, 357, 3, 2, 2, 2, 360, 362, 5, 12, 7, 2, 361, 363, 9, 6, 2, 2, 362, 361, 3, 2, 2, 2, 362, 363, 3, 2, 2, 2, 363, 366, 3, 2, 2, 2, 364, 365, 7, 43, 2, 2, 365, 367, 9, 7, 2, 2, 366, 364, 3, 2, 2, 2, 366, 367, 3, 2, 2, 2, 367, 53, 3, 2, 2, 2, 368, 369, 7, 9, 2, 2, 369, 374, 5, 40, 21, 2, 370, 371, 7, 31, 2, 2, 371, 373, 5, 40, 21, 2, 372, 370, 3, 2, 2, 2, 373, 376, 3, 2, 2, 2, 374, 372, 3, 2, 2, 2, 374, 375, 3, 2, 2, 2, 375, 387, 3, 2, 2, 2, 376, 374, 3, 2, 2, 2, 377, 378, 7, 12, 2, 2, 378, 383, 5, 40, 21, 2, 379, 380, 7, 31, 2, 2, 380, 382, 5, 40, 21, 2, 381, 379, 3, 2, 2, 2, 382, 385, 3, 2, 2, 2, 383, 381, 3, 2, 2, 2, 383, 384, 3, 2, 2, 2, 384, 387, 3, 2, 2, 2, 385, 383, 3, 2, 2, 2, 386, 368, 3, 2, 2, 2, 386, 377, 3, 2, 2, 2, 387, 55, 3, 2, 2, 2, 388, 389, 7, 4, 2, 2, 389, 394, 5, 40, 21, 2, 390, 391, 7, 31, 2, 2, 391, 393, 5, 40, 21, 2, 392, 390, 3, 2, 2, 2, 393, 396, 3, 2, 2, 2, 394, 392, 3, 2, 2, 2, 394, 395, 3, 2, 2, 2, 395, 57, 3, 2, 2, 2, 396, 394, 3, 2, 2, 2, 397, 398, 7, 13, 2, 2, 398, 403, 5, 60, 31, 2, 399, 400, 7, 31, 2, 2, 400, 402, 5, 60, 31, 2, 401, 399, 3, 2, 2, 2, 402, 405, 3, 2, 2, 2, 403, 401, 3, 2, 2, 2, 403, 404, 3, 2, 2, 2, 404, 59, 3, 2, 2, 2, 405, 403, 3, 2, 2, 2, 406, 407, 5, 40, 21, 2, 407, 408, 7, 70, 2, 2, 408, 409, 5, 40, 21, 2, 409, 61, 3, 2, 2, 2, 410, 411, 7, 3, 2, 2, 411, 412, 5, 20, 11, 2, 412, 414, 5, 80, 41, 2, 413, 415, 5, 68, 35, 2, 414, 413, 3, 2, 2, 2, 414, 415, 3, 2, 2, 2, 415, 63, 3, 2, 2, 2, 416, 417, 7, 8, 2, 2, 417, 418, 5, 20, 11, 2, 418, 419, 5, 80, 41, 2, 419, 65, 3, 2, 2, 2, 420, 421, 7, 11, 2, 2, 421, 422, 5, 40, 21, 2, 422, 67, 3, 2, 2, 2, 423, 428, 5, 70, 36, 2, 424, 425, 7, 31, 2, 2, 425, 427, 5, 70, 36, 2, 426, 424, 3, 2, 2, 2, 427, 430, 3, 2, 2, 2, 428, 426, 3, 2, 2, 2, 428, 429, 3, 2, 2, 2, 429, 69, 3, 2, 2, 2, 430, 428, 3, 2, 2, 2, 431, 432, 5, 44, 23, 2, 432, 433, 7, 30, 2, 2, 433, 434, 5, 46, 24, 2, 434, 71, 3, 2, 2, 2, 435, 436, 9, 8, 2, 2, 436, 73, 3, 2, 2, 2, 437, 440, 5, 76, 39, 2, 438, 440, 5, 78, 40, 2, 439, 437, 3, 2, 2, 2, 439, 438, 3, 2, 2, 2, 440, 75, 3, 2, 2, 2, 441, 443, 9, 2, 2, 2, 442, 441, 3, 2, 2, 2, 442, 443, 3, 2, 2, 2, 443, 444, 3, 2, 2, 2, 444, 445, 7, 26, 2, 2, 445, 77, 3, 2, 2, 2, 446, 448, 9, 2, 2, 2, 447, 446, 3, 2, 2, 2, 447, 448, 3, 2, 2, 2, 448, 449, 3, 2, 2, 2, 449, 450, 7, 25, 2, 2, 450, 79, 3, 2, 2, 2, 451, 452, 7, 24, 2, 2, 452, 81, 3, 2, 2, 2, 453, 454, 9, 9, 2, 2, 454, 83, 3, 2, 2, 2, 455, 456, 7, 15, 2, 2, 456, 460, 7, 49, 2, 2, 457, 458, 7, 15, 2, 2, 458, 460, 7, 50, 2, 2, 459, 455, 3, 2, 2, 2, 459, 457, 3, 2, 2, 2, 460, 85, 3, 2, 2, 2, 461, 462, 7, 5, 2, 2, 462, 465, 5, 40, 21, 2, 463, 464, 7, 72, 2, 2, 464, 466, 5, 40, 21, 2, 465, 463, 3, 2, 2, 2, 465, 466, 3, 2, 2, 2, 466, 476, 3, 2, 2, 2, 467, 468, 7, 73, 2, 2, 468, 473, 5, 88, 45, 2, 469, 470, 7, 31, 2, 2, 470, 472, 5, 88, 45, 2, 471, 469, 3, 2, 2, 2, 472, 475, 3, 2, 2, 2, 473, 471, 3, 2, 2, 2, 473, 474, 3, 2, 2, 2, 474, 477, 3, 2, 2, 2, 475, 473, 3, 2, 2, 2, 476, 467, 3, 2, 2, 2, 476, 477, 3, 2, 2, 2, 477, 87, 3, 2, 2, 2, 478, 479, 5, 40, 21, 2, 479, 480, 7, 30, 2, 2, 480, 482, 3, 2, 2, 2, 481, 478, 3, 2, 2, 2, 481, 482, 3, 2, 2, 2, 482, 483, 3, 2, 2, 2, 483, 484, 5, 40, 21, 2, 484, 89, 3, 2, 2, 2, 52, 101, 107, 121, 133, 142, 150, 154, 162, 164, 169, 176, 181, 188, 194, 202, 204, 214, 224, 227, 239, 247, 255, 259, 268, 278, 282, 289, 299, 319, 330, 341, 346, 357, 362, 366, 374, 383, 386, 394, 403, 414, 428, 439, 442, 447, 459, 465, 473, 476, 481] \ No newline at end of file diff --git a/packages/kbn-monaco/src/esql/antlr/esql_parser.tokens b/packages/kbn-monaco/src/esql/antlr/esql_parser.tokens index b72e97b9a2961..c3160ce1f6472 100644 --- a/packages/kbn-monaco/src/esql/antlr/esql_parser.tokens +++ b/packages/kbn-monaco/src/esql/antlr/esql_parser.tokens @@ -1,98 +1,93 @@ DISSECT=1 -GROK=2 -EVAL=3 -EXPLAIN=4 +DROP=2 +ENRICH=3 +EVAL=4 FROM=5 -ROW=6 -STATS=7 -WHERE=8 -SORT=9 -MV_EXPAND=10 -LIMIT=11 -PROJECT=12 -DROP=13 -RENAME=14 -SHOW=15 -ENRICH=16 -KEEP=17 +GROK=6 +KEEP=7 +LIMIT=8 +MV_EXPAND=9 +PROJECT=10 +RENAME=11 +ROW=12 +SHOW=13 +SORT=14 +STATS=15 +WHERE=16 +UNKNOWN_CMD=17 LINE_COMMENT=18 MULTILINE_COMMENT=19 WS=20 -EXPLAIN_WS=21 -EXPLAIN_LINE_COMMENT=22 -EXPLAIN_MULTILINE_COMMENT=23 -PIPE=24 -STRING=25 -INTEGER_LITERAL=26 -DECIMAL_LITERAL=27 -BY=28 -DATE_LITERAL=29 -AND=30 -ASSIGN=31 -COMMA=32 -DOT=33 -LP=34 -OPENING_BRACKET=35 -CLOSING_BRACKET=36 -NOT=37 +PIPE=21 +STRING=22 +INTEGER_LITERAL=23 +DECIMAL_LITERAL=24 +BY=25 +AND=26 +ASC=27 +ASSIGN=28 +COMMA=29 +DESC=30 +DOT=31 +FALSE=32 +FIRST=33 +LAST=34 +LP=35 +IN=36 +IS=37 LIKE=38 -RLIKE=39 -IN=40 -IS=41 -AS=42 -NULL=43 -OR=44 +NOT=39 +NULL=40 +NULLS=41 +OR=42 +PARAM=43 +RLIKE=44 RP=45 -UNDERSCORE=46 +TRUE=46 INFO=47 FUNCTIONS=48 -BOOLEAN_VALUE=49 -COMPARISON_OPERATOR=50 -PLUS=51 -MINUS=52 -ASTERISK=53 -SLASH=54 -PERCENT=55 -TEN=56 -ORDERING=57 -NULLS_ORDERING=58 -NULLS_ORDERING_DIRECTION=59 -MATH_FUNCTION=60 -UNARY_FUNCTION=61 -WHERE_FUNCTIONS=62 +UNDERSCORE=49 +EQ=50 +NEQ=51 +LT=52 +LTE=53 +GT=54 +GTE=55 +PLUS=56 +MINUS=57 +ASTERISK=58 +SLASH=59 +PERCENT=60 +OPENING_BRACKET=61 +CLOSING_BRACKET=62 UNQUOTED_IDENTIFIER=63 QUOTED_IDENTIFIER=64 EXPR_LINE_COMMENT=65 EXPR_MULTILINE_COMMENT=66 EXPR_WS=67 -METADATA=68 -SRC_UNQUOTED_IDENTIFIER=69 -SRC_QUOTED_IDENTIFIER=70 -SRC_LINE_COMMENT=71 -SRC_MULTILINE_COMMENT=72 -SRC_WS=73 -ON=74 -WITH=75 -ENR_UNQUOTED_IDENTIFIER=76 -ENR_QUOTED_IDENTIFIER=77 -ENR_LINE_COMMENT=78 -ENR_MULTILINE_COMMENT=79 -ENR_WS=80 -EXPLAIN_PIPE=81 -'by'=28 -'and'=30 -'.'=33 -'('=34 -']'=36 -'or'=44 +AS=68 +METADATA=69 +ON=70 +WITH=71 +SRC_UNQUOTED_IDENTIFIER=72 +SRC_QUOTED_IDENTIFIER=73 +SRC_LINE_COMMENT=74 +SRC_MULTILINE_COMMENT=75 +SRC_WS=76 +'.'=31 +'('=35 +'?'=43 ')'=45 -'_'=46 -'info'=47 -'functions'=48 -'+'=51 -'-'=52 -'*'=53 -'/'=54 -'%'=55 -'10'=56 -'nulls'=58 +'_'=49 +'=='=50 +'!='=51 +'<'=52 +'<='=53 +'>'=54 +'>='=55 +'+'=56 +'-'=57 +'*'=58 +'/'=59 +'%'=60 +']'=62 diff --git a/packages/kbn-monaco/src/esql/antlr/esql_parser.ts b/packages/kbn-monaco/src/esql/antlr/esql_parser.ts index 494ffadd8aad9..8e0a4ca8199b8 100644 --- a/packages/kbn-monaco/src/esql/antlr/esql_parser.ts +++ b/packages/kbn-monaco/src/esql/antlr/esql_parser.ts @@ -28,162 +28,136 @@ import { esql_parserListener } from "./esql_parserListener"; export class esql_parser extends Parser { public static readonly DISSECT = 1; - public static readonly GROK = 2; - public static readonly EVAL = 3; - public static readonly EXPLAIN = 4; + public static readonly DROP = 2; + public static readonly ENRICH = 3; + public static readonly EVAL = 4; public static readonly FROM = 5; - public static readonly ROW = 6; - public static readonly STATS = 7; - public static readonly WHERE = 8; - public static readonly SORT = 9; - public static readonly MV_EXPAND = 10; - public static readonly LIMIT = 11; - public static readonly PROJECT = 12; - public static readonly DROP = 13; - public static readonly RENAME = 14; - public static readonly SHOW = 15; - public static readonly ENRICH = 16; - public static readonly KEEP = 17; + public static readonly GROK = 6; + public static readonly KEEP = 7; + public static readonly LIMIT = 8; + public static readonly MV_EXPAND = 9; + public static readonly PROJECT = 10; + public static readonly RENAME = 11; + public static readonly ROW = 12; + public static readonly SHOW = 13; + public static readonly SORT = 14; + public static readonly STATS = 15; + public static readonly WHERE = 16; + public static readonly UNKNOWN_CMD = 17; public static readonly LINE_COMMENT = 18; public static readonly MULTILINE_COMMENT = 19; public static readonly WS = 20; - public static readonly EXPLAIN_WS = 21; - public static readonly EXPLAIN_LINE_COMMENT = 22; - public static readonly EXPLAIN_MULTILINE_COMMENT = 23; - public static readonly PIPE = 24; - public static readonly STRING = 25; - public static readonly INTEGER_LITERAL = 26; - public static readonly DECIMAL_LITERAL = 27; - public static readonly BY = 28; - public static readonly DATE_LITERAL = 29; - public static readonly AND = 30; - public static readonly ASSIGN = 31; - public static readonly COMMA = 32; - public static readonly DOT = 33; - public static readonly LP = 34; - public static readonly OPENING_BRACKET = 35; - public static readonly CLOSING_BRACKET = 36; - public static readonly NOT = 37; + public static readonly PIPE = 21; + public static readonly STRING = 22; + public static readonly INTEGER_LITERAL = 23; + public static readonly DECIMAL_LITERAL = 24; + public static readonly BY = 25; + public static readonly AND = 26; + public static readonly ASC = 27; + public static readonly ASSIGN = 28; + public static readonly COMMA = 29; + public static readonly DESC = 30; + public static readonly DOT = 31; + public static readonly FALSE = 32; + public static readonly FIRST = 33; + public static readonly LAST = 34; + public static readonly LP = 35; + public static readonly IN = 36; + public static readonly IS = 37; public static readonly LIKE = 38; - public static readonly RLIKE = 39; - public static readonly IN = 40; - public static readonly IS = 41; - public static readonly AS = 42; - public static readonly NULL = 43; - public static readonly OR = 44; + public static readonly NOT = 39; + public static readonly NULL = 40; + public static readonly NULLS = 41; + public static readonly OR = 42; + public static readonly PARAM = 43; + public static readonly RLIKE = 44; public static readonly RP = 45; - public static readonly UNDERSCORE = 46; + public static readonly TRUE = 46; public static readonly INFO = 47; public static readonly FUNCTIONS = 48; - public static readonly BOOLEAN_VALUE = 49; - public static readonly COMPARISON_OPERATOR = 50; - public static readonly PLUS = 51; - public static readonly MINUS = 52; - public static readonly ASTERISK = 53; - public static readonly SLASH = 54; - public static readonly PERCENT = 55; - public static readonly TEN = 56; - public static readonly ORDERING = 57; - public static readonly NULLS_ORDERING = 58; - public static readonly NULLS_ORDERING_DIRECTION = 59; - public static readonly MATH_FUNCTION = 60; - public static readonly UNARY_FUNCTION = 61; - public static readonly WHERE_FUNCTIONS = 62; + public static readonly UNDERSCORE = 49; + public static readonly EQ = 50; + public static readonly NEQ = 51; + public static readonly LT = 52; + public static readonly LTE = 53; + public static readonly GT = 54; + public static readonly GTE = 55; + public static readonly PLUS = 56; + public static readonly MINUS = 57; + public static readonly ASTERISK = 58; + public static readonly SLASH = 59; + public static readonly PERCENT = 60; + public static readonly OPENING_BRACKET = 61; + public static readonly CLOSING_BRACKET = 62; public static readonly UNQUOTED_IDENTIFIER = 63; public static readonly QUOTED_IDENTIFIER = 64; public static readonly EXPR_LINE_COMMENT = 65; public static readonly EXPR_MULTILINE_COMMENT = 66; public static readonly EXPR_WS = 67; - public static readonly METADATA = 68; - public static readonly SRC_UNQUOTED_IDENTIFIER = 69; - public static readonly SRC_QUOTED_IDENTIFIER = 70; - public static readonly SRC_LINE_COMMENT = 71; - public static readonly SRC_MULTILINE_COMMENT = 72; - public static readonly SRC_WS = 73; - public static readonly ON = 74; - public static readonly WITH = 75; - public static readonly ENR_UNQUOTED_IDENTIFIER = 76; - public static readonly ENR_QUOTED_IDENTIFIER = 77; - public static readonly ENR_LINE_COMMENT = 78; - public static readonly ENR_MULTILINE_COMMENT = 79; - public static readonly ENR_WS = 80; - public static readonly EXPLAIN_PIPE = 81; + public static readonly AS = 68; + public static readonly METADATA = 69; + public static readonly ON = 70; + public static readonly WITH = 71; + public static readonly SRC_UNQUOTED_IDENTIFIER = 72; + public static readonly SRC_QUOTED_IDENTIFIER = 73; + public static readonly SRC_LINE_COMMENT = 74; + public static readonly SRC_MULTILINE_COMMENT = 75; + public static readonly SRC_WS = 76; public static readonly RULE_singleStatement = 0; public static readonly RULE_query = 1; public static readonly RULE_sourceCommand = 2; public static readonly RULE_processingCommand = 3; - public static readonly RULE_enrichCommand = 4; - public static readonly RULE_enrichWithClause = 5; - public static readonly RULE_mvExpandCommand = 6; - public static readonly RULE_whereCommand = 7; - public static readonly RULE_whereBooleanExpression = 8; - public static readonly RULE_booleanExpression = 9; - public static readonly RULE_regexBooleanExpression = 10; - public static readonly RULE_valueExpression = 11; - public static readonly RULE_comparison = 12; - public static readonly RULE_mathFn = 13; - public static readonly RULE_mathEvalFn = 14; - public static readonly RULE_dateExpression = 15; - public static readonly RULE_operatorExpression = 16; - public static readonly RULE_primaryExpression = 17; - public static readonly RULE_rowCommand = 18; - public static readonly RULE_fields = 19; - public static readonly RULE_field = 20; - public static readonly RULE_enrichFieldIdentifier = 21; - public static readonly RULE_userVariable = 22; - public static readonly RULE_fromCommand = 23; - public static readonly RULE_metadata = 24; - public static readonly RULE_evalCommand = 25; - public static readonly RULE_statsCommand = 26; - public static readonly RULE_sourceIdentifier = 27; - public static readonly RULE_enrichIdentifier = 28; - public static readonly RULE_functionExpressionArgument = 29; - public static readonly RULE_mathFunctionExpressionArgument = 30; - public static readonly RULE_qualifiedName = 31; - public static readonly RULE_qualifiedNames = 32; - public static readonly RULE_identifier = 33; - public static readonly RULE_mathFunctionIdentifier = 34; - public static readonly RULE_functionIdentifier = 35; - public static readonly RULE_constant = 36; - public static readonly RULE_numericValue = 37; - public static readonly RULE_limitCommand = 38; - public static readonly RULE_sortCommand = 39; - public static readonly RULE_orderExpression = 40; - public static readonly RULE_projectCommand = 41; - public static readonly RULE_keepCommand = 42; - public static readonly RULE_dropCommand = 43; - public static readonly RULE_renameVariable = 44; - public static readonly RULE_renameCommand = 45; - public static readonly RULE_renameClause = 46; - public static readonly RULE_dissectCommand = 47; - public static readonly RULE_grokCommand = 48; - public static readonly RULE_commandOptions = 49; - public static readonly RULE_commandOption = 50; - public static readonly RULE_booleanValue = 51; - public static readonly RULE_number = 52; - public static readonly RULE_decimalValue = 53; - public static readonly RULE_integerValue = 54; - public static readonly RULE_string = 55; - public static readonly RULE_comparisonOperator = 56; - public static readonly RULE_explainCommand = 57; - public static readonly RULE_subqueryExpression = 58; - public static readonly RULE_showCommand = 59; + public static readonly RULE_whereCommand = 4; + public static readonly RULE_booleanExpression = 5; + public static readonly RULE_regexBooleanExpression = 6; + public static readonly RULE_valueExpression = 7; + public static readonly RULE_operatorExpression = 8; + public static readonly RULE_primaryExpression = 9; + public static readonly RULE_functionExpression = 10; + public static readonly RULE_rowCommand = 11; + public static readonly RULE_fields = 12; + public static readonly RULE_field = 13; + public static readonly RULE_fromCommand = 14; + public static readonly RULE_metadata = 15; + public static readonly RULE_evalCommand = 16; + public static readonly RULE_statsCommand = 17; + public static readonly RULE_grouping = 18; + public static readonly RULE_sourceIdentifier = 19; + public static readonly RULE_qualifiedName = 20; + public static readonly RULE_identifier = 21; + public static readonly RULE_constant = 22; + public static readonly RULE_limitCommand = 23; + public static readonly RULE_sortCommand = 24; + public static readonly RULE_orderExpression = 25; + public static readonly RULE_keepCommand = 26; + public static readonly RULE_dropCommand = 27; + public static readonly RULE_renameCommand = 28; + public static readonly RULE_renameClause = 29; + public static readonly RULE_dissectCommand = 30; + public static readonly RULE_grokCommand = 31; + public static readonly RULE_mvExpandCommand = 32; + public static readonly RULE_commandOptions = 33; + public static readonly RULE_commandOption = 34; + public static readonly RULE_booleanValue = 35; + public static readonly RULE_numericValue = 36; + public static readonly RULE_decimalValue = 37; + public static readonly RULE_integerValue = 38; + public static readonly RULE_string = 39; + public static readonly RULE_comparisonOperator = 40; + public static readonly RULE_showCommand = 41; + public static readonly RULE_enrichCommand = 42; + public static readonly RULE_enrichWithClause = 43; // tslint:disable:no-trailing-whitespace public static readonly ruleNames: string[] = [ - "singleStatement", "query", "sourceCommand", "processingCommand", "enrichCommand", - "enrichWithClause", "mvExpandCommand", "whereCommand", "whereBooleanExpression", - "booleanExpression", "regexBooleanExpression", "valueExpression", "comparison", - "mathFn", "mathEvalFn", "dateExpression", "operatorExpression", "primaryExpression", - "rowCommand", "fields", "field", "enrichFieldIdentifier", "userVariable", - "fromCommand", "metadata", "evalCommand", "statsCommand", "sourceIdentifier", - "enrichIdentifier", "functionExpressionArgument", "mathFunctionExpressionArgument", - "qualifiedName", "qualifiedNames", "identifier", "mathFunctionIdentifier", - "functionIdentifier", "constant", "numericValue", "limitCommand", "sortCommand", - "orderExpression", "projectCommand", "keepCommand", "dropCommand", "renameVariable", - "renameCommand", "renameClause", "dissectCommand", "grokCommand", "commandOptions", - "commandOption", "booleanValue", "number", "decimalValue", "integerValue", - "string", "comparisonOperator", "explainCommand", "subqueryExpression", - "showCommand", + "singleStatement", "query", "sourceCommand", "processingCommand", "whereCommand", + "booleanExpression", "regexBooleanExpression", "valueExpression", "operatorExpression", + "primaryExpression", "functionExpression", "rowCommand", "fields", "field", + "fromCommand", "metadata", "evalCommand", "statsCommand", "grouping", + "sourceIdentifier", "qualifiedName", "identifier", "constant", "limitCommand", + "sortCommand", "orderExpression", "keepCommand", "dropCommand", "renameCommand", + "renameClause", "dissectCommand", "grokCommand", "mvExpandCommand", "commandOptions", + "commandOption", "booleanValue", "numericValue", "decimalValue", "integerValue", + "string", "comparisonOperator", "showCommand", "enrichCommand", "enrichWithClause", ]; private static readonly _LITERAL_NAMES: Array = [ @@ -191,27 +165,25 @@ export class esql_parser extends Parser { undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, - "'by'", undefined, "'and'", undefined, undefined, "'.'", "'('", undefined, - "']'", undefined, undefined, undefined, undefined, undefined, undefined, - undefined, "'or'", "')'", "'_'", "'info'", "'functions'", undefined, undefined, - "'+'", "'-'", "'*'", "'/'", "'%'", "'10'", undefined, "'nulls'", + undefined, undefined, undefined, "'.'", undefined, undefined, undefined, + "'('", undefined, undefined, undefined, undefined, undefined, undefined, + undefined, "'?'", undefined, "')'", undefined, undefined, undefined, "'_'", + "'=='", "'!='", "'<'", "'<='", "'>'", "'>='", "'+'", "'-'", "'*'", "'/'", + "'%'", undefined, "']'", ]; private static readonly _SYMBOLIC_NAMES: Array = [ - undefined, "DISSECT", "GROK", "EVAL", "EXPLAIN", "FROM", "ROW", "STATS", - "WHERE", "SORT", "MV_EXPAND", "LIMIT", "PROJECT", "DROP", "RENAME", "SHOW", - "ENRICH", "KEEP", "LINE_COMMENT", "MULTILINE_COMMENT", "WS", "EXPLAIN_WS", - "EXPLAIN_LINE_COMMENT", "EXPLAIN_MULTILINE_COMMENT", "PIPE", "STRING", - "INTEGER_LITERAL", "DECIMAL_LITERAL", "BY", "DATE_LITERAL", "AND", "ASSIGN", - "COMMA", "DOT", "LP", "OPENING_BRACKET", "CLOSING_BRACKET", "NOT", "LIKE", - "RLIKE", "IN", "IS", "AS", "NULL", "OR", "RP", "UNDERSCORE", "INFO", "FUNCTIONS", - "BOOLEAN_VALUE", "COMPARISON_OPERATOR", "PLUS", "MINUS", "ASTERISK", "SLASH", - "PERCENT", "TEN", "ORDERING", "NULLS_ORDERING", "NULLS_ORDERING_DIRECTION", - "MATH_FUNCTION", "UNARY_FUNCTION", "WHERE_FUNCTIONS", "UNQUOTED_IDENTIFIER", - "QUOTED_IDENTIFIER", "EXPR_LINE_COMMENT", "EXPR_MULTILINE_COMMENT", "EXPR_WS", - "METADATA", "SRC_UNQUOTED_IDENTIFIER", "SRC_QUOTED_IDENTIFIER", "SRC_LINE_COMMENT", - "SRC_MULTILINE_COMMENT", "SRC_WS", "ON", "WITH", "ENR_UNQUOTED_IDENTIFIER", - "ENR_QUOTED_IDENTIFIER", "ENR_LINE_COMMENT", "ENR_MULTILINE_COMMENT", - "ENR_WS", "EXPLAIN_PIPE", + undefined, "DISSECT", "DROP", "ENRICH", "EVAL", "FROM", "GROK", "KEEP", + "LIMIT", "MV_EXPAND", "PROJECT", "RENAME", "ROW", "SHOW", "SORT", "STATS", + "WHERE", "UNKNOWN_CMD", "LINE_COMMENT", "MULTILINE_COMMENT", "WS", "PIPE", + "STRING", "INTEGER_LITERAL", "DECIMAL_LITERAL", "BY", "AND", "ASC", "ASSIGN", + "COMMA", "DESC", "DOT", "FALSE", "FIRST", "LAST", "LP", "IN", "IS", "LIKE", + "NOT", "NULL", "NULLS", "OR", "PARAM", "RLIKE", "RP", "TRUE", "INFO", + "FUNCTIONS", "UNDERSCORE", "EQ", "NEQ", "LT", "LTE", "GT", "GTE", "PLUS", + "MINUS", "ASTERISK", "SLASH", "PERCENT", "OPENING_BRACKET", "CLOSING_BRACKET", + "UNQUOTED_IDENTIFIER", "QUOTED_IDENTIFIER", "EXPR_LINE_COMMENT", "EXPR_MULTILINE_COMMENT", + "EXPR_WS", "AS", "METADATA", "ON", "WITH", "SRC_UNQUOTED_IDENTIFIER", + "SRC_QUOTED_IDENTIFIER", "SRC_LINE_COMMENT", "SRC_MULTILINE_COMMENT", + "SRC_WS", ]; public static readonly VOCABULARY: Vocabulary = new VocabularyImpl(esql_parser._LITERAL_NAMES, esql_parser._SYMBOLIC_NAMES, []); @@ -242,9 +214,9 @@ export class esql_parser extends Parser { try { this.enterOuterAlt(_localctx, 1); { - this.state = 120; + this.state = 88; this.query(0); - this.state = 121; + this.state = 89; this.match(esql_parser.EOF); } } @@ -286,11 +258,11 @@ export class esql_parser extends Parser { this._ctx = _localctx; _prevctx = _localctx; - this.state = 124; + this.state = 92; this.sourceCommand(); } this._ctx._stop = this._input.tryLT(-1); - this.state = 131; + this.state = 99; this._errHandler.sync(this); _alt = this.interpreter.adaptivePredict(this._input, 0, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { @@ -303,18 +275,18 @@ export class esql_parser extends Parser { { _localctx = new CompositeQueryContext(new QueryContext(_parentctx, _parentState)); this.pushNewRecursionContext(_localctx, _startState, esql_parser.RULE_query); - this.state = 126; + this.state = 94; if (!(this.precpred(this._ctx, 1))) { throw new FailedPredicateException(this, "this.precpred(this._ctx, 1)"); } - this.state = 127; + this.state = 95; this.match(esql_parser.PIPE); - this.state = 128; + this.state = 96; this.processingCommand(); } } } - this.state = 133; + this.state = 101; this._errHandler.sync(this); _alt = this.interpreter.adaptivePredict(this._input, 0, this._ctx); } @@ -339,34 +311,27 @@ export class esql_parser extends Parser { let _localctx: SourceCommandContext = new SourceCommandContext(this._ctx, this.state); this.enterRule(_localctx, 4, esql_parser.RULE_sourceCommand); try { - this.state = 138; + this.state = 105; this._errHandler.sync(this); switch (this._input.LA(1)) { - case esql_parser.EXPLAIN: - this.enterOuterAlt(_localctx, 1); - { - this.state = 134; - this.explainCommand(); - } - break; case esql_parser.FROM: - this.enterOuterAlt(_localctx, 2); + this.enterOuterAlt(_localctx, 1); { - this.state = 135; + this.state = 102; this.fromCommand(); } break; case esql_parser.ROW: - this.enterOuterAlt(_localctx, 3); + this.enterOuterAlt(_localctx, 2); { - this.state = 136; + this.state = 103; this.rowCommand(); } break; case esql_parser.SHOW: - this.enterOuterAlt(_localctx, 4); + this.enterOuterAlt(_localctx, 3); { - this.state = 137; + this.state = 104; this.showCommand(); } break; @@ -393,100 +358,94 @@ export class esql_parser extends Parser { let _localctx: ProcessingCommandContext = new ProcessingCommandContext(this._ctx, this.state); this.enterRule(_localctx, 6, esql_parser.RULE_processingCommand); try { - this.state = 153; + this.state = 119; this._errHandler.sync(this); switch (this._input.LA(1)) { case esql_parser.EVAL: this.enterOuterAlt(_localctx, 1); { - this.state = 140; + this.state = 107; this.evalCommand(); } break; case esql_parser.LIMIT: this.enterOuterAlt(_localctx, 2); { - this.state = 141; + this.state = 108; this.limitCommand(); } break; + case esql_parser.KEEP: case esql_parser.PROJECT: this.enterOuterAlt(_localctx, 3); { - this.state = 142; - this.projectCommand(); + this.state = 109; + this.keepCommand(); } break; - case esql_parser.KEEP: + case esql_parser.SORT: this.enterOuterAlt(_localctx, 4); { - this.state = 143; - this.keepCommand(); + this.state = 110; + this.sortCommand(); } break; - case esql_parser.RENAME: + case esql_parser.STATS: this.enterOuterAlt(_localctx, 5); { - this.state = 144; - this.renameCommand(); + this.state = 111; + this.statsCommand(); } break; - case esql_parser.DROP: + case esql_parser.WHERE: this.enterOuterAlt(_localctx, 6); { - this.state = 145; - this.dropCommand(); + this.state = 112; + this.whereCommand(); } break; - case esql_parser.DISSECT: + case esql_parser.DROP: this.enterOuterAlt(_localctx, 7); { - this.state = 146; - this.dissectCommand(); + this.state = 113; + this.dropCommand(); } break; - case esql_parser.GROK: + case esql_parser.RENAME: this.enterOuterAlt(_localctx, 8); { - this.state = 147; - this.grokCommand(); + this.state = 114; + this.renameCommand(); } break; - case esql_parser.SORT: + case esql_parser.DISSECT: this.enterOuterAlt(_localctx, 9); { - this.state = 148; - this.sortCommand(); + this.state = 115; + this.dissectCommand(); } break; - case esql_parser.STATS: + case esql_parser.GROK: this.enterOuterAlt(_localctx, 10); { - this.state = 149; - this.statsCommand(); + this.state = 116; + this.grokCommand(); } break; - case esql_parser.WHERE: + case esql_parser.ENRICH: this.enterOuterAlt(_localctx, 11); { - this.state = 150; - this.whereCommand(); + this.state = 117; + this.enrichCommand(); } break; case esql_parser.MV_EXPAND: this.enterOuterAlt(_localctx, 12); { - this.state = 151; + this.state = 118; this.mvExpandCommand(); } break; - case esql_parser.ENRICH: - this.enterOuterAlt(_localctx, 13); - { - this.state = 152; - this.enrichCommand(); - } - break; default: throw new NoViableAltException(this); } @@ -506,150 +465,16 @@ export class esql_parser extends Parser { return _localctx; } // @RuleVersion(0) - public enrichCommand(): EnrichCommandContext { - let _localctx: EnrichCommandContext = new EnrichCommandContext(this._ctx, this.state); - this.enterRule(_localctx, 8, esql_parser.RULE_enrichCommand); - try { - let _alt: number; - this.enterOuterAlt(_localctx, 1); - { - this.state = 155; - this.match(esql_parser.ENRICH); - this.state = 156; - _localctx._policyName = this.enrichIdentifier(); - this.state = 159; - this._errHandler.sync(this); - switch ( this.interpreter.adaptivePredict(this._input, 3, this._ctx) ) { - case 1: - { - this.state = 157; - this.match(esql_parser.ON); - this.state = 158; - _localctx._matchField = this.enrichFieldIdentifier(); - } - break; - } - this.state = 170; - this._errHandler.sync(this); - switch ( this.interpreter.adaptivePredict(this._input, 5, this._ctx) ) { - case 1: - { - this.state = 161; - this.match(esql_parser.WITH); - this.state = 162; - this.enrichWithClause(); - this.state = 167; - this._errHandler.sync(this); - _alt = this.interpreter.adaptivePredict(this._input, 4, this._ctx); - while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { - if (_alt === 1) { - { - { - this.state = 163; - this.match(esql_parser.COMMA); - this.state = 164; - this.enrichWithClause(); - } - } - } - this.state = 169; - this._errHandler.sync(this); - _alt = this.interpreter.adaptivePredict(this._input, 4, this._ctx); - } - } - break; - } - } - } - catch (re) { - if (re instanceof RecognitionException) { - _localctx.exception = re; - this._errHandler.reportError(this, re); - this._errHandler.recover(this, re); - } else { - throw re; - } - } - finally { - this.exitRule(); - } - return _localctx; - } - // @RuleVersion(0) - public enrichWithClause(): EnrichWithClauseContext { - let _localctx: EnrichWithClauseContext = new EnrichWithClauseContext(this._ctx, this.state); - this.enterRule(_localctx, 10, esql_parser.RULE_enrichWithClause); - try { - this.enterOuterAlt(_localctx, 1); - { - this.state = 175; - this._errHandler.sync(this); - switch ( this.interpreter.adaptivePredict(this._input, 6, this._ctx) ) { - case 1: - { - this.state = 172; - _localctx._newName = this.enrichFieldIdentifier(); - this.state = 173; - this.match(esql_parser.ASSIGN); - } - break; - } - this.state = 177; - _localctx._enrichField = this.enrichFieldIdentifier(); - } - } - catch (re) { - if (re instanceof RecognitionException) { - _localctx.exception = re; - this._errHandler.reportError(this, re); - this._errHandler.recover(this, re); - } else { - throw re; - } - } - finally { - this.exitRule(); - } - return _localctx; - } - // @RuleVersion(0) - public mvExpandCommand(): MvExpandCommandContext { - let _localctx: MvExpandCommandContext = new MvExpandCommandContext(this._ctx, this.state); - this.enterRule(_localctx, 12, esql_parser.RULE_mvExpandCommand); - try { - this.enterOuterAlt(_localctx, 1); - { - this.state = 179; - this.match(esql_parser.MV_EXPAND); - this.state = 180; - this.qualifiedNames(); - } - } - catch (re) { - if (re instanceof RecognitionException) { - _localctx.exception = re; - this._errHandler.reportError(this, re); - this._errHandler.recover(this, re); - } else { - throw re; - } - } - finally { - this.exitRule(); - } - return _localctx; - } - // @RuleVersion(0) public whereCommand(): WhereCommandContext { let _localctx: WhereCommandContext = new WhereCommandContext(this._ctx, this.state); - this.enterRule(_localctx, 14, esql_parser.RULE_whereCommand); + this.enterRule(_localctx, 8, esql_parser.RULE_whereCommand); try { this.enterOuterAlt(_localctx, 1); { - this.state = 182; + this.state = 121; this.match(esql_parser.WHERE); - this.state = 183; - this.whereBooleanExpression(0); + this.state = 122; + this.booleanExpression(0); } } catch (re) { @@ -667,164 +492,133 @@ export class esql_parser extends Parser { return _localctx; } - public whereBooleanExpression(): WhereBooleanExpressionContext; - public whereBooleanExpression(_p: number): WhereBooleanExpressionContext; + public booleanExpression(): BooleanExpressionContext; + public booleanExpression(_p: number): BooleanExpressionContext; // @RuleVersion(0) - public whereBooleanExpression(_p?: number): WhereBooleanExpressionContext { + public booleanExpression(_p?: number): BooleanExpressionContext { if (_p === undefined) { _p = 0; } let _parentctx: ParserRuleContext = this._ctx; let _parentState: number = this.state; - let _localctx: WhereBooleanExpressionContext = new WhereBooleanExpressionContext(this._ctx, _parentState); - let _prevctx: WhereBooleanExpressionContext = _localctx; - let _startState: number = 16; - this.enterRecursionRule(_localctx, 16, esql_parser.RULE_whereBooleanExpression, _p); + let _localctx: BooleanExpressionContext = new BooleanExpressionContext(this._ctx, _parentState); + let _prevctx: BooleanExpressionContext = _localctx; + let _startState: number = 10; + this.enterRecursionRule(_localctx, 10, esql_parser.RULE_booleanExpression, _p); let _la: number; try { let _alt: number; this.enterOuterAlt(_localctx, 1); { - this.state = 230; + this.state = 152; this._errHandler.sync(this); - switch ( this.interpreter.adaptivePredict(this._input, 13, this._ctx) ) { + switch ( this.interpreter.adaptivePredict(this._input, 6, this._ctx) ) { case 1: { - this.state = 186; + _localctx = new LogicalNotContext(_localctx); + this._ctx = _localctx; + _prevctx = _localctx; + + this.state = 125; this.match(esql_parser.NOT); - this.state = 187; - this.whereBooleanExpression(8); + this.state = 126; + this.booleanExpression(7); } break; case 2: { - this.state = 188; + _localctx = new BooleanDefaultContext(_localctx); + this._ctx = _localctx; + _prevctx = _localctx; + this.state = 127; this.valueExpression(); } break; case 3: { - this.state = 189; + _localctx = new RegexExpressionContext(_localctx); + this._ctx = _localctx; + _prevctx = _localctx; + this.state = 128; this.regexBooleanExpression(); } break; case 4: { - this.state = 190; + _localctx = new LogicalInContext(_localctx); + this._ctx = _localctx; + _prevctx = _localctx; + this.state = 129; this.valueExpression(); - this.state = 192; + this.state = 131; this._errHandler.sync(this); _la = this._input.LA(1); if (_la === esql_parser.NOT) { { - this.state = 191; + this.state = 130; this.match(esql_parser.NOT); } } - this.state = 194; + this.state = 133; this.match(esql_parser.IN); - this.state = 195; + this.state = 134; this.match(esql_parser.LP); - this.state = 196; + this.state = 135; this.valueExpression(); - this.state = 201; + this.state = 140; this._errHandler.sync(this); _la = this._input.LA(1); while (_la === esql_parser.COMMA) { { { - this.state = 197; + this.state = 136; this.match(esql_parser.COMMA); - this.state = 198; + this.state = 137; this.valueExpression(); } } - this.state = 203; + this.state = 142; this._errHandler.sync(this); _la = this._input.LA(1); } - this.state = 204; + this.state = 143; this.match(esql_parser.RP); } break; case 5: { - this.state = 207; - this._errHandler.sync(this); - _la = this._input.LA(1); - if (_la === esql_parser.NOT) { - { - this.state = 206; - this.match(esql_parser.NOT); - } - } - - this.state = 209; - this.match(esql_parser.WHERE_FUNCTIONS); - this.state = 210; - this.match(esql_parser.LP); - this.state = 211; - this.qualifiedName(); - this.state = 219; - this._errHandler.sync(this); - switch ( this.interpreter.adaptivePredict(this._input, 11, this._ctx) ) { - case 1: - { - this.state = 216; - this._errHandler.sync(this); - _la = this._input.LA(1); - while (_la === esql_parser.COMMA) { - { - { - this.state = 212; - this.match(esql_parser.COMMA); - this.state = 213; - this.functionExpressionArgument(); - } - } - this.state = 218; - this._errHandler.sync(this); - _la = this._input.LA(1); - } - } - break; - } - this.state = 221; - this.match(esql_parser.RP); - } - break; - - case 6: - { - this.state = 223; + _localctx = new IsNullContext(_localctx); + this._ctx = _localctx; + _prevctx = _localctx; + this.state = 145; this.valueExpression(); - this.state = 224; + this.state = 146; this.match(esql_parser.IS); - this.state = 226; + this.state = 148; this._errHandler.sync(this); _la = this._input.LA(1); if (_la === esql_parser.NOT) { { - this.state = 225; + this.state = 147; this.match(esql_parser.NOT); } } - this.state = 228; + this.state = 150; this.match(esql_parser.NULL); } break; } this._ctx._stop = this._input.tryLT(-1); - this.state = 240; + this.state = 162; this._errHandler.sync(this); - _alt = this.interpreter.adaptivePredict(this._input, 15, this._ctx); + _alt = this.interpreter.adaptivePredict(this._input, 8, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { if (this._parseListeners != null) { @@ -832,46 +626,46 @@ export class esql_parser extends Parser { } _prevctx = _localctx; { - this.state = 238; + this.state = 160; this._errHandler.sync(this); - switch ( this.interpreter.adaptivePredict(this._input, 14, this._ctx) ) { + switch ( this.interpreter.adaptivePredict(this._input, 7, this._ctx) ) { case 1: { - _localctx = new WhereBooleanExpressionContext(_parentctx, _parentState); - _localctx._left = _prevctx; - this.pushNewRecursionContext(_localctx, _startState, esql_parser.RULE_whereBooleanExpression); - this.state = 232; - if (!(this.precpred(this._ctx, 5))) { - throw new FailedPredicateException(this, "this.precpred(this._ctx, 5)"); + _localctx = new LogicalBinaryContext(new BooleanExpressionContext(_parentctx, _parentState)); + (_localctx as LogicalBinaryContext)._left = _prevctx; + this.pushNewRecursionContext(_localctx, _startState, esql_parser.RULE_booleanExpression); + this.state = 154; + if (!(this.precpred(this._ctx, 4))) { + throw new FailedPredicateException(this, "this.precpred(this._ctx, 4)"); } - this.state = 233; - _localctx._operator = this.match(esql_parser.AND); - this.state = 234; - _localctx._right = this.whereBooleanExpression(6); + this.state = 155; + (_localctx as LogicalBinaryContext)._operator = this.match(esql_parser.AND); + this.state = 156; + (_localctx as LogicalBinaryContext)._right = this.booleanExpression(5); } break; case 2: { - _localctx = new WhereBooleanExpressionContext(_parentctx, _parentState); - _localctx._left = _prevctx; - this.pushNewRecursionContext(_localctx, _startState, esql_parser.RULE_whereBooleanExpression); - this.state = 235; - if (!(this.precpred(this._ctx, 4))) { - throw new FailedPredicateException(this, "this.precpred(this._ctx, 4)"); + _localctx = new LogicalBinaryContext(new BooleanExpressionContext(_parentctx, _parentState)); + (_localctx as LogicalBinaryContext)._left = _prevctx; + this.pushNewRecursionContext(_localctx, _startState, esql_parser.RULE_booleanExpression); + this.state = 157; + if (!(this.precpred(this._ctx, 3))) { + throw new FailedPredicateException(this, "this.precpred(this._ctx, 3)"); } - this.state = 236; - _localctx._operator = this.match(esql_parser.OR); - this.state = 237; - _localctx._right = this.whereBooleanExpression(5); + this.state = 158; + (_localctx as LogicalBinaryContext)._operator = this.match(esql_parser.OR); + this.state = 159; + (_localctx as LogicalBinaryContext)._right = this.booleanExpression(4); } break; } } } - this.state = 242; + this.state = 164; this._errHandler.sync(this); - _alt = this.interpreter.adaptivePredict(this._input, 15, this._ctx); + _alt = this.interpreter.adaptivePredict(this._input, 8, this._ctx); } } } @@ -889,153 +683,33 @@ export class esql_parser extends Parser { } return _localctx; } - - public booleanExpression(): BooleanExpressionContext; - public booleanExpression(_p: number): BooleanExpressionContext; // @RuleVersion(0) - public booleanExpression(_p?: number): BooleanExpressionContext { - if (_p === undefined) { - _p = 0; - } - - let _parentctx: ParserRuleContext = this._ctx; - let _parentState: number = this.state; - let _localctx: BooleanExpressionContext = new BooleanExpressionContext(this._ctx, _parentState); - let _prevctx: BooleanExpressionContext = _localctx; - let _startState: number = 18; - this.enterRecursionRule(_localctx, 18, esql_parser.RULE_booleanExpression, _p); + public regexBooleanExpression(): RegexBooleanExpressionContext { + let _localctx: RegexBooleanExpressionContext = new RegexBooleanExpressionContext(this._ctx, this.state); + this.enterRule(_localctx, 12, esql_parser.RULE_regexBooleanExpression); + let _la: number; try { - let _alt: number; - this.enterOuterAlt(_localctx, 1); - { - this.state = 247; + this.state = 179; this._errHandler.sync(this); - switch (this._input.LA(1)) { - case esql_parser.NOT: + switch ( this.interpreter.adaptivePredict(this._input, 11, this._ctx) ) { + case 1: + this.enterOuterAlt(_localctx, 1); { - this.state = 244; - this.match(esql_parser.NOT); - this.state = 245; - this.booleanExpression(4); - } - break; - case esql_parser.STRING: - case esql_parser.INTEGER_LITERAL: - case esql_parser.DECIMAL_LITERAL: - case esql_parser.LP: - case esql_parser.OPENING_BRACKET: - case esql_parser.NULL: - case esql_parser.BOOLEAN_VALUE: - case esql_parser.PLUS: - case esql_parser.MINUS: - case esql_parser.ASTERISK: - case esql_parser.MATH_FUNCTION: - case esql_parser.UNARY_FUNCTION: - case esql_parser.UNQUOTED_IDENTIFIER: - case esql_parser.QUOTED_IDENTIFIER: - { - this.state = 246; - this.valueExpression(); - } - break; - default: - throw new NoViableAltException(this); - } - this._ctx._stop = this._input.tryLT(-1); - this.state = 257; - this._errHandler.sync(this); - _alt = this.interpreter.adaptivePredict(this._input, 18, this._ctx); - while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { - if (_alt === 1) { - if (this._parseListeners != null) { - this.triggerExitRuleEvent(); - } - _prevctx = _localctx; - { - this.state = 255; - this._errHandler.sync(this); - switch ( this.interpreter.adaptivePredict(this._input, 17, this._ctx) ) { - case 1: - { - _localctx = new BooleanExpressionContext(_parentctx, _parentState); - _localctx._left = _prevctx; - this.pushNewRecursionContext(_localctx, _startState, esql_parser.RULE_booleanExpression); - this.state = 249; - if (!(this.precpred(this._ctx, 2))) { - throw new FailedPredicateException(this, "this.precpred(this._ctx, 2)"); - } - this.state = 250; - _localctx._operator = this.match(esql_parser.AND); - this.state = 251; - _localctx._right = this.booleanExpression(3); - } - break; - - case 2: - { - _localctx = new BooleanExpressionContext(_parentctx, _parentState); - _localctx._left = _prevctx; - this.pushNewRecursionContext(_localctx, _startState, esql_parser.RULE_booleanExpression); - this.state = 252; - if (!(this.precpred(this._ctx, 1))) { - throw new FailedPredicateException(this, "this.precpred(this._ctx, 1)"); - } - this.state = 253; - _localctx._operator = this.match(esql_parser.OR); - this.state = 254; - _localctx._right = this.booleanExpression(2); - } - break; - } - } - } - this.state = 259; - this._errHandler.sync(this); - _alt = this.interpreter.adaptivePredict(this._input, 18, this._ctx); - } - } - } - catch (re) { - if (re instanceof RecognitionException) { - _localctx.exception = re; - this._errHandler.reportError(this, re); - this._errHandler.recover(this, re); - } else { - throw re; - } - } - finally { - this.unrollRecursionContexts(_parentctx); - } - return _localctx; - } - // @RuleVersion(0) - public regexBooleanExpression(): RegexBooleanExpressionContext { - let _localctx: RegexBooleanExpressionContext = new RegexBooleanExpressionContext(this._ctx, this.state); - this.enterRule(_localctx, 20, esql_parser.RULE_regexBooleanExpression); - let _la: number; - try { - this.state = 274; - this._errHandler.sync(this); - switch ( this.interpreter.adaptivePredict(this._input, 21, this._ctx) ) { - case 1: - this.enterOuterAlt(_localctx, 1); - { - this.state = 260; + this.state = 165; this.valueExpression(); - this.state = 262; + this.state = 167; this._errHandler.sync(this); _la = this._input.LA(1); if (_la === esql_parser.NOT) { { - this.state = 261; + this.state = 166; this.match(esql_parser.NOT); } } - this.state = 264; + this.state = 169; _localctx._kind = this.match(esql_parser.LIKE); - this.state = 265; + this.state = 170; _localctx._pattern = this.string(); } break; @@ -1043,21 +717,21 @@ export class esql_parser extends Parser { case 2: this.enterOuterAlt(_localctx, 2); { - this.state = 267; + this.state = 172; this.valueExpression(); - this.state = 269; + this.state = 174; this._errHandler.sync(this); _la = this._input.LA(1); if (_la === esql_parser.NOT) { { - this.state = 268; + this.state = 173; this.match(esql_parser.NOT); } } - this.state = 271; + this.state = 176; _localctx._kind = this.match(esql_parser.RLIKE); - this.state = 272; + this.state = 177; _localctx._pattern = this.string(); } break; @@ -1080,24 +754,30 @@ export class esql_parser extends Parser { // @RuleVersion(0) public valueExpression(): ValueExpressionContext { let _localctx: ValueExpressionContext = new ValueExpressionContext(this._ctx, this.state); - this.enterRule(_localctx, 22, esql_parser.RULE_valueExpression); + this.enterRule(_localctx, 14, esql_parser.RULE_valueExpression); try { - this.state = 278; + this.state = 186; this._errHandler.sync(this); - switch ( this.interpreter.adaptivePredict(this._input, 22, this._ctx) ) { + switch ( this.interpreter.adaptivePredict(this._input, 12, this._ctx) ) { case 1: + _localctx = new ValueExpressionDefaultContext(_localctx); this.enterOuterAlt(_localctx, 1); { - this.state = 276; + this.state = 181; this.operatorExpression(0); } break; case 2: + _localctx = new ComparisonContext(_localctx); this.enterOuterAlt(_localctx, 2); { - this.state = 277; - this.comparison(); + this.state = 182; + (_localctx as ComparisonContext)._left = this.operatorExpression(0); + this.state = 183; + this.comparisonOperator(); + this.state = 184; + (_localctx as ComparisonContext)._right = this.operatorExpression(0); } break; } @@ -1116,174 +796,6 @@ export class esql_parser extends Parser { } return _localctx; } - // @RuleVersion(0) - public comparison(): ComparisonContext { - let _localctx: ComparisonContext = new ComparisonContext(this._ctx, this.state); - this.enterRule(_localctx, 24, esql_parser.RULE_comparison); - try { - this.enterOuterAlt(_localctx, 1); - { - this.state = 280; - _localctx._left = this.operatorExpression(0); - this.state = 281; - this.comparisonOperator(); - this.state = 282; - _localctx._right = this.operatorExpression(0); - } - } - catch (re) { - if (re instanceof RecognitionException) { - _localctx.exception = re; - this._errHandler.reportError(this, re); - this._errHandler.recover(this, re); - } else { - throw re; - } - } - finally { - this.exitRule(); - } - return _localctx; - } - // @RuleVersion(0) - public mathFn(): MathFnContext { - let _localctx: MathFnContext = new MathFnContext(this._ctx, this.state); - this.enterRule(_localctx, 26, esql_parser.RULE_mathFn); - let _la: number; - try { - this.enterOuterAlt(_localctx, 1); - { - this.state = 284; - this.functionIdentifier(); - this.state = 285; - this.match(esql_parser.LP); - this.state = 294; - this._errHandler.sync(this); - _la = this._input.LA(1); - if ((((_la) & ~0x1F) === 0 && ((1 << _la) & ((1 << esql_parser.STRING) | (1 << esql_parser.INTEGER_LITERAL) | (1 << esql_parser.DECIMAL_LITERAL))) !== 0) || ((((_la - 53)) & ~0x1F) === 0 && ((1 << (_la - 53)) & ((1 << (esql_parser.ASTERISK - 53)) | (1 << (esql_parser.UNQUOTED_IDENTIFIER - 53)) | (1 << (esql_parser.QUOTED_IDENTIFIER - 53)))) !== 0)) { - { - this.state = 286; - this.functionExpressionArgument(); - this.state = 291; - this._errHandler.sync(this); - _la = this._input.LA(1); - while (_la === esql_parser.COMMA) { - { - { - this.state = 287; - this.match(esql_parser.COMMA); - this.state = 288; - this.functionExpressionArgument(); - } - } - this.state = 293; - this._errHandler.sync(this); - _la = this._input.LA(1); - } - } - } - - this.state = 296; - this.match(esql_parser.RP); - } - } - catch (re) { - if (re instanceof RecognitionException) { - _localctx.exception = re; - this._errHandler.reportError(this, re); - this._errHandler.recover(this, re); - } else { - throw re; - } - } - finally { - this.exitRule(); - } - return _localctx; - } - // @RuleVersion(0) - public mathEvalFn(): MathEvalFnContext { - let _localctx: MathEvalFnContext = new MathEvalFnContext(this._ctx, this.state); - this.enterRule(_localctx, 28, esql_parser.RULE_mathEvalFn); - let _la: number; - try { - this.enterOuterAlt(_localctx, 1); - { - this.state = 298; - this.mathFunctionIdentifier(); - this.state = 299; - this.match(esql_parser.LP); - this.state = 308; - this._errHandler.sync(this); - _la = this._input.LA(1); - if ((((_la) & ~0x1F) === 0 && ((1 << _la) & ((1 << esql_parser.STRING) | (1 << esql_parser.INTEGER_LITERAL) | (1 << esql_parser.DECIMAL_LITERAL))) !== 0) || ((((_la - 34)) & ~0x1F) === 0 && ((1 << (_la - 34)) & ((1 << (esql_parser.LP - 34)) | (1 << (esql_parser.OPENING_BRACKET - 34)) | (1 << (esql_parser.NULL - 34)) | (1 << (esql_parser.BOOLEAN_VALUE - 34)) | (1 << (esql_parser.PLUS - 34)) | (1 << (esql_parser.MINUS - 34)) | (1 << (esql_parser.ASTERISK - 34)) | (1 << (esql_parser.MATH_FUNCTION - 34)) | (1 << (esql_parser.UNARY_FUNCTION - 34)) | (1 << (esql_parser.UNQUOTED_IDENTIFIER - 34)) | (1 << (esql_parser.QUOTED_IDENTIFIER - 34)))) !== 0)) { - { - this.state = 300; - this.mathFunctionExpressionArgument(); - this.state = 305; - this._errHandler.sync(this); - _la = this._input.LA(1); - while (_la === esql_parser.COMMA) { - { - { - this.state = 301; - this.match(esql_parser.COMMA); - this.state = 302; - this.mathFunctionExpressionArgument(); - } - } - this.state = 307; - this._errHandler.sync(this); - _la = this._input.LA(1); - } - } - } - - this.state = 310; - this.match(esql_parser.RP); - } - } - catch (re) { - if (re instanceof RecognitionException) { - _localctx.exception = re; - this._errHandler.reportError(this, re); - this._errHandler.recover(this, re); - } else { - throw re; - } - } - finally { - this.exitRule(); - } - return _localctx; - } - // @RuleVersion(0) - public dateExpression(): DateExpressionContext { - let _localctx: DateExpressionContext = new DateExpressionContext(this._ctx, this.state); - this.enterRule(_localctx, 30, esql_parser.RULE_dateExpression); - try { - this.enterOuterAlt(_localctx, 1); - { - this.state = 312; - _localctx._quantifier = this.number(); - this.state = 313; - this.match(esql_parser.DATE_LITERAL); - } - } - catch (re) { - if (re instanceof RecognitionException) { - _localctx.exception = re; - this._errHandler.reportError(this, re); - this._errHandler.recover(this, re); - } else { - throw re; - } - } - finally { - this.exitRule(); - } - return _localctx; - } public operatorExpression(): OperatorExpressionContext; public operatorExpression(_p: number): OperatorExpressionContext; @@ -1297,51 +809,37 @@ export class esql_parser extends Parser { let _parentState: number = this.state; let _localctx: OperatorExpressionContext = new OperatorExpressionContext(this._ctx, _parentState); let _prevctx: OperatorExpressionContext = _localctx; - let _startState: number = 32; - this.enterRecursionRule(_localctx, 32, esql_parser.RULE_operatorExpression, _p); + let _startState: number = 16; + this.enterRecursionRule(_localctx, 16, esql_parser.RULE_operatorExpression, _p); let _la: number; try { let _alt: number; this.enterOuterAlt(_localctx, 1); { - this.state = 321; + this.state = 192; this._errHandler.sync(this); - switch (this._input.LA(1)) { - case esql_parser.STRING: - case esql_parser.INTEGER_LITERAL: - case esql_parser.DECIMAL_LITERAL: - case esql_parser.LP: - case esql_parser.OPENING_BRACKET: - case esql_parser.NULL: - case esql_parser.BOOLEAN_VALUE: - case esql_parser.ASTERISK: - case esql_parser.UNQUOTED_IDENTIFIER: - case esql_parser.QUOTED_IDENTIFIER: + switch ( this.interpreter.adaptivePredict(this._input, 13, this._ctx) ) { + case 1: { - this.state = 316; + _localctx = new OperatorExpressionDefaultContext(_localctx); + this._ctx = _localctx; + _prevctx = _localctx; + + this.state = 189; this.primaryExpression(); } break; - case esql_parser.UNARY_FUNCTION: - { - this.state = 317; - this.mathFn(); - } - break; - case esql_parser.MATH_FUNCTION: - { - this.state = 318; - this.mathEvalFn(); - } - break; - case esql_parser.PLUS: - case esql_parser.MINUS: + + case 2: { - this.state = 319; - _localctx._operator = this._input.LT(1); + _localctx = new ArithmeticUnaryContext(_localctx); + this._ctx = _localctx; + _prevctx = _localctx; + this.state = 190; + (_localctx as ArithmeticUnaryContext)._operator = this._input.LT(1); _la = this._input.LA(1); if (!(_la === esql_parser.PLUS || _la === esql_parser.MINUS)) { - _localctx._operator = this._errHandler.recoverInline(this); + (_localctx as ArithmeticUnaryContext)._operator = this._errHandler.recoverInline(this); } else { if (this._input.LA(1) === Token.EOF) { this.matchedEOF = true; @@ -1350,17 +848,15 @@ export class esql_parser extends Parser { this._errHandler.reportMatch(this); this.consume(); } - this.state = 320; + this.state = 191; this.operatorExpression(3); } break; - default: - throw new NoViableAltException(this); } this._ctx._stop = this._input.tryLT(-1); - this.state = 331; + this.state = 202; this._errHandler.sync(this); - _alt = this.interpreter.adaptivePredict(this._input, 29, this._ctx); + _alt = this.interpreter.adaptivePredict(this._input, 15, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { if (this._parseListeners != null) { @@ -1368,23 +864,23 @@ export class esql_parser extends Parser { } _prevctx = _localctx; { - this.state = 329; + this.state = 200; this._errHandler.sync(this); - switch ( this.interpreter.adaptivePredict(this._input, 28, this._ctx) ) { + switch ( this.interpreter.adaptivePredict(this._input, 14, this._ctx) ) { case 1: { - _localctx = new OperatorExpressionContext(_parentctx, _parentState); - _localctx._left = _prevctx; + _localctx = new ArithmeticBinaryContext(new OperatorExpressionContext(_parentctx, _parentState)); + (_localctx as ArithmeticBinaryContext)._left = _prevctx; this.pushNewRecursionContext(_localctx, _startState, esql_parser.RULE_operatorExpression); - this.state = 323; + this.state = 194; if (!(this.precpred(this._ctx, 2))) { throw new FailedPredicateException(this, "this.precpred(this._ctx, 2)"); } - this.state = 324; - _localctx._operator = this._input.LT(1); + this.state = 195; + (_localctx as ArithmeticBinaryContext)._operator = this._input.LT(1); _la = this._input.LA(1); - if (!(((((_la - 53)) & ~0x1F) === 0 && ((1 << (_la - 53)) & ((1 << (esql_parser.ASTERISK - 53)) | (1 << (esql_parser.SLASH - 53)) | (1 << (esql_parser.PERCENT - 53)))) !== 0))) { - _localctx._operator = this._errHandler.recoverInline(this); + if (!(((((_la - 58)) & ~0x1F) === 0 && ((1 << (_la - 58)) & ((1 << (esql_parser.ASTERISK - 58)) | (1 << (esql_parser.SLASH - 58)) | (1 << (esql_parser.PERCENT - 58)))) !== 0))) { + (_localctx as ArithmeticBinaryContext)._operator = this._errHandler.recoverInline(this); } else { if (this._input.LA(1) === Token.EOF) { this.matchedEOF = true; @@ -1393,25 +889,25 @@ export class esql_parser extends Parser { this._errHandler.reportMatch(this); this.consume(); } - this.state = 325; - _localctx._right = this.operatorExpression(3); + this.state = 196; + (_localctx as ArithmeticBinaryContext)._right = this.operatorExpression(3); } break; case 2: { - _localctx = new OperatorExpressionContext(_parentctx, _parentState); - _localctx._left = _prevctx; + _localctx = new ArithmeticBinaryContext(new OperatorExpressionContext(_parentctx, _parentState)); + (_localctx as ArithmeticBinaryContext)._left = _prevctx; this.pushNewRecursionContext(_localctx, _startState, esql_parser.RULE_operatorExpression); - this.state = 326; + this.state = 197; if (!(this.precpred(this._ctx, 1))) { throw new FailedPredicateException(this, "this.precpred(this._ctx, 1)"); } - this.state = 327; - _localctx._operator = this._input.LT(1); + this.state = 198; + (_localctx as ArithmeticBinaryContext)._operator = this._input.LT(1); _la = this._input.LA(1); if (!(_la === esql_parser.PLUS || _la === esql_parser.MINUS)) { - _localctx._operator = this._errHandler.recoverInline(this); + (_localctx as ArithmeticBinaryContext)._operator = this._errHandler.recoverInline(this); } else { if (this._input.LA(1) === Token.EOF) { this.matchedEOF = true; @@ -1420,16 +916,16 @@ export class esql_parser extends Parser { this._errHandler.reportMatch(this); this.consume(); } - this.state = 328; - _localctx._right = this.operatorExpression(2); + this.state = 199; + (_localctx as ArithmeticBinaryContext)._right = this.operatorExpression(2); } break; } } } - this.state = 333; + this.state = 204; this._errHandler.sync(this); - _alt = this.interpreter.adaptivePredict(this._input, 29, this._ctx); + _alt = this.interpreter.adaptivePredict(this._input, 15, this._ctx); } } } @@ -1450,85 +946,131 @@ export class esql_parser extends Parser { // @RuleVersion(0) public primaryExpression(): PrimaryExpressionContext { let _localctx: PrimaryExpressionContext = new PrimaryExpressionContext(this._ctx, this.state); - this.enterRule(_localctx, 34, esql_parser.RULE_primaryExpression); - let _la: number; + this.enterRule(_localctx, 18, esql_parser.RULE_primaryExpression); try { - this.state = 355; + this.state = 212; this._errHandler.sync(this); - switch ( this.interpreter.adaptivePredict(this._input, 32, this._ctx) ) { + switch ( this.interpreter.adaptivePredict(this._input, 16, this._ctx) ) { case 1: + _localctx = new ConstantDefaultContext(_localctx); this.enterOuterAlt(_localctx, 1); { - this.state = 334; + this.state = 205; this.constant(); } break; case 2: + _localctx = new DereferenceContext(_localctx); this.enterOuterAlt(_localctx, 2); { - this.state = 335; + this.state = 206; this.qualifiedName(); } break; case 3: + _localctx = new FunctionContext(_localctx); this.enterOuterAlt(_localctx, 3); { - this.state = 336; - this.dateExpression(); + this.state = 207; + this.functionExpression(); } break; case 4: + _localctx = new ParenthesizedExpressionContext(_localctx); this.enterOuterAlt(_localctx, 4); { - this.state = 337; + this.state = 208; this.match(esql_parser.LP); - this.state = 338; + this.state = 209; this.booleanExpression(0); - this.state = 339; + this.state = 210; this.match(esql_parser.RP); } break; - - case 5: - this.enterOuterAlt(_localctx, 5); + } + } + catch (re) { + if (re instanceof RecognitionException) { + _localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return _localctx; + } + // @RuleVersion(0) + public functionExpression(): FunctionExpressionContext { + let _localctx: FunctionExpressionContext = new FunctionExpressionContext(this._ctx, this.state); + this.enterRule(_localctx, 20, esql_parser.RULE_functionExpression); + let _la: number; + try { + this.enterOuterAlt(_localctx, 1); + { + this.state = 214; + this.identifier(); + this.state = 215; + this.match(esql_parser.LP); + this.state = 225; + this._errHandler.sync(this); + switch (this._input.LA(1)) { + case esql_parser.ASTERISK: { - this.state = 341; - this.identifier(); - this.state = 342; - this.match(esql_parser.LP); - this.state = 351; + this.state = 216; + this.match(esql_parser.ASTERISK); + } + break; + case esql_parser.STRING: + case esql_parser.INTEGER_LITERAL: + case esql_parser.DECIMAL_LITERAL: + case esql_parser.FALSE: + case esql_parser.LP: + case esql_parser.NOT: + case esql_parser.NULL: + case esql_parser.PARAM: + case esql_parser.TRUE: + case esql_parser.PLUS: + case esql_parser.MINUS: + case esql_parser.OPENING_BRACKET: + case esql_parser.UNQUOTED_IDENTIFIER: + case esql_parser.QUOTED_IDENTIFIER: + { + { + this.state = 217; + this.booleanExpression(0); + this.state = 222; this._errHandler.sync(this); _la = this._input.LA(1); - if ((((_la) & ~0x1F) === 0 && ((1 << _la) & ((1 << esql_parser.STRING) | (1 << esql_parser.INTEGER_LITERAL) | (1 << esql_parser.DECIMAL_LITERAL))) !== 0) || ((((_la - 34)) & ~0x1F) === 0 && ((1 << (_la - 34)) & ((1 << (esql_parser.LP - 34)) | (1 << (esql_parser.OPENING_BRACKET - 34)) | (1 << (esql_parser.NOT - 34)) | (1 << (esql_parser.NULL - 34)) | (1 << (esql_parser.BOOLEAN_VALUE - 34)) | (1 << (esql_parser.PLUS - 34)) | (1 << (esql_parser.MINUS - 34)) | (1 << (esql_parser.ASTERISK - 34)) | (1 << (esql_parser.MATH_FUNCTION - 34)) | (1 << (esql_parser.UNARY_FUNCTION - 34)) | (1 << (esql_parser.UNQUOTED_IDENTIFIER - 34)) | (1 << (esql_parser.QUOTED_IDENTIFIER - 34)))) !== 0)) { + while (_la === esql_parser.COMMA) { { - this.state = 343; + { + this.state = 218; + this.match(esql_parser.COMMA); + this.state = 219; this.booleanExpression(0); - this.state = 348; - this._errHandler.sync(this); - _la = this._input.LA(1); - while (_la === esql_parser.COMMA) { - { - { - this.state = 344; - this.match(esql_parser.COMMA); - this.state = 345; - this.booleanExpression(0); - } - } - this.state = 350; - this._errHandler.sync(this); - _la = this._input.LA(1); } } + this.state = 224; + this._errHandler.sync(this); + _la = this._input.LA(1); + } } - - this.state = 353; - this.match(esql_parser.RP); } break; + case esql_parser.RP: + break; + default: + break; + } + this.state = 227; + this.match(esql_parser.RP); } } catch (re) { @@ -1548,13 +1090,13 @@ export class esql_parser extends Parser { // @RuleVersion(0) public rowCommand(): RowCommandContext { let _localctx: RowCommandContext = new RowCommandContext(this._ctx, this.state); - this.enterRule(_localctx, 36, esql_parser.RULE_rowCommand); + this.enterRule(_localctx, 22, esql_parser.RULE_rowCommand); try { this.enterOuterAlt(_localctx, 1); { - this.state = 357; + this.state = 229; this.match(esql_parser.ROW); - this.state = 358; + this.state = 230; this.fields(); } } @@ -1575,30 +1117,30 @@ export class esql_parser extends Parser { // @RuleVersion(0) public fields(): FieldsContext { let _localctx: FieldsContext = new FieldsContext(this._ctx, this.state); - this.enterRule(_localctx, 38, esql_parser.RULE_fields); + this.enterRule(_localctx, 24, esql_parser.RULE_fields); try { let _alt: number; this.enterOuterAlt(_localctx, 1); { - this.state = 360; + this.state = 232; this.field(); - this.state = 365; + this.state = 237; this._errHandler.sync(this); - _alt = this.interpreter.adaptivePredict(this._input, 33, this._ctx); + _alt = this.interpreter.adaptivePredict(this._input, 19, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 361; + this.state = 233; this.match(esql_parser.COMMA); - this.state = 362; + this.state = 234; this.field(); } } } - this.state = 367; + this.state = 239; this._errHandler.sync(this); - _alt = this.interpreter.adaptivePredict(this._input, 33, this._ctx); + _alt = this.interpreter.adaptivePredict(this._input, 19, this._ctx); } } } @@ -1619,15 +1161,15 @@ export class esql_parser extends Parser { // @RuleVersion(0) public field(): FieldContext { let _localctx: FieldContext = new FieldContext(this._ctx, this.state); - this.enterRule(_localctx, 40, esql_parser.RULE_field); + this.enterRule(_localctx, 26, esql_parser.RULE_field); try { - this.state = 373; + this.state = 245; this._errHandler.sync(this); - switch ( this.interpreter.adaptivePredict(this._input, 34, this._ctx) ) { + switch ( this.interpreter.adaptivePredict(this._input, 20, this._ctx) ) { case 1: this.enterOuterAlt(_localctx, 1); { - this.state = 368; + this.state = 240; this.booleanExpression(0); } break; @@ -1635,11 +1177,11 @@ export class esql_parser extends Parser { case 2: this.enterOuterAlt(_localctx, 2); { - this.state = 369; - this.userVariable(); - this.state = 370; + this.state = 241; + this.qualifiedName(); + this.state = 242; this.match(esql_parser.ASSIGN); - this.state = 371; + this.state = 243; this.booleanExpression(0); } break; @@ -1660,102 +1202,41 @@ export class esql_parser extends Parser { return _localctx; } // @RuleVersion(0) - public enrichFieldIdentifier(): EnrichFieldIdentifierContext { - let _localctx: EnrichFieldIdentifierContext = new EnrichFieldIdentifierContext(this._ctx, this.state); - this.enterRule(_localctx, 42, esql_parser.RULE_enrichFieldIdentifier); - let _la: number; - try { - this.enterOuterAlt(_localctx, 1); - { - this.state = 375; - _la = this._input.LA(1); - if (!(_la === esql_parser.ENR_UNQUOTED_IDENTIFIER || _la === esql_parser.ENR_QUOTED_IDENTIFIER)) { - this._errHandler.recoverInline(this); - } else { - if (this._input.LA(1) === Token.EOF) { - this.matchedEOF = true; - } - - this._errHandler.reportMatch(this); - this.consume(); - } - } - } - catch (re) { - if (re instanceof RecognitionException) { - _localctx.exception = re; - this._errHandler.reportError(this, re); - this._errHandler.recover(this, re); - } else { - throw re; - } - } - finally { - this.exitRule(); - } - return _localctx; - } - // @RuleVersion(0) - public userVariable(): UserVariableContext { - let _localctx: UserVariableContext = new UserVariableContext(this._ctx, this.state); - this.enterRule(_localctx, 44, esql_parser.RULE_userVariable); - try { - this.enterOuterAlt(_localctx, 1); - { - this.state = 377; - this.identifier(); - } - } - catch (re) { - if (re instanceof RecognitionException) { - _localctx.exception = re; - this._errHandler.reportError(this, re); - this._errHandler.recover(this, re); - } else { - throw re; - } - } - finally { - this.exitRule(); - } - return _localctx; - } - // @RuleVersion(0) public fromCommand(): FromCommandContext { let _localctx: FromCommandContext = new FromCommandContext(this._ctx, this.state); - this.enterRule(_localctx, 46, esql_parser.RULE_fromCommand); + this.enterRule(_localctx, 28, esql_parser.RULE_fromCommand); try { let _alt: number; this.enterOuterAlt(_localctx, 1); { - this.state = 379; + this.state = 247; this.match(esql_parser.FROM); - this.state = 380; + this.state = 248; this.sourceIdentifier(); - this.state = 385; + this.state = 253; this._errHandler.sync(this); - _alt = this.interpreter.adaptivePredict(this._input, 35, this._ctx); + _alt = this.interpreter.adaptivePredict(this._input, 21, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 381; + this.state = 249; this.match(esql_parser.COMMA); - this.state = 382; + this.state = 250; this.sourceIdentifier(); } } } - this.state = 387; + this.state = 255; this._errHandler.sync(this); - _alt = this.interpreter.adaptivePredict(this._input, 35, this._ctx); + _alt = this.interpreter.adaptivePredict(this._input, 21, this._ctx); } - this.state = 389; + this.state = 257; this._errHandler.sync(this); - switch ( this.interpreter.adaptivePredict(this._input, 36, this._ctx) ) { + switch ( this.interpreter.adaptivePredict(this._input, 22, this._ctx) ) { case 1: { - this.state = 388; + this.state = 256; this.metadata(); } break; @@ -1779,34 +1260,34 @@ export class esql_parser extends Parser { // @RuleVersion(0) public metadata(): MetadataContext { let _localctx: MetadataContext = new MetadataContext(this._ctx, this.state); - this.enterRule(_localctx, 48, esql_parser.RULE_metadata); + this.enterRule(_localctx, 30, esql_parser.RULE_metadata); let _la: number; try { this.enterOuterAlt(_localctx, 1); { - this.state = 391; + this.state = 259; this.match(esql_parser.OPENING_BRACKET); - this.state = 392; + this.state = 260; this.match(esql_parser.METADATA); - this.state = 393; + this.state = 261; this.sourceIdentifier(); - this.state = 398; + this.state = 266; this._errHandler.sync(this); _la = this._input.LA(1); while (_la === esql_parser.COMMA) { { { - this.state = 394; + this.state = 262; this.match(esql_parser.COMMA); - this.state = 395; + this.state = 263; this.sourceIdentifier(); } } - this.state = 400; + this.state = 268; this._errHandler.sync(this); _la = this._input.LA(1); } - this.state = 401; + this.state = 269; this.match(esql_parser.CLOSING_BRACKET); } } @@ -1827,13 +1308,13 @@ export class esql_parser extends Parser { // @RuleVersion(0) public evalCommand(): EvalCommandContext { let _localctx: EvalCommandContext = new EvalCommandContext(this._ctx, this.state); - this.enterRule(_localctx, 50, esql_parser.RULE_evalCommand); + this.enterRule(_localctx, 32, esql_parser.RULE_evalCommand); try { this.enterOuterAlt(_localctx, 1); { - this.state = 403; + this.state = 271; this.match(esql_parser.EVAL); - this.state = 404; + this.state = 272; this.fields(); } } @@ -1854,31 +1335,31 @@ export class esql_parser extends Parser { // @RuleVersion(0) public statsCommand(): StatsCommandContext { let _localctx: StatsCommandContext = new StatsCommandContext(this._ctx, this.state); - this.enterRule(_localctx, 52, esql_parser.RULE_statsCommand); + this.enterRule(_localctx, 34, esql_parser.RULE_statsCommand); try { this.enterOuterAlt(_localctx, 1); { - this.state = 406; + this.state = 274; this.match(esql_parser.STATS); - this.state = 408; + this.state = 276; this._errHandler.sync(this); - switch ( this.interpreter.adaptivePredict(this._input, 38, this._ctx) ) { + switch ( this.interpreter.adaptivePredict(this._input, 24, this._ctx) ) { case 1: { - this.state = 407; + this.state = 275; this.fields(); } break; } - this.state = 412; + this.state = 280; this._errHandler.sync(this); - switch ( this.interpreter.adaptivePredict(this._input, 39, this._ctx) ) { + switch ( this.interpreter.adaptivePredict(this._input, 25, this._ctx) ) { case 1: { - this.state = 410; + this.state = 278; this.match(esql_parser.BY); - this.state = 411; - this.qualifiedNames(); + this.state = 279; + this.grouping(); } break; } @@ -1899,24 +1380,32 @@ export class esql_parser extends Parser { return _localctx; } // @RuleVersion(0) - public sourceIdentifier(): SourceIdentifierContext { - let _localctx: SourceIdentifierContext = new SourceIdentifierContext(this._ctx, this.state); - this.enterRule(_localctx, 54, esql_parser.RULE_sourceIdentifier); - let _la: number; + public grouping(): GroupingContext { + let _localctx: GroupingContext = new GroupingContext(this._ctx, this.state); + this.enterRule(_localctx, 36, esql_parser.RULE_grouping); try { + let _alt: number; this.enterOuterAlt(_localctx, 1); { - this.state = 414; - _la = this._input.LA(1); - if (!(_la === esql_parser.SRC_UNQUOTED_IDENTIFIER || _la === esql_parser.SRC_QUOTED_IDENTIFIER)) { - this._errHandler.recoverInline(this); - } else { - if (this._input.LA(1) === Token.EOF) { - this.matchedEOF = true; + this.state = 282; + this.qualifiedName(); + this.state = 287; + this._errHandler.sync(this); + _alt = this.interpreter.adaptivePredict(this._input, 26, this._ctx); + while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { + if (_alt === 1) { + { + { + this.state = 283; + this.match(esql_parser.COMMA); + this.state = 284; + this.qualifiedName(); + } + } } - - this._errHandler.reportMatch(this); - this.consume(); + this.state = 289; + this._errHandler.sync(this); + _alt = this.interpreter.adaptivePredict(this._input, 26, this._ctx); } } } @@ -1935,16 +1424,16 @@ export class esql_parser extends Parser { return _localctx; } // @RuleVersion(0) - public enrichIdentifier(): EnrichIdentifierContext { - let _localctx: EnrichIdentifierContext = new EnrichIdentifierContext(this._ctx, this.state); - this.enterRule(_localctx, 56, esql_parser.RULE_enrichIdentifier); + public sourceIdentifier(): SourceIdentifierContext { + let _localctx: SourceIdentifierContext = new SourceIdentifierContext(this._ctx, this.state); + this.enterRule(_localctx, 38, esql_parser.RULE_sourceIdentifier); let _la: number; try { this.enterOuterAlt(_localctx, 1); { - this.state = 416; + this.state = 290; _la = this._input.LA(1); - if (!(_la === esql_parser.ENR_UNQUOTED_IDENTIFIER || _la === esql_parser.ENR_QUOTED_IDENTIFIER)) { + if (!(_la === esql_parser.SRC_UNQUOTED_IDENTIFIER || _la === esql_parser.SRC_QUOTED_IDENTIFIER)) { this._errHandler.recoverInline(this); } else { if (this._input.LA(1) === Token.EOF) { @@ -1971,197 +1460,32 @@ export class esql_parser extends Parser { return _localctx; } // @RuleVersion(0) - public functionExpressionArgument(): FunctionExpressionArgumentContext { - let _localctx: FunctionExpressionArgumentContext = new FunctionExpressionArgumentContext(this._ctx, this.state); - this.enterRule(_localctx, 58, esql_parser.RULE_functionExpressionArgument); - try { - this.state = 421; - this._errHandler.sync(this); - switch (this._input.LA(1)) { - case esql_parser.ASTERISK: - case esql_parser.UNQUOTED_IDENTIFIER: - case esql_parser.QUOTED_IDENTIFIER: - this.enterOuterAlt(_localctx, 1); - { - this.state = 418; - this.qualifiedName(); - } - break; - case esql_parser.STRING: - this.enterOuterAlt(_localctx, 2); - { - this.state = 419; - this.string(); - } - break; - case esql_parser.INTEGER_LITERAL: - case esql_parser.DECIMAL_LITERAL: - this.enterOuterAlt(_localctx, 3); - { - this.state = 420; - this.number(); - } - break; - default: - throw new NoViableAltException(this); - } - } - catch (re) { - if (re instanceof RecognitionException) { - _localctx.exception = re; - this._errHandler.reportError(this, re); - this._errHandler.recover(this, re); - } else { - throw re; - } - } - finally { - this.exitRule(); - } - return _localctx; - } - // @RuleVersion(0) - public mathFunctionExpressionArgument(): MathFunctionExpressionArgumentContext { - let _localctx: MathFunctionExpressionArgumentContext = new MathFunctionExpressionArgumentContext(this._ctx, this.state); - this.enterRule(_localctx, 60, esql_parser.RULE_mathFunctionExpressionArgument); - try { - this.state = 429; - this._errHandler.sync(this); - switch ( this.interpreter.adaptivePredict(this._input, 41, this._ctx) ) { - case 1: - this.enterOuterAlt(_localctx, 1); - { - this.state = 423; - this.qualifiedName(); - } - break; - - case 2: - this.enterOuterAlt(_localctx, 2); - { - this.state = 424; - this.string(); - } - break; - - case 3: - this.enterOuterAlt(_localctx, 3); - { - this.state = 425; - this.number(); - } - break; - - case 4: - this.enterOuterAlt(_localctx, 4); - { - this.state = 426; - this.operatorExpression(0); - } - break; - - case 5: - this.enterOuterAlt(_localctx, 5); - { - this.state = 427; - this.dateExpression(); - } - break; - - case 6: - this.enterOuterAlt(_localctx, 6); - { - this.state = 428; - this.comparison(); - } - break; - } - } - catch (re) { - if (re instanceof RecognitionException) { - _localctx.exception = re; - this._errHandler.reportError(this, re); - this._errHandler.recover(this, re); - } else { - throw re; - } - } - finally { - this.exitRule(); - } - return _localctx; - } - // @RuleVersion(0) public qualifiedName(): QualifiedNameContext { let _localctx: QualifiedNameContext = new QualifiedNameContext(this._ctx, this.state); - this.enterRule(_localctx, 62, esql_parser.RULE_qualifiedName); + this.enterRule(_localctx, 40, esql_parser.RULE_qualifiedName); try { let _alt: number; this.enterOuterAlt(_localctx, 1); { - this.state = 431; + this.state = 292; this.identifier(); - this.state = 436; + this.state = 297; this._errHandler.sync(this); - _alt = this.interpreter.adaptivePredict(this._input, 42, this._ctx); + _alt = this.interpreter.adaptivePredict(this._input, 27, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 432; + this.state = 293; this.match(esql_parser.DOT); - this.state = 433; + this.state = 294; this.identifier(); } } } - this.state = 438; - this._errHandler.sync(this); - _alt = this.interpreter.adaptivePredict(this._input, 42, this._ctx); - } - } - } - catch (re) { - if (re instanceof RecognitionException) { - _localctx.exception = re; - this._errHandler.reportError(this, re); - this._errHandler.recover(this, re); - } else { - throw re; - } - } - finally { - this.exitRule(); - } - return _localctx; - } - // @RuleVersion(0) - public qualifiedNames(): QualifiedNamesContext { - let _localctx: QualifiedNamesContext = new QualifiedNamesContext(this._ctx, this.state); - this.enterRule(_localctx, 64, esql_parser.RULE_qualifiedNames); - try { - let _alt: number; - this.enterOuterAlt(_localctx, 1); - { - this.state = 439; - this.qualifiedName(); - this.state = 444; - this._errHandler.sync(this); - _alt = this.interpreter.adaptivePredict(this._input, 43, this._ctx); - while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { - if (_alt === 1) { - { - { - this.state = 440; - this.match(esql_parser.COMMA); - this.state = 441; - this.qualifiedName(); - } - } - } - this.state = 446; + this.state = 299; this._errHandler.sync(this); - _alt = this.interpreter.adaptivePredict(this._input, 43, this._ctx); + _alt = this.interpreter.adaptivePredict(this._input, 27, this._ctx); } } } @@ -2182,14 +1506,14 @@ export class esql_parser extends Parser { // @RuleVersion(0) public identifier(): IdentifierContext { let _localctx: IdentifierContext = new IdentifierContext(this._ctx, this.state); - this.enterRule(_localctx, 66, esql_parser.RULE_identifier); + this.enterRule(_localctx, 42, esql_parser.RULE_identifier); let _la: number; try { this.enterOuterAlt(_localctx, 1); { - this.state = 447; + this.state = 300; _la = this._input.LA(1); - if (!(((((_la - 53)) & ~0x1F) === 0 && ((1 << (_la - 53)) & ((1 << (esql_parser.ASTERISK - 53)) | (1 << (esql_parser.UNQUOTED_IDENTIFIER - 53)) | (1 << (esql_parser.QUOTED_IDENTIFIER - 53)))) !== 0))) { + if (!(_la === esql_parser.UNQUOTED_IDENTIFIER || _la === esql_parser.QUOTED_IDENTIFIER)) { this._errHandler.recoverInline(this); } else { if (this._input.LA(1) === Token.EOF) { @@ -2216,176 +1540,162 @@ export class esql_parser extends Parser { return _localctx; } // @RuleVersion(0) - public mathFunctionIdentifier(): MathFunctionIdentifierContext { - let _localctx: MathFunctionIdentifierContext = new MathFunctionIdentifierContext(this._ctx, this.state); - this.enterRule(_localctx, 68, esql_parser.RULE_mathFunctionIdentifier); - try { - this.enterOuterAlt(_localctx, 1); - { - this.state = 449; - this.match(esql_parser.MATH_FUNCTION); - } - } - catch (re) { - if (re instanceof RecognitionException) { - _localctx.exception = re; - this._errHandler.reportError(this, re); - this._errHandler.recover(this, re); - } else { - throw re; - } - } - finally { - this.exitRule(); - } - return _localctx; - } - // @RuleVersion(0) - public functionIdentifier(): FunctionIdentifierContext { - let _localctx: FunctionIdentifierContext = new FunctionIdentifierContext(this._ctx, this.state); - this.enterRule(_localctx, 70, esql_parser.RULE_functionIdentifier); - try { - this.enterOuterAlt(_localctx, 1); - { - this.state = 451; - this.match(esql_parser.UNARY_FUNCTION); - } - } - catch (re) { - if (re instanceof RecognitionException) { - _localctx.exception = re; - this._errHandler.reportError(this, re); - this._errHandler.recover(this, re); - } else { - throw re; - } - } - finally { - this.exitRule(); - } - return _localctx; - } - // @RuleVersion(0) public constant(): ConstantContext { let _localctx: ConstantContext = new ConstantContext(this._ctx, this.state); - this.enterRule(_localctx, 72, esql_parser.RULE_constant); + this.enterRule(_localctx, 44, esql_parser.RULE_constant); let _la: number; try { - this.state = 490; + this.state = 344; this._errHandler.sync(this); - switch ( this.interpreter.adaptivePredict(this._input, 47, this._ctx) ) { + switch ( this.interpreter.adaptivePredict(this._input, 31, this._ctx) ) { case 1: + _localctx = new NullLiteralContext(_localctx); this.enterOuterAlt(_localctx, 1); { - this.state = 453; + this.state = 302; this.match(esql_parser.NULL); } break; case 2: + _localctx = new QualifiedIntegerLiteralContext(_localctx); this.enterOuterAlt(_localctx, 2); { - this.state = 454; - this.numericValue(); + this.state = 303; + this.integerValue(); + this.state = 304; + this.match(esql_parser.UNQUOTED_IDENTIFIER); } break; case 3: + _localctx = new DecimalLiteralContext(_localctx); this.enterOuterAlt(_localctx, 3); { - this.state = 455; - this.booleanValue(); + this.state = 306; + this.decimalValue(); } break; case 4: + _localctx = new IntegerLiteralContext(_localctx); this.enterOuterAlt(_localctx, 4); { - this.state = 456; + this.state = 307; + this.integerValue(); + } + break; + + case 5: + _localctx = new BooleanLiteralContext(_localctx); + this.enterOuterAlt(_localctx, 5); + { + this.state = 308; + this.booleanValue(); + } + break; + + case 6: + _localctx = new InputParamContext(_localctx); + this.enterOuterAlt(_localctx, 6); + { + this.state = 309; + this.match(esql_parser.PARAM); + } + break; + + case 7: + _localctx = new StringLiteralContext(_localctx); + this.enterOuterAlt(_localctx, 7); + { + this.state = 310; this.string(); } break; - case 5: - this.enterOuterAlt(_localctx, 5); + case 8: + _localctx = new NumericArrayLiteralContext(_localctx); + this.enterOuterAlt(_localctx, 8); { - this.state = 457; + this.state = 311; this.match(esql_parser.OPENING_BRACKET); - this.state = 458; + this.state = 312; this.numericValue(); - this.state = 463; + this.state = 317; this._errHandler.sync(this); _la = this._input.LA(1); while (_la === esql_parser.COMMA) { { { - this.state = 459; + this.state = 313; this.match(esql_parser.COMMA); - this.state = 460; + this.state = 314; this.numericValue(); } } - this.state = 465; + this.state = 319; this._errHandler.sync(this); _la = this._input.LA(1); } - this.state = 466; + this.state = 320; this.match(esql_parser.CLOSING_BRACKET); } break; - case 6: - this.enterOuterAlt(_localctx, 6); + case 9: + _localctx = new BooleanArrayLiteralContext(_localctx); + this.enterOuterAlt(_localctx, 9); { - this.state = 468; + this.state = 322; this.match(esql_parser.OPENING_BRACKET); - this.state = 469; + this.state = 323; this.booleanValue(); - this.state = 474; + this.state = 328; this._errHandler.sync(this); _la = this._input.LA(1); while (_la === esql_parser.COMMA) { { { - this.state = 470; + this.state = 324; this.match(esql_parser.COMMA); - this.state = 471; + this.state = 325; this.booleanValue(); } } - this.state = 476; + this.state = 330; this._errHandler.sync(this); _la = this._input.LA(1); } - this.state = 477; + this.state = 331; this.match(esql_parser.CLOSING_BRACKET); } break; - case 7: - this.enterOuterAlt(_localctx, 7); + case 10: + _localctx = new StringArrayLiteralContext(_localctx); + this.enterOuterAlt(_localctx, 10); { - this.state = 479; + this.state = 333; this.match(esql_parser.OPENING_BRACKET); - this.state = 480; + this.state = 334; this.string(); - this.state = 485; + this.state = 339; this._errHandler.sync(this); _la = this._input.LA(1); while (_la === esql_parser.COMMA) { { { - this.state = 481; + this.state = 335; this.match(esql_parser.COMMA); - this.state = 482; + this.state = 336; this.string(); } } - this.state = 487; + this.state = 341; this._errHandler.sync(this); _la = this._input.LA(1); } - this.state = 488; + this.state = 342; this.match(esql_parser.CLOSING_BRACKET); } break; @@ -2406,55 +1716,15 @@ export class esql_parser extends Parser { return _localctx; } // @RuleVersion(0) - public numericValue(): NumericValueContext { - let _localctx: NumericValueContext = new NumericValueContext(this._ctx, this.state); - this.enterRule(_localctx, 74, esql_parser.RULE_numericValue); - try { - this.state = 494; - this._errHandler.sync(this); - switch (this._input.LA(1)) { - case esql_parser.DECIMAL_LITERAL: - this.enterOuterAlt(_localctx, 1); - { - this.state = 492; - this.decimalValue(); - } - break; - case esql_parser.INTEGER_LITERAL: - this.enterOuterAlt(_localctx, 2); - { - this.state = 493; - this.integerValue(); - } - break; - default: - throw new NoViableAltException(this); - } - } - catch (re) { - if (re instanceof RecognitionException) { - _localctx.exception = re; - this._errHandler.reportError(this, re); - this._errHandler.recover(this, re); - } else { - throw re; - } - } - finally { - this.exitRule(); - } - return _localctx; - } - // @RuleVersion(0) public limitCommand(): LimitCommandContext { let _localctx: LimitCommandContext = new LimitCommandContext(this._ctx, this.state); - this.enterRule(_localctx, 76, esql_parser.RULE_limitCommand); + this.enterRule(_localctx, 46, esql_parser.RULE_limitCommand); try { this.enterOuterAlt(_localctx, 1); { - this.state = 496; + this.state = 346; this.match(esql_parser.LIMIT); - this.state = 497; + this.state = 347; this.match(esql_parser.INTEGER_LITERAL); } } @@ -2475,32 +1745,32 @@ export class esql_parser extends Parser { // @RuleVersion(0) public sortCommand(): SortCommandContext { let _localctx: SortCommandContext = new SortCommandContext(this._ctx, this.state); - this.enterRule(_localctx, 78, esql_parser.RULE_sortCommand); + this.enterRule(_localctx, 48, esql_parser.RULE_sortCommand); try { let _alt: number; this.enterOuterAlt(_localctx, 1); { - this.state = 499; + this.state = 349; this.match(esql_parser.SORT); - this.state = 500; + this.state = 350; this.orderExpression(); - this.state = 505; + this.state = 355; this._errHandler.sync(this); - _alt = this.interpreter.adaptivePredict(this._input, 49, this._ctx); + _alt = this.interpreter.adaptivePredict(this._input, 32, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 501; + this.state = 351; this.match(esql_parser.COMMA); - this.state = 502; + this.state = 352; this.orderExpression(); } } } - this.state = 507; + this.state = 357; this._errHandler.sync(this); - _alt = this.interpreter.adaptivePredict(this._input, 49, this._ctx); + _alt = this.interpreter.adaptivePredict(this._input, 32, this._ctx); } } } @@ -2521,32 +1791,53 @@ export class esql_parser extends Parser { // @RuleVersion(0) public orderExpression(): OrderExpressionContext { let _localctx: OrderExpressionContext = new OrderExpressionContext(this._ctx, this.state); - this.enterRule(_localctx, 80, esql_parser.RULE_orderExpression); + this.enterRule(_localctx, 50, esql_parser.RULE_orderExpression); + let _la: number; try { this.enterOuterAlt(_localctx, 1); { - this.state = 508; + this.state = 358; this.booleanExpression(0); - this.state = 510; + this.state = 360; this._errHandler.sync(this); - switch ( this.interpreter.adaptivePredict(this._input, 50, this._ctx) ) { + switch ( this.interpreter.adaptivePredict(this._input, 33, this._ctx) ) { case 1: { - this.state = 509; - this.match(esql_parser.ORDERING); + this.state = 359; + _localctx._ordering = this._input.LT(1); + _la = this._input.LA(1); + if (!(_la === esql_parser.ASC || _la === esql_parser.DESC)) { + _localctx._ordering = this._errHandler.recoverInline(this); + } else { + if (this._input.LA(1) === Token.EOF) { + this.matchedEOF = true; + } + + this._errHandler.reportMatch(this); + this.consume(); + } } break; } - this.state = 514; + this.state = 364; this._errHandler.sync(this); - switch ( this.interpreter.adaptivePredict(this._input, 51, this._ctx) ) { + switch ( this.interpreter.adaptivePredict(this._input, 34, this._ctx) ) { case 1: { - this.state = 512; - this.match(esql_parser.NULLS_ORDERING); - { - this.state = 513; - this.match(esql_parser.NULLS_ORDERING_DIRECTION); + this.state = 362; + this.match(esql_parser.NULLS); + this.state = 363; + _localctx._nullOrdering = this._input.LT(1); + _la = this._input.LA(1); + if (!(_la === esql_parser.FIRST || _la === esql_parser.LAST)) { + _localctx._nullOrdering = this._errHandler.recoverInline(this); + } else { + if (this._input.LA(1) === Token.EOF) { + this.matchedEOF = true; + } + + this._errHandler.reportMatch(this); + this.consume(); } } break; @@ -2568,43 +1859,70 @@ export class esql_parser extends Parser { return _localctx; } // @RuleVersion(0) - public projectCommand(): ProjectCommandContext { - let _localctx: ProjectCommandContext = new ProjectCommandContext(this._ctx, this.state); - this.enterRule(_localctx, 82, esql_parser.RULE_projectCommand); - try { - this.enterOuterAlt(_localctx, 1); - { - this.state = 516; - this.match(esql_parser.PROJECT); - this.state = 517; - this.qualifiedNames(); - } - } - catch (re) { - if (re instanceof RecognitionException) { - _localctx.exception = re; - this._errHandler.reportError(this, re); - this._errHandler.recover(this, re); - } else { - throw re; - } - } - finally { - this.exitRule(); - } - return _localctx; - } - // @RuleVersion(0) public keepCommand(): KeepCommandContext { let _localctx: KeepCommandContext = new KeepCommandContext(this._ctx, this.state); - this.enterRule(_localctx, 84, esql_parser.RULE_keepCommand); + this.enterRule(_localctx, 52, esql_parser.RULE_keepCommand); try { - this.enterOuterAlt(_localctx, 1); - { - this.state = 519; - this.match(esql_parser.KEEP); - this.state = 520; - this.qualifiedNames(); + let _alt: number; + this.state = 384; + this._errHandler.sync(this); + switch (this._input.LA(1)) { + case esql_parser.KEEP: + this.enterOuterAlt(_localctx, 1); + { + this.state = 366; + this.match(esql_parser.KEEP); + this.state = 367; + this.sourceIdentifier(); + this.state = 372; + this._errHandler.sync(this); + _alt = this.interpreter.adaptivePredict(this._input, 35, this._ctx); + while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { + if (_alt === 1) { + { + { + this.state = 368; + this.match(esql_parser.COMMA); + this.state = 369; + this.sourceIdentifier(); + } + } + } + this.state = 374; + this._errHandler.sync(this); + _alt = this.interpreter.adaptivePredict(this._input, 35, this._ctx); + } + } + break; + case esql_parser.PROJECT: + this.enterOuterAlt(_localctx, 2); + { + this.state = 375; + this.match(esql_parser.PROJECT); + this.state = 376; + this.sourceIdentifier(); + this.state = 381; + this._errHandler.sync(this); + _alt = this.interpreter.adaptivePredict(this._input, 36, this._ctx); + while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { + if (_alt === 1) { + { + { + this.state = 377; + this.match(esql_parser.COMMA); + this.state = 378; + this.sourceIdentifier(); + } + } + } + this.state = 383; + this._errHandler.sync(this); + _alt = this.interpreter.adaptivePredict(this._input, 36, this._ctx); + } + } + break; + default: + throw new NoViableAltException(this); } } catch (re) { @@ -2624,57 +1942,32 @@ export class esql_parser extends Parser { // @RuleVersion(0) public dropCommand(): DropCommandContext { let _localctx: DropCommandContext = new DropCommandContext(this._ctx, this.state); - this.enterRule(_localctx, 86, esql_parser.RULE_dropCommand); - try { - this.enterOuterAlt(_localctx, 1); - { - this.state = 522; - this.match(esql_parser.DROP); - this.state = 523; - this.qualifiedNames(); - } - } - catch (re) { - if (re instanceof RecognitionException) { - _localctx.exception = re; - this._errHandler.reportError(this, re); - this._errHandler.recover(this, re); - } else { - throw re; - } - } - finally { - this.exitRule(); - } - return _localctx; - } - // @RuleVersion(0) - public renameVariable(): RenameVariableContext { - let _localctx: RenameVariableContext = new RenameVariableContext(this._ctx, this.state); - this.enterRule(_localctx, 88, esql_parser.RULE_renameVariable); + this.enterRule(_localctx, 54, esql_parser.RULE_dropCommand); try { let _alt: number; this.enterOuterAlt(_localctx, 1); { - this.state = 525; - this.identifier(); - this.state = 530; + this.state = 386; + this.match(esql_parser.DROP); + this.state = 387; + this.sourceIdentifier(); + this.state = 392; this._errHandler.sync(this); - _alt = this.interpreter.adaptivePredict(this._input, 52, this._ctx); + _alt = this.interpreter.adaptivePredict(this._input, 38, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 526; - this.match(esql_parser.DOT); - this.state = 527; - this.identifier(); + this.state = 388; + this.match(esql_parser.COMMA); + this.state = 389; + this.sourceIdentifier(); } } } - this.state = 532; + this.state = 394; this._errHandler.sync(this); - _alt = this.interpreter.adaptivePredict(this._input, 52, this._ctx); + _alt = this.interpreter.adaptivePredict(this._input, 38, this._ctx); } } } @@ -2695,32 +1988,32 @@ export class esql_parser extends Parser { // @RuleVersion(0) public renameCommand(): RenameCommandContext { let _localctx: RenameCommandContext = new RenameCommandContext(this._ctx, this.state); - this.enterRule(_localctx, 90, esql_parser.RULE_renameCommand); + this.enterRule(_localctx, 56, esql_parser.RULE_renameCommand); try { let _alt: number; this.enterOuterAlt(_localctx, 1); { - this.state = 533; + this.state = 395; this.match(esql_parser.RENAME); - this.state = 534; + this.state = 396; this.renameClause(); - this.state = 539; + this.state = 401; this._errHandler.sync(this); - _alt = this.interpreter.adaptivePredict(this._input, 53, this._ctx); + _alt = this.interpreter.adaptivePredict(this._input, 39, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 535; + this.state = 397; this.match(esql_parser.COMMA); - this.state = 536; + this.state = 398; this.renameClause(); } } } - this.state = 541; + this.state = 403; this._errHandler.sync(this); - _alt = this.interpreter.adaptivePredict(this._input, 53, this._ctx); + _alt = this.interpreter.adaptivePredict(this._input, 39, this._ctx); } } } @@ -2741,16 +2034,16 @@ export class esql_parser extends Parser { // @RuleVersion(0) public renameClause(): RenameClauseContext { let _localctx: RenameClauseContext = new RenameClauseContext(this._ctx, this.state); - this.enterRule(_localctx, 92, esql_parser.RULE_renameClause); + this.enterRule(_localctx, 58, esql_parser.RULE_renameClause); try { this.enterOuterAlt(_localctx, 1); { - this.state = 542; - this.qualifiedName(); - this.state = 543; + this.state = 404; + _localctx._oldName = this.sourceIdentifier(); + this.state = 405; this.match(esql_parser.AS); - this.state = 544; - this.renameVariable(); + this.state = 406; + _localctx._newName = this.sourceIdentifier(); } } catch (re) { @@ -2770,22 +2063,22 @@ export class esql_parser extends Parser { // @RuleVersion(0) public dissectCommand(): DissectCommandContext { let _localctx: DissectCommandContext = new DissectCommandContext(this._ctx, this.state); - this.enterRule(_localctx, 94, esql_parser.RULE_dissectCommand); + this.enterRule(_localctx, 60, esql_parser.RULE_dissectCommand); try { this.enterOuterAlt(_localctx, 1); { - this.state = 546; + this.state = 408; this.match(esql_parser.DISSECT); - this.state = 547; - this.qualifiedNames(); - this.state = 548; + this.state = 409; + this.primaryExpression(); + this.state = 410; this.string(); - this.state = 550; + this.state = 412; this._errHandler.sync(this); - switch ( this.interpreter.adaptivePredict(this._input, 54, this._ctx) ) { + switch ( this.interpreter.adaptivePredict(this._input, 40, this._ctx) ) { case 1: { - this.state = 549; + this.state = 411; this.commandOptions(); } break; @@ -2809,15 +2102,15 @@ export class esql_parser extends Parser { // @RuleVersion(0) public grokCommand(): GrokCommandContext { let _localctx: GrokCommandContext = new GrokCommandContext(this._ctx, this.state); - this.enterRule(_localctx, 96, esql_parser.RULE_grokCommand); + this.enterRule(_localctx, 62, esql_parser.RULE_grokCommand); try { this.enterOuterAlt(_localctx, 1); { - this.state = 552; + this.state = 414; this.match(esql_parser.GROK); - this.state = 553; - this.qualifiedNames(); - this.state = 554; + this.state = 415; + this.primaryExpression(); + this.state = 416; this.string(); } } @@ -2836,32 +2129,59 @@ export class esql_parser extends Parser { return _localctx; } // @RuleVersion(0) + public mvExpandCommand(): MvExpandCommandContext { + let _localctx: MvExpandCommandContext = new MvExpandCommandContext(this._ctx, this.state); + this.enterRule(_localctx, 64, esql_parser.RULE_mvExpandCommand); + try { + this.enterOuterAlt(_localctx, 1); + { + this.state = 418; + this.match(esql_parser.MV_EXPAND); + this.state = 419; + this.sourceIdentifier(); + } + } + catch (re) { + if (re instanceof RecognitionException) { + _localctx.exception = re; + this._errHandler.reportError(this, re); + this._errHandler.recover(this, re); + } else { + throw re; + } + } + finally { + this.exitRule(); + } + return _localctx; + } + // @RuleVersion(0) public commandOptions(): CommandOptionsContext { let _localctx: CommandOptionsContext = new CommandOptionsContext(this._ctx, this.state); - this.enterRule(_localctx, 98, esql_parser.RULE_commandOptions); + this.enterRule(_localctx, 66, esql_parser.RULE_commandOptions); try { let _alt: number; this.enterOuterAlt(_localctx, 1); { - this.state = 556; + this.state = 421; this.commandOption(); - this.state = 561; + this.state = 426; this._errHandler.sync(this); - _alt = this.interpreter.adaptivePredict(this._input, 55, this._ctx); + _alt = this.interpreter.adaptivePredict(this._input, 41, this._ctx); while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { if (_alt === 1) { { { - this.state = 557; + this.state = 422; this.match(esql_parser.COMMA); - this.state = 558; + this.state = 423; this.commandOption(); } } } - this.state = 563; + this.state = 428; this._errHandler.sync(this); - _alt = this.interpreter.adaptivePredict(this._input, 55, this._ctx); + _alt = this.interpreter.adaptivePredict(this._input, 41, this._ctx); } } } @@ -2882,15 +2202,15 @@ export class esql_parser extends Parser { // @RuleVersion(0) public commandOption(): CommandOptionContext { let _localctx: CommandOptionContext = new CommandOptionContext(this._ctx, this.state); - this.enterRule(_localctx, 100, esql_parser.RULE_commandOption); + this.enterRule(_localctx, 68, esql_parser.RULE_commandOption); try { this.enterOuterAlt(_localctx, 1); { - this.state = 564; + this.state = 429; this.identifier(); - this.state = 565; + this.state = 430; this.match(esql_parser.ASSIGN); - this.state = 566; + this.state = 431; this.constant(); } } @@ -2911,12 +2231,23 @@ export class esql_parser extends Parser { // @RuleVersion(0) public booleanValue(): BooleanValueContext { let _localctx: BooleanValueContext = new BooleanValueContext(this._ctx, this.state); - this.enterRule(_localctx, 102, esql_parser.RULE_booleanValue); + this.enterRule(_localctx, 70, esql_parser.RULE_booleanValue); + let _la: number; try { this.enterOuterAlt(_localctx, 1); { - this.state = 568; - this.match(esql_parser.BOOLEAN_VALUE); + this.state = 433; + _la = this._input.LA(1); + if (!(_la === esql_parser.FALSE || _la === esql_parser.TRUE)) { + this._errHandler.recoverInline(this); + } else { + if (this._input.LA(1) === Token.EOF) { + this.matchedEOF = true; + } + + this._errHandler.reportMatch(this); + this.consume(); + } } } catch (re) { @@ -2934,31 +2265,28 @@ export class esql_parser extends Parser { return _localctx; } // @RuleVersion(0) - public number(): NumberContext { - let _localctx: NumberContext = new NumberContext(this._ctx, this.state); - this.enterRule(_localctx, 104, esql_parser.RULE_number); + public numericValue(): NumericValueContext { + let _localctx: NumericValueContext = new NumericValueContext(this._ctx, this.state); + this.enterRule(_localctx, 72, esql_parser.RULE_numericValue); try { - this.state = 572; + this.state = 437; this._errHandler.sync(this); - switch (this._input.LA(1)) { - case esql_parser.DECIMAL_LITERAL: - _localctx = new DecimalLiteralContext(_localctx); + switch ( this.interpreter.adaptivePredict(this._input, 42, this._ctx) ) { + case 1: this.enterOuterAlt(_localctx, 1); { - this.state = 570; - this.match(esql_parser.DECIMAL_LITERAL); + this.state = 435; + this.decimalValue(); } break; - case esql_parser.INTEGER_LITERAL: - _localctx = new IntegerLiteralContext(_localctx); + + case 2: this.enterOuterAlt(_localctx, 2); { - this.state = 571; - this.match(esql_parser.INTEGER_LITERAL); + this.state = 436; + this.integerValue(); } break; - default: - throw new NoViableAltException(this); } } catch (re) { @@ -2978,11 +2306,32 @@ export class esql_parser extends Parser { // @RuleVersion(0) public decimalValue(): DecimalValueContext { let _localctx: DecimalValueContext = new DecimalValueContext(this._ctx, this.state); - this.enterRule(_localctx, 106, esql_parser.RULE_decimalValue); + this.enterRule(_localctx, 74, esql_parser.RULE_decimalValue); + let _la: number; try { this.enterOuterAlt(_localctx, 1); { - this.state = 574; + this.state = 440; + this._errHandler.sync(this); + _la = this._input.LA(1); + if (_la === esql_parser.PLUS || _la === esql_parser.MINUS) { + { + this.state = 439; + _la = this._input.LA(1); + if (!(_la === esql_parser.PLUS || _la === esql_parser.MINUS)) { + this._errHandler.recoverInline(this); + } else { + if (this._input.LA(1) === Token.EOF) { + this.matchedEOF = true; + } + + this._errHandler.reportMatch(this); + this.consume(); + } + } + } + + this.state = 442; this.match(esql_parser.DECIMAL_LITERAL); } } @@ -3003,11 +2352,32 @@ export class esql_parser extends Parser { // @RuleVersion(0) public integerValue(): IntegerValueContext { let _localctx: IntegerValueContext = new IntegerValueContext(this._ctx, this.state); - this.enterRule(_localctx, 108, esql_parser.RULE_integerValue); + this.enterRule(_localctx, 76, esql_parser.RULE_integerValue); + let _la: number; try { this.enterOuterAlt(_localctx, 1); { - this.state = 576; + this.state = 445; + this._errHandler.sync(this); + _la = this._input.LA(1); + if (_la === esql_parser.PLUS || _la === esql_parser.MINUS) { + { + this.state = 444; + _la = this._input.LA(1); + if (!(_la === esql_parser.PLUS || _la === esql_parser.MINUS)) { + this._errHandler.recoverInline(this); + } else { + if (this._input.LA(1) === Token.EOF) { + this.matchedEOF = true; + } + + this._errHandler.reportMatch(this); + this.consume(); + } + } + } + + this.state = 447; this.match(esql_parser.INTEGER_LITERAL); } } @@ -3028,11 +2398,11 @@ export class esql_parser extends Parser { // @RuleVersion(0) public string(): StringContext { let _localctx: StringContext = new StringContext(this._ctx, this.state); - this.enterRule(_localctx, 110, esql_parser.RULE_string); + this.enterRule(_localctx, 78, esql_parser.RULE_string); try { this.enterOuterAlt(_localctx, 1); { - this.state = 578; + this.state = 449; this.match(esql_parser.STRING); } } @@ -3053,12 +2423,23 @@ export class esql_parser extends Parser { // @RuleVersion(0) public comparisonOperator(): ComparisonOperatorContext { let _localctx: ComparisonOperatorContext = new ComparisonOperatorContext(this._ctx, this.state); - this.enterRule(_localctx, 112, esql_parser.RULE_comparisonOperator); + this.enterRule(_localctx, 80, esql_parser.RULE_comparisonOperator); + let _la: number; try { this.enterOuterAlt(_localctx, 1); { - this.state = 580; - this.match(esql_parser.COMPARISON_OPERATOR); + this.state = 451; + _la = this._input.LA(1); + if (!(((((_la - 50)) & ~0x1F) === 0 && ((1 << (_la - 50)) & ((1 << (esql_parser.EQ - 50)) | (1 << (esql_parser.NEQ - 50)) | (1 << (esql_parser.LT - 50)) | (1 << (esql_parser.LTE - 50)) | (1 << (esql_parser.GT - 50)) | (1 << (esql_parser.GTE - 50)))) !== 0))) { + this._errHandler.recoverInline(this); + } else { + if (this._input.LA(1) === Token.EOF) { + this.matchedEOF = true; + } + + this._errHandler.reportMatch(this); + this.consume(); + } } } catch (re) { @@ -3076,16 +2457,34 @@ export class esql_parser extends Parser { return _localctx; } // @RuleVersion(0) - public explainCommand(): ExplainCommandContext { - let _localctx: ExplainCommandContext = new ExplainCommandContext(this._ctx, this.state); - this.enterRule(_localctx, 114, esql_parser.RULE_explainCommand); + public showCommand(): ShowCommandContext { + let _localctx: ShowCommandContext = new ShowCommandContext(this._ctx, this.state); + this.enterRule(_localctx, 82, esql_parser.RULE_showCommand); try { - this.enterOuterAlt(_localctx, 1); - { - this.state = 582; - this.match(esql_parser.EXPLAIN); - this.state = 583; - this.subqueryExpression(); + this.state = 457; + this._errHandler.sync(this); + switch ( this.interpreter.adaptivePredict(this._input, 45, this._ctx) ) { + case 1: + _localctx = new ShowInfoContext(_localctx); + this.enterOuterAlt(_localctx, 1); + { + this.state = 453; + this.match(esql_parser.SHOW); + this.state = 454; + this.match(esql_parser.INFO); + } + break; + + case 2: + _localctx = new ShowFunctionsContext(_localctx); + this.enterOuterAlt(_localctx, 2); + { + this.state = 455; + this.match(esql_parser.SHOW); + this.state = 456; + this.match(esql_parser.FUNCTIONS); + } + break; } } catch (re) { @@ -3103,18 +2502,59 @@ export class esql_parser extends Parser { return _localctx; } // @RuleVersion(0) - public subqueryExpression(): SubqueryExpressionContext { - let _localctx: SubqueryExpressionContext = new SubqueryExpressionContext(this._ctx, this.state); - this.enterRule(_localctx, 116, esql_parser.RULE_subqueryExpression); + public enrichCommand(): EnrichCommandContext { + let _localctx: EnrichCommandContext = new EnrichCommandContext(this._ctx, this.state); + this.enterRule(_localctx, 84, esql_parser.RULE_enrichCommand); try { + let _alt: number; this.enterOuterAlt(_localctx, 1); { - this.state = 585; - this.match(esql_parser.OPENING_BRACKET); - this.state = 586; - this.query(0); - this.state = 587; - this.match(esql_parser.CLOSING_BRACKET); + this.state = 459; + this.match(esql_parser.ENRICH); + this.state = 460; + _localctx._policyName = this.sourceIdentifier(); + this.state = 463; + this._errHandler.sync(this); + switch ( this.interpreter.adaptivePredict(this._input, 46, this._ctx) ) { + case 1: + { + this.state = 461; + this.match(esql_parser.ON); + this.state = 462; + _localctx._matchField = this.sourceIdentifier(); + } + break; + } + this.state = 474; + this._errHandler.sync(this); + switch ( this.interpreter.adaptivePredict(this._input, 48, this._ctx) ) { + case 1: + { + this.state = 465; + this.match(esql_parser.WITH); + this.state = 466; + this.enrichWithClause(); + this.state = 471; + this._errHandler.sync(this); + _alt = this.interpreter.adaptivePredict(this._input, 47, this._ctx); + while (_alt !== 2 && _alt !== ATN.INVALID_ALT_NUMBER) { + if (_alt === 1) { + { + { + this.state = 467; + this.match(esql_parser.COMMA); + this.state = 468; + this.enrichWithClause(); + } + } + } + this.state = 473; + this._errHandler.sync(this); + _alt = this.interpreter.adaptivePredict(this._input, 47, this._ctx); + } + } + break; + } } } catch (re) { @@ -3132,33 +2572,27 @@ export class esql_parser extends Parser { return _localctx; } // @RuleVersion(0) - public showCommand(): ShowCommandContext { - let _localctx: ShowCommandContext = new ShowCommandContext(this._ctx, this.state); - this.enterRule(_localctx, 118, esql_parser.RULE_showCommand); + public enrichWithClause(): EnrichWithClauseContext { + let _localctx: EnrichWithClauseContext = new EnrichWithClauseContext(this._ctx, this.state); + this.enterRule(_localctx, 86, esql_parser.RULE_enrichWithClause); try { - this.state = 593; + this.enterOuterAlt(_localctx, 1); + { + this.state = 479; this._errHandler.sync(this); - switch ( this.interpreter.adaptivePredict(this._input, 57, this._ctx) ) { + switch ( this.interpreter.adaptivePredict(this._input, 49, this._ctx) ) { case 1: - this.enterOuterAlt(_localctx, 1); - { - this.state = 589; - this.match(esql_parser.SHOW); - this.state = 590; - this.match(esql_parser.INFO); - } - break; - - case 2: - this.enterOuterAlt(_localctx, 2); { - this.state = 591; - this.match(esql_parser.SHOW); - this.state = 592; - this.match(esql_parser.FUNCTIONS); + this.state = 476; + _localctx._newName = this.sourceIdentifier(); + this.state = 477; + this.match(esql_parser.ASSIGN); } break; } + this.state = 481; + _localctx._enrichField = this.sourceIdentifier(); + } } catch (re) { if (re instanceof RecognitionException) { @@ -3180,13 +2614,10 @@ export class esql_parser extends Parser { case 1: return this.query_sempred(_localctx as QueryContext, predIndex); - case 8: - return this.whereBooleanExpression_sempred(_localctx as WhereBooleanExpressionContext, predIndex); - - case 9: + case 5: return this.booleanExpression_sempred(_localctx as BooleanExpressionContext, predIndex); - case 16: + case 8: return this.operatorExpression_sempred(_localctx as OperatorExpressionContext, predIndex); } return true; @@ -3198,17 +2629,17 @@ export class esql_parser extends Parser { } return true; } - private whereBooleanExpression_sempred(_localctx: WhereBooleanExpressionContext, predIndex: number): boolean { + private booleanExpression_sempred(_localctx: BooleanExpressionContext, predIndex: number): boolean { switch (predIndex) { case 1: - return this.precpred(this._ctx, 5); + return this.precpred(this._ctx, 4); case 2: - return this.precpred(this._ctx, 4); + return this.precpred(this._ctx, 3); } return true; } - private booleanExpression_sempred(_localctx: BooleanExpressionContext, predIndex: number): boolean { + private operatorExpression_sempred(_localctx: OperatorExpressionContext, predIndex: number): boolean { switch (predIndex) { case 3: return this.precpred(this._ctx, 2); @@ -3218,20 +2649,9 @@ export class esql_parser extends Parser { } return true; } - private operatorExpression_sempred(_localctx: OperatorExpressionContext, predIndex: number): boolean { - switch (predIndex) { - case 5: - return this.precpred(this._ctx, 2); - - case 6: - return this.precpred(this._ctx, 1); - } - return true; - } - private static readonly _serializedATNSegments: number = 2; - private static readonly _serializedATNSegment0: string = - "\x03\uC91D\uCABA\u058D\uAFBA\u4F53\u0607\uEA8B\uC241\x03S\u0256\x04\x02" + + public static readonly _serializedATN: string = + "\x03\uC91D\uCABA\u058D\uAFBA\u4F53\u0607\uEA8B\uC241\x03N\u01E6\x04\x02" + "\t\x02\x04\x03\t\x03\x04\x04\t\x04\x04\x05\t\x05\x04\x06\t\x06\x04\x07" + "\t\x07\x04\b\t\b\x04\t\t\t\x04\n\t\n\x04\v\t\v\x04\f\t\f\x04\r\t\r\x04" + "\x0E\t\x0E\x04\x0F\t\x0F\x04\x10\t\x10\x04\x11\t\x11\x04\x12\t\x12\x04" + @@ -3239,293 +2659,233 @@ export class esql_parser extends Parser { "\x18\t\x18\x04\x19\t\x19\x04\x1A\t\x1A\x04\x1B\t\x1B\x04\x1C\t\x1C\x04" + "\x1D\t\x1D\x04\x1E\t\x1E\x04\x1F\t\x1F\x04 \t \x04!\t!\x04\"\t\"\x04#" + "\t#\x04$\t$\x04%\t%\x04&\t&\x04\'\t\'\x04(\t(\x04)\t)\x04*\t*\x04+\t+" + - "\x04,\t,\x04-\t-\x04.\t.\x04/\t/\x040\t0\x041\t1\x042\t2\x043\t3\x044" + - "\t4\x045\t5\x046\t6\x047\t7\x048\t8\x049\t9\x04:\t:\x04;\t;\x04<\t<\x04" + - "=\t=\x03\x02\x03\x02\x03\x02\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03\x03" + - "\x03\x07\x03\x84\n\x03\f\x03\x0E\x03\x87\v\x03\x03\x04\x03\x04\x03\x04" + - "\x03\x04\x05\x04\x8D\n\x04\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03" + - "\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x05\x05\x9C" + - "\n\x05\x03\x06\x03\x06\x03\x06\x03\x06\x05\x06\xA2\n\x06\x03\x06\x03\x06" + - "\x03\x06\x03\x06\x07\x06\xA8\n\x06\f\x06\x0E\x06\xAB\v\x06\x05\x06\xAD" + - "\n\x06\x03\x07\x03\x07\x03\x07\x05\x07\xB2\n\x07\x03\x07\x03\x07\x03\b" + - "\x03\b\x03\b\x03\t\x03\t\x03\t\x03\n\x03\n\x03\n\x03\n\x03\n\x03\n\x03" + - "\n\x05\n\xC3\n\n\x03\n\x03\n\x03\n\x03\n\x03\n\x07\n\xCA\n\n\f\n\x0E\n" + - "\xCD\v\n\x03\n\x03\n\x03\n\x05\n\xD2\n\n\x03\n\x03\n\x03\n\x03\n\x03\n" + - "\x07\n\xD9\n\n\f\n\x0E\n\xDC\v\n\x05\n\xDE\n\n\x03\n\x03\n\x03\n\x03\n" + - "\x03\n\x05\n\xE5\n\n\x03\n\x03\n\x05\n\xE9\n\n\x03\n\x03\n\x03\n\x03\n" + - "\x03\n\x03\n\x07\n\xF1\n\n\f\n\x0E\n\xF4\v\n\x03\v\x03\v\x03\v\x03\v\x05" + - "\v\xFA\n\v\x03\v\x03\v\x03\v\x03\v\x03\v\x03\v\x07\v\u0102\n\v\f\v\x0E" + - "\v\u0105\v\v\x03\f\x03\f\x05\f\u0109\n\f\x03\f\x03\f\x03\f\x03\f\x03\f" + - "\x05\f\u0110\n\f\x03\f\x03\f\x03\f\x05\f\u0115\n\f\x03\r\x03\r\x05\r\u0119" + - "\n\r\x03\x0E\x03\x0E\x03\x0E\x03\x0E\x03\x0F\x03\x0F\x03\x0F\x03\x0F\x03" + - "\x0F\x07\x0F\u0124\n\x0F\f\x0F\x0E\x0F\u0127\v\x0F\x05\x0F\u0129\n\x0F" + - "\x03\x0F\x03\x0F\x03\x10\x03\x10\x03\x10\x03\x10\x03\x10\x07\x10\u0132" + - "\n\x10\f\x10\x0E\x10\u0135\v\x10\x05\x10\u0137\n\x10\x03\x10\x03\x10\x03" + - "\x11\x03\x11\x03\x11\x03\x12\x03\x12\x03\x12\x03\x12\x03\x12\x03\x12\x05" + - "\x12\u0144\n\x12\x03\x12\x03\x12\x03\x12\x03\x12\x03\x12\x03\x12\x07\x12" + - "\u014C\n\x12\f\x12\x0E\x12\u014F\v\x12\x03\x13\x03\x13\x03\x13\x03\x13" + - "\x03\x13\x03\x13\x03\x13\x03\x13\x03\x13\x03\x13\x03\x13\x03\x13\x07\x13" + - "\u015D\n\x13\f\x13\x0E\x13\u0160\v\x13\x05\x13\u0162\n\x13\x03\x13\x03" + - "\x13\x05\x13\u0166\n\x13\x03\x14\x03\x14\x03\x14\x03\x15\x03\x15\x03\x15" + - "\x07\x15\u016E\n\x15\f\x15\x0E\x15\u0171\v\x15\x03\x16\x03\x16\x03\x16" + - "\x03\x16\x03\x16\x05\x16\u0178\n\x16\x03\x17\x03\x17\x03\x18\x03\x18\x03" + - "\x19\x03\x19\x03\x19\x03\x19\x07\x19\u0182\n\x19\f\x19\x0E\x19\u0185\v" + - "\x19\x03\x19\x05\x19\u0188\n\x19\x03\x1A\x03\x1A\x03\x1A\x03\x1A\x03\x1A" + - "\x07\x1A\u018F\n\x1A\f\x1A\x0E\x1A\u0192\v\x1A\x03\x1A\x03\x1A\x03\x1B" + - "\x03\x1B\x03\x1B\x03\x1C\x03\x1C\x05\x1C\u019B\n\x1C\x03\x1C\x03\x1C\x05" + - "\x1C\u019F\n\x1C\x03\x1D\x03\x1D\x03\x1E\x03\x1E\x03\x1F\x03\x1F\x03\x1F" + - "\x05\x1F\u01A8\n\x1F\x03 \x03 \x03 \x03 \x03 \x03 \x05 \u01B0\n \x03!" + - "\x03!\x03!\x07!\u01B5\n!\f!\x0E!\u01B8\v!\x03\"\x03\"\x03\"\x07\"\u01BD" + - "\n\"\f\"\x0E\"\u01C0\v\"\x03#\x03#\x03$\x03$\x03%\x03%\x03&\x03&\x03&" + - "\x03&\x03&\x03&\x03&\x03&\x07&\u01D0\n&\f&\x0E&\u01D3\v&\x03&\x03&\x03" + - "&\x03&\x03&\x03&\x07&\u01DB\n&\f&\x0E&\u01DE\v&\x03&\x03&\x03&\x03&\x03" + - "&\x03&\x07&\u01E6\n&\f&\x0E&\u01E9\v&\x03&\x03&\x05&\u01ED\n&\x03\'\x03" + - "\'\x05\'\u01F1\n\'\x03(\x03(\x03(\x03)\x03)\x03)\x03)\x07)\u01FA\n)\f" + - ")\x0E)\u01FD\v)\x03*\x03*\x05*\u0201\n*\x03*\x03*\x05*\u0205\n*\x03+\x03" + - "+\x03+\x03,\x03,\x03,\x03-\x03-\x03-\x03.\x03.\x03.\x07.\u0213\n.\f.\x0E" + - ".\u0216\v.\x03/\x03/\x03/\x03/\x07/\u021C\n/\f/\x0E/\u021F\v/\x030\x03" + - "0\x030\x030\x031\x031\x031\x031\x051\u0229\n1\x032\x032\x032\x032\x03" + - "3\x033\x033\x073\u0232\n3\f3\x0E3\u0235\v3\x034\x034\x034\x034\x035\x03" + - "5\x036\x036\x056\u023F\n6\x037\x037\x038\x038\x039\x039\x03:\x03:\x03" + - ";\x03;\x03;\x03<\x03<\x03<\x03<\x03=\x03=\x03=\x03=\x05=\u0254\n=\x03" + - "=\x02\x02\x06\x04\x12\x14\">\x02\x02\x04\x02\x06\x02\b\x02\n\x02\f\x02" + - "\x0E\x02\x10\x02\x12\x02\x14\x02\x16\x02\x18\x02\x1A\x02\x1C\x02\x1E\x02" + - " \x02\"\x02$\x02&\x02(\x02*\x02,\x02.\x020\x022\x024\x026\x028\x02:\x02" + - "<\x02>\x02@\x02B\x02D\x02F\x02H\x02J\x02L\x02N\x02P\x02R\x02T\x02V\x02" + - "X\x02Z\x02\\\x02^\x02`\x02b\x02d\x02f\x02h\x02j\x02l\x02n\x02p\x02r\x02" + - "t\x02v\x02x\x02\x02\x07\x03\x0256\x03\x0279\x03\x02NO\x03\x02GH\x04\x02" + - "77AB\x02\u0273\x02z\x03\x02\x02\x02\x04}\x03\x02\x02\x02\x06\x8C\x03\x02" + - "\x02\x02\b\x9B\x03\x02\x02\x02\n\x9D\x03\x02\x02\x02\f\xB1\x03\x02\x02" + - "\x02\x0E\xB5\x03\x02\x02\x02\x10\xB8\x03\x02\x02\x02\x12\xE8\x03\x02\x02" + - "\x02\x14\xF9\x03\x02\x02\x02\x16\u0114\x03\x02\x02\x02\x18\u0118\x03\x02" + - "\x02\x02\x1A\u011A\x03\x02\x02\x02\x1C\u011E\x03\x02\x02\x02\x1E\u012C" + - "\x03\x02\x02\x02 \u013A\x03\x02\x02\x02\"\u0143\x03\x02\x02\x02$\u0165" + - "\x03\x02\x02\x02&\u0167\x03\x02\x02\x02(\u016A\x03\x02\x02\x02*\u0177" + - "\x03\x02\x02\x02,\u0179\x03\x02\x02\x02.\u017B\x03\x02\x02\x020\u017D" + - "\x03\x02\x02\x022\u0189\x03\x02\x02\x024\u0195\x03\x02\x02\x026\u0198" + - "\x03\x02\x02\x028\u01A0\x03\x02\x02\x02:\u01A2\x03\x02\x02\x02<\u01A7" + - "\x03\x02\x02\x02>\u01AF\x03\x02\x02\x02@\u01B1\x03\x02\x02\x02B\u01B9" + - "\x03\x02\x02\x02D\u01C1\x03\x02\x02\x02F\u01C3\x03\x02\x02\x02H\u01C5" + - "\x03\x02\x02\x02J\u01EC\x03\x02\x02\x02L\u01F0\x03\x02\x02\x02N\u01F2" + - "\x03\x02\x02\x02P\u01F5\x03\x02\x02\x02R\u01FE\x03\x02\x02\x02T\u0206" + - "\x03\x02\x02\x02V\u0209\x03\x02\x02\x02X\u020C\x03\x02\x02\x02Z\u020F" + - "\x03\x02\x02\x02\\\u0217\x03\x02\x02\x02^\u0220\x03\x02\x02\x02`\u0224" + - "\x03\x02\x02\x02b\u022A\x03\x02\x02\x02d\u022E\x03\x02\x02\x02f\u0236" + - "\x03\x02\x02\x02h\u023A\x03\x02\x02\x02j\u023E\x03\x02\x02\x02l\u0240" + - "\x03\x02\x02\x02n\u0242\x03\x02\x02\x02p\u0244\x03\x02\x02\x02r\u0246" + - "\x03\x02\x02\x02t\u0248\x03\x02\x02\x02v\u024B\x03\x02\x02\x02x\u0253" + - "\x03\x02\x02\x02z{\x05\x04\x03\x02{|\x07\x02\x02\x03|\x03\x03\x02\x02" + - "\x02}~\b\x03\x01\x02~\x7F\x05\x06\x04\x02\x7F\x85\x03\x02\x02\x02\x80" + - "\x81\f\x03\x02\x02\x81\x82\x07\x1A\x02\x02\x82\x84\x05\b\x05\x02\x83\x80" + - "\x03\x02\x02\x02\x84\x87\x03\x02\x02\x02\x85\x83\x03\x02\x02\x02\x85\x86" + - "\x03\x02\x02\x02\x86\x05\x03\x02\x02\x02\x87\x85\x03\x02\x02\x02\x88\x8D" + - "\x05t;\x02\x89\x8D\x050\x19\x02\x8A\x8D\x05&\x14\x02\x8B\x8D\x05x=\x02" + - "\x8C\x88\x03\x02\x02\x02\x8C\x89\x03\x02\x02\x02\x8C\x8A\x03\x02\x02\x02" + - "\x8C\x8B\x03\x02\x02\x02\x8D\x07\x03\x02\x02\x02\x8E\x9C\x054\x1B\x02" + - "\x8F\x9C\x05N(\x02\x90\x9C\x05T+\x02\x91\x9C\x05V,\x02\x92\x9C\x05\\/" + - "\x02\x93\x9C\x05X-\x02\x94\x9C\x05`1\x02\x95\x9C\x05b2\x02\x96\x9C\x05" + - "P)\x02\x97\x9C\x056\x1C\x02\x98\x9C\x05\x10\t\x02\x99\x9C\x05\x0E\b\x02" + - "\x9A\x9C\x05\n\x06\x02\x9B\x8E\x03\x02\x02\x02\x9B\x8F\x03\x02\x02\x02" + - "\x9B\x90\x03\x02\x02\x02\x9B\x91\x03\x02\x02\x02\x9B\x92\x03\x02\x02\x02" + - "\x9B\x93\x03\x02\x02\x02\x9B\x94\x03\x02\x02\x02\x9B\x95\x03\x02\x02\x02" + - "\x9B\x96\x03\x02\x02\x02\x9B\x97\x03\x02\x02\x02\x9B\x98\x03\x02\x02\x02" + - "\x9B\x99\x03\x02\x02\x02\x9B\x9A\x03\x02\x02\x02\x9C\t\x03\x02\x02\x02" + - "\x9D\x9E\x07\x12\x02\x02\x9E\xA1\x05:\x1E\x02\x9F\xA0\x07L\x02\x02\xA0" + - "\xA2\x05,\x17\x02\xA1\x9F\x03\x02\x02\x02\xA1\xA2\x03\x02\x02\x02\xA2" + - "\xAC\x03\x02\x02\x02\xA3\xA4\x07M\x02\x02\xA4\xA9\x05\f\x07\x02\xA5\xA6" + - "\x07\"\x02\x02\xA6\xA8\x05\f\x07\x02\xA7\xA5\x03\x02\x02\x02\xA8\xAB\x03" + - "\x02\x02\x02\xA9\xA7\x03\x02\x02\x02\xA9\xAA\x03\x02\x02\x02\xAA\xAD\x03" + - "\x02\x02\x02\xAB\xA9\x03\x02\x02\x02\xAC\xA3\x03\x02\x02\x02\xAC\xAD\x03" + - "\x02\x02\x02\xAD\v\x03\x02\x02\x02\xAE\xAF\x05,\x17\x02\xAF\xB0\x07!\x02" + - "\x02\xB0\xB2\x03\x02\x02\x02\xB1\xAE\x03\x02\x02\x02\xB1\xB2\x03\x02\x02" + - "\x02\xB2\xB3\x03\x02\x02\x02\xB3\xB4\x05,\x17\x02\xB4\r\x03\x02\x02\x02" + - "\xB5\xB6\x07\f\x02\x02\xB6\xB7\x05B\"\x02\xB7\x0F\x03\x02\x02\x02\xB8" + - "\xB9\x07\n\x02\x02\xB9\xBA\x05\x12\n\x02\xBA\x11\x03\x02\x02\x02\xBB\xBC" + - "\b\n\x01\x02\xBC\xBD\x07\'\x02\x02\xBD\xE9\x05\x12\n\n\xBE\xE9\x05\x18" + - "\r\x02\xBF\xE9\x05\x16\f\x02\xC0\xC2\x05\x18\r\x02\xC1\xC3\x07\'\x02\x02" + - "\xC2\xC1\x03\x02\x02\x02\xC2\xC3\x03\x02\x02\x02\xC3\xC4\x03\x02\x02\x02" + - "\xC4\xC5\x07*\x02\x02\xC5\xC6\x07$\x02\x02\xC6\xCB\x05\x18\r\x02\xC7\xC8" + - "\x07\"\x02\x02\xC8\xCA\x05\x18\r\x02\xC9\xC7\x03\x02\x02\x02\xCA\xCD\x03" + - "\x02\x02\x02\xCB\xC9\x03\x02\x02\x02\xCB\xCC\x03\x02\x02\x02\xCC\xCE\x03" + - "\x02\x02\x02\xCD\xCB\x03\x02\x02\x02\xCE\xCF\x07/\x02\x02\xCF\xE9\x03" + - "\x02\x02\x02\xD0\xD2\x07\'\x02\x02\xD1\xD0\x03\x02\x02\x02\xD1\xD2\x03" + - "\x02\x02\x02\xD2\xD3\x03\x02\x02\x02\xD3\xD4\x07@\x02\x02\xD4\xD5\x07" + - "$\x02\x02\xD5\xDD\x05@!\x02\xD6\xD7\x07\"\x02\x02\xD7\xD9\x05<\x1F\x02" + - "\xD8\xD6\x03\x02\x02\x02\xD9\xDC\x03\x02\x02\x02\xDA\xD8\x03\x02\x02\x02" + - "\xDA\xDB\x03\x02\x02\x02\xDB\xDE\x03\x02\x02\x02\xDC\xDA\x03\x02\x02\x02" + - "\xDD\xDA\x03\x02\x02\x02\xDD\xDE\x03\x02\x02\x02\xDE\xDF\x03\x02\x02\x02" + - "\xDF\xE0\x07/\x02\x02\xE0\xE9\x03\x02\x02\x02\xE1\xE2\x05\x18\r\x02\xE2" + - "\xE4\x07+\x02\x02\xE3\xE5\x07\'\x02\x02\xE4\xE3\x03\x02\x02\x02\xE4\xE5" + - "\x03\x02\x02\x02\xE5\xE6\x03\x02\x02\x02\xE6\xE7\x07-\x02\x02\xE7\xE9" + - "\x03\x02\x02\x02\xE8\xBB\x03\x02\x02\x02\xE8\xBE\x03\x02\x02\x02\xE8\xBF" + - "\x03\x02\x02\x02\xE8\xC0\x03\x02\x02\x02\xE8\xD1\x03\x02\x02\x02\xE8\xE1" + - "\x03\x02\x02\x02\xE9\xF2\x03\x02\x02\x02\xEA\xEB\f\x07\x02\x02\xEB\xEC" + - "\x07 \x02\x02\xEC\xF1\x05\x12\n\b\xED\xEE\f\x06\x02\x02\xEE\xEF\x07.\x02" + - "\x02\xEF\xF1\x05\x12\n\x07\xF0\xEA\x03\x02\x02\x02\xF0\xED\x03\x02\x02" + - "\x02\xF1\xF4\x03\x02\x02\x02\xF2\xF0\x03\x02\x02\x02\xF2\xF3\x03\x02\x02" + - "\x02\xF3\x13\x03\x02\x02\x02\xF4\xF2\x03\x02\x02\x02\xF5\xF6\b\v\x01\x02" + - "\xF6\xF7\x07\'\x02\x02\xF7\xFA\x05\x14\v\x06\xF8\xFA\x05\x18\r\x02\xF9" + - "\xF5\x03\x02\x02\x02\xF9\xF8\x03\x02\x02\x02\xFA\u0103\x03\x02\x02\x02" + - "\xFB\xFC\f\x04\x02\x02\xFC\xFD\x07 \x02\x02\xFD\u0102\x05\x14\v\x05\xFE" + - "\xFF\f\x03\x02\x02\xFF\u0100\x07.\x02\x02\u0100\u0102\x05\x14\v\x04\u0101" + - "\xFB\x03\x02\x02\x02\u0101\xFE\x03\x02\x02\x02\u0102\u0105\x03\x02\x02" + - "\x02\u0103\u0101\x03\x02\x02\x02\u0103\u0104\x03\x02\x02\x02\u0104\x15" + - "\x03\x02\x02\x02\u0105\u0103\x03\x02\x02\x02\u0106\u0108\x05\x18\r\x02" + - "\u0107\u0109\x07\'\x02\x02\u0108\u0107\x03\x02\x02\x02\u0108\u0109\x03" + - "\x02\x02\x02\u0109\u010A\x03\x02\x02\x02\u010A\u010B\x07(\x02\x02\u010B" + - "\u010C\x05p9\x02\u010C\u0115\x03\x02\x02\x02\u010D\u010F\x05\x18\r\x02" + - "\u010E\u0110\x07\'\x02\x02\u010F\u010E\x03\x02\x02\x02\u010F\u0110\x03" + - "\x02\x02\x02\u0110\u0111\x03\x02\x02\x02\u0111\u0112\x07)\x02\x02\u0112" + - "\u0113\x05p9\x02\u0113\u0115\x03\x02\x02\x02\u0114\u0106\x03\x02\x02\x02" + - "\u0114\u010D\x03\x02\x02\x02\u0115\x17\x03\x02\x02\x02\u0116\u0119\x05" + - "\"\x12\x02\u0117\u0119\x05\x1A\x0E\x02\u0118\u0116\x03\x02\x02\x02\u0118" + - "\u0117\x03\x02\x02\x02\u0119\x19\x03\x02\x02\x02\u011A\u011B\x05\"\x12" + - "\x02\u011B\u011C\x05r:\x02\u011C\u011D\x05\"\x12\x02\u011D\x1B\x03\x02" + - "\x02\x02\u011E\u011F\x05H%\x02\u011F\u0128\x07$\x02\x02\u0120\u0125\x05" + - "<\x1F\x02\u0121\u0122\x07\"\x02\x02\u0122\u0124\x05<\x1F\x02\u0123\u0121" + - "\x03\x02\x02\x02\u0124\u0127\x03\x02\x02\x02\u0125\u0123\x03\x02\x02\x02" + - "\u0125\u0126\x03\x02\x02\x02\u0126\u0129\x03\x02\x02\x02\u0127\u0125\x03" + - "\x02\x02\x02\u0128\u0120\x03\x02\x02\x02\u0128\u0129\x03\x02\x02\x02\u0129" + - "\u012A\x03\x02\x02\x02\u012A\u012B\x07/\x02\x02\u012B\x1D\x03\x02\x02" + - "\x02\u012C\u012D\x05F$\x02\u012D\u0136\x07$\x02\x02\u012E\u0133\x05> " + - "\x02\u012F\u0130\x07\"\x02\x02\u0130\u0132\x05> \x02\u0131\u012F\x03\x02" + - "\x02\x02\u0132\u0135\x03\x02\x02\x02\u0133\u0131\x03\x02\x02\x02\u0133" + - "\u0134\x03\x02\x02\x02\u0134\u0137\x03\x02\x02\x02\u0135\u0133\x03\x02" + - "\x02\x02\u0136\u012E\x03\x02\x02\x02\u0136\u0137\x03\x02\x02\x02\u0137" + - "\u0138\x03\x02\x02\x02\u0138\u0139\x07/\x02\x02\u0139\x1F\x03\x02\x02" + - "\x02\u013A\u013B\x05j6\x02\u013B\u013C\x07\x1F\x02\x02\u013C!\x03\x02" + - "\x02\x02\u013D\u013E\b\x12\x01\x02\u013E\u0144\x05$\x13\x02\u013F\u0144" + - "\x05\x1C\x0F\x02\u0140\u0144\x05\x1E\x10\x02\u0141\u0142\t\x02\x02\x02" + - "\u0142\u0144\x05\"\x12\x05\u0143\u013D\x03\x02\x02\x02\u0143\u013F\x03" + - "\x02\x02\x02\u0143\u0140\x03\x02\x02\x02\u0143\u0141\x03\x02\x02\x02\u0144" + - "\u014D\x03\x02\x02\x02\u0145\u0146\f\x04\x02\x02\u0146\u0147\t\x03\x02" + - "\x02\u0147\u014C\x05\"\x12\x05\u0148\u0149\f\x03\x02\x02\u0149\u014A\t" + - "\x02\x02\x02\u014A\u014C\x05\"\x12\x04\u014B\u0145\x03\x02\x02\x02\u014B" + - "\u0148\x03\x02\x02\x02\u014C\u014F\x03\x02\x02\x02\u014D\u014B\x03\x02" + - "\x02\x02\u014D\u014E\x03\x02\x02\x02\u014E#\x03\x02\x02\x02\u014F\u014D" + - "\x03\x02\x02\x02\u0150\u0166\x05J&\x02\u0151\u0166\x05@!\x02\u0152\u0166" + - "\x05 \x11\x02\u0153\u0154\x07$\x02\x02\u0154\u0155\x05\x14\v\x02\u0155" + - "\u0156\x07/\x02\x02\u0156\u0166\x03\x02\x02\x02\u0157\u0158\x05D#\x02" + - "\u0158\u0161\x07$\x02\x02\u0159\u015E\x05\x14\v\x02\u015A\u015B\x07\"" + - "\x02\x02\u015B\u015D\x05\x14\v\x02\u015C\u015A\x03\x02\x02\x02\u015D\u0160" + - "\x03\x02\x02\x02\u015E\u015C\x03\x02\x02\x02\u015E\u015F\x03\x02\x02\x02" + - "\u015F\u0162\x03\x02\x02\x02\u0160\u015E\x03\x02\x02\x02\u0161\u0159\x03" + - "\x02\x02\x02\u0161\u0162\x03\x02\x02\x02\u0162\u0163\x03\x02\x02\x02\u0163" + - "\u0164\x07/\x02\x02\u0164\u0166\x03\x02\x02\x02\u0165\u0150\x03\x02\x02" + - "\x02\u0165\u0151\x03\x02\x02\x02\u0165\u0152\x03\x02\x02\x02\u0165\u0153" + - "\x03\x02\x02\x02\u0165\u0157\x03\x02\x02\x02\u0166%\x03\x02\x02\x02\u0167" + - "\u0168\x07\b\x02\x02\u0168\u0169\x05(\x15\x02\u0169\'\x03\x02\x02\x02" + - "\u016A\u016F\x05*\x16\x02\u016B\u016C\x07\"\x02\x02\u016C\u016E\x05*\x16" + - "\x02\u016D\u016B\x03\x02\x02\x02\u016E\u0171\x03\x02\x02\x02\u016F\u016D" + - "\x03\x02\x02\x02\u016F\u0170\x03\x02\x02\x02\u0170)\x03\x02\x02\x02\u0171" + - "\u016F\x03\x02\x02\x02\u0172\u0178\x05\x14\v\x02\u0173\u0174\x05.\x18" + - "\x02\u0174\u0175\x07!\x02\x02\u0175\u0176\x05\x14\v\x02\u0176\u0178\x03" + - "\x02\x02\x02\u0177\u0172\x03\x02\x02\x02\u0177\u0173\x03\x02\x02\x02\u0178" + - "+\x03\x02\x02\x02\u0179\u017A\t\x04\x02\x02\u017A-\x03\x02\x02\x02\u017B" + - "\u017C\x05D#\x02\u017C/\x03\x02\x02\x02\u017D\u017E\x07\x07\x02\x02\u017E" + - "\u0183\x058\x1D\x02\u017F\u0180\x07\"\x02\x02\u0180\u0182\x058\x1D\x02" + - "\u0181\u017F\x03\x02\x02\x02\u0182\u0185\x03\x02\x02\x02\u0183\u0181\x03" + - "\x02\x02\x02\u0183\u0184\x03\x02\x02\x02\u0184\u0187\x03\x02\x02\x02\u0185" + - "\u0183\x03\x02\x02\x02\u0186\u0188\x052\x1A\x02\u0187\u0186\x03\x02\x02" + - "\x02\u0187\u0188\x03\x02\x02\x02\u01881\x03\x02\x02\x02\u0189\u018A\x07" + - "%\x02\x02\u018A\u018B\x07F\x02\x02\u018B\u0190\x058\x1D\x02\u018C\u018D" + - "\x07\"\x02\x02\u018D\u018F\x058\x1D\x02\u018E\u018C\x03\x02\x02\x02\u018F" + - "\u0192\x03\x02\x02\x02\u0190\u018E\x03\x02\x02\x02\u0190\u0191\x03\x02" + - "\x02\x02\u0191\u0193\x03\x02\x02\x02\u0192\u0190\x03\x02\x02\x02\u0193" + - "\u0194\x07&\x02\x02\u01943\x03\x02\x02\x02\u0195\u0196\x07\x05\x02\x02" + - "\u0196\u0197\x05(\x15\x02\u01975\x03\x02\x02\x02\u0198\u019A\x07\t\x02" + - "\x02\u0199\u019B\x05(\x15\x02\u019A\u0199\x03\x02\x02\x02\u019A\u019B" + - "\x03\x02\x02\x02\u019B\u019E\x03\x02\x02\x02\u019C\u019D\x07\x1E\x02\x02" + - "\u019D\u019F\x05B\"\x02\u019E\u019C\x03\x02\x02\x02\u019E\u019F\x03\x02" + - "\x02\x02\u019F7\x03\x02\x02\x02\u01A0\u01A1\t\x05\x02\x02\u01A19\x03\x02" + - "\x02\x02\u01A2\u01A3\t\x04\x02\x02\u01A3;\x03\x02\x02\x02\u01A4\u01A8" + - "\x05@!\x02\u01A5\u01A8\x05p9\x02\u01A6\u01A8\x05j6\x02\u01A7\u01A4\x03" + - "\x02\x02\x02\u01A7\u01A5\x03\x02\x02\x02\u01A7\u01A6\x03\x02\x02\x02\u01A8" + - "=\x03\x02\x02\x02\u01A9\u01B0\x05@!\x02\u01AA\u01B0\x05p9\x02\u01AB\u01B0" + - "\x05j6\x02\u01AC\u01B0\x05\"\x12\x02\u01AD\u01B0\x05 \x11\x02\u01AE\u01B0" + - "\x05\x1A\x0E\x02\u01AF\u01A9\x03\x02\x02\x02\u01AF\u01AA\x03\x02\x02\x02" + - "\u01AF\u01AB\x03\x02\x02\x02\u01AF\u01AC\x03\x02\x02\x02\u01AF\u01AD\x03" + - "\x02\x02\x02\u01AF\u01AE\x03\x02\x02\x02\u01B0?\x03\x02\x02\x02\u01B1" + - "\u01B6\x05D#\x02\u01B2\u01B3\x07#\x02\x02\u01B3\u01B5\x05D#\x02\u01B4" + - "\u01B2\x03\x02\x02\x02\u01B5\u01B8\x03\x02\x02\x02\u01B6\u01B4\x03\x02" + - "\x02\x02\u01B6\u01B7\x03\x02\x02\x02\u01B7A\x03\x02\x02\x02\u01B8\u01B6" + - "\x03\x02\x02\x02\u01B9\u01BE\x05@!\x02\u01BA\u01BB\x07\"\x02\x02\u01BB" + - "\u01BD\x05@!\x02\u01BC\u01BA\x03\x02\x02\x02\u01BD\u01C0\x03\x02\x02\x02" + - "\u01BE\u01BC\x03\x02\x02\x02\u01BE\u01BF\x03\x02\x02\x02\u01BFC\x03\x02" + - "\x02\x02\u01C0\u01BE\x03\x02\x02\x02\u01C1\u01C2\t\x06\x02\x02\u01C2E" + - "\x03\x02\x02\x02\u01C3\u01C4\x07>\x02\x02\u01C4G\x03\x02\x02\x02\u01C5" + - "\u01C6\x07?\x02\x02\u01C6I\x03\x02\x02\x02\u01C7\u01ED\x07-\x02\x02\u01C8" + - "\u01ED\x05L\'\x02\u01C9\u01ED\x05h5\x02\u01CA\u01ED\x05p9\x02\u01CB\u01CC" + - "\x07%\x02\x02\u01CC\u01D1\x05L\'\x02\u01CD\u01CE\x07\"\x02\x02\u01CE\u01D0" + - "\x05L\'\x02\u01CF\u01CD\x03\x02\x02\x02\u01D0\u01D3\x03\x02\x02\x02\u01D1" + - "\u01CF\x03\x02\x02\x02\u01D1\u01D2\x03\x02\x02\x02\u01D2\u01D4\x03\x02" + - "\x02\x02\u01D3\u01D1\x03\x02\x02\x02\u01D4\u01D5\x07&\x02\x02\u01D5\u01ED" + - "\x03\x02\x02\x02\u01D6\u01D7\x07%\x02\x02\u01D7\u01DC\x05h5\x02\u01D8" + - "\u01D9\x07\"\x02\x02\u01D9\u01DB\x05h5\x02\u01DA\u01D8\x03\x02\x02\x02" + - "\u01DB\u01DE\x03\x02\x02\x02\u01DC\u01DA\x03\x02\x02\x02\u01DC\u01DD\x03" + - "\x02\x02\x02\u01DD\u01DF\x03\x02\x02\x02\u01DE\u01DC\x03\x02\x02\x02\u01DF" + - "\u01E0\x07&\x02\x02\u01E0\u01ED\x03\x02\x02\x02\u01E1\u01E2\x07%\x02\x02" + - "\u01E2\u01E7\x05p9\x02\u01E3\u01E4\x07\"\x02\x02\u01E4\u01E6\x05p9\x02" + - "\u01E5\u01E3\x03\x02\x02\x02\u01E6\u01E9\x03\x02\x02\x02\u01E7\u01E5\x03" + - "\x02\x02\x02\u01E7\u01E8\x03\x02\x02\x02\u01E8\u01EA\x03\x02\x02\x02\u01E9" + - "\u01E7\x03\x02\x02\x02\u01EA\u01EB\x07&\x02\x02\u01EB\u01ED\x03\x02\x02" + - "\x02\u01EC\u01C7\x03\x02\x02\x02\u01EC\u01C8\x03\x02\x02\x02\u01EC\u01C9" + - "\x03\x02\x02\x02\u01EC\u01CA\x03\x02\x02\x02\u01EC\u01CB\x03\x02\x02\x02" + - "\u01EC\u01D6\x03\x02\x02\x02\u01EC\u01E1\x03\x02\x02\x02\u01EDK\x03\x02" + - "\x02\x02\u01EE\u01F1\x05l7\x02\u01EF\u01F1\x05n8\x02\u01F0\u01EE\x03\x02" + - "\x02\x02\u01F0\u01EF\x03\x02\x02\x02\u01F1M\x03\x02\x02\x02\u01F2\u01F3" + - "\x07\r\x02\x02\u01F3\u01F4\x07\x1C\x02\x02\u01F4O\x03\x02\x02\x02\u01F5" + - "\u01F6\x07\v\x02\x02\u01F6\u01FB\x05R*\x02\u01F7\u01F8\x07\"\x02\x02\u01F8" + - "\u01FA\x05R*\x02\u01F9\u01F7\x03\x02\x02\x02\u01FA\u01FD\x03\x02\x02\x02" + - "\u01FB\u01F9\x03\x02\x02\x02\u01FB\u01FC\x03\x02\x02\x02\u01FCQ\x03\x02" + - "\x02\x02\u01FD\u01FB\x03\x02\x02\x02\u01FE\u0200\x05\x14\v\x02\u01FF\u0201" + - "\x07;\x02\x02\u0200\u01FF\x03\x02\x02\x02\u0200\u0201\x03\x02\x02\x02" + - "\u0201\u0204\x03\x02\x02\x02\u0202\u0203\x07<\x02\x02\u0203\u0205\x07" + - "=\x02\x02\u0204\u0202\x03\x02\x02\x02\u0204\u0205\x03\x02\x02\x02\u0205" + - "S\x03\x02\x02\x02\u0206\u0207\x07\x0E\x02\x02\u0207\u0208\x05B\"\x02\u0208" + - "U\x03\x02\x02\x02\u0209\u020A\x07\x13\x02\x02\u020A\u020B\x05B\"\x02\u020B" + - "W\x03\x02\x02\x02\u020C\u020D\x07\x0F\x02\x02\u020D\u020E\x05B\"\x02\u020E" + - "Y\x03\x02\x02\x02\u020F\u0214\x05D#\x02\u0210\u0211\x07#\x02\x02\u0211" + - "\u0213\x05D#\x02\u0212\u0210\x03\x02\x02\x02\u0213\u0216\x03\x02\x02\x02" + - "\u0214\u0212\x03\x02\x02\x02\u0214\u0215\x03\x02\x02\x02\u0215[\x03\x02" + - "\x02\x02\u0216\u0214\x03\x02\x02\x02\u0217\u0218\x07\x10\x02\x02\u0218" + - "\u021D\x05^0\x02\u0219\u021A\x07\"\x02\x02\u021A\u021C\x05^0\x02\u021B" + - "\u0219\x03\x02\x02\x02\u021C\u021F\x03\x02\x02\x02\u021D\u021B\x03\x02" + - "\x02\x02\u021D\u021E\x03\x02\x02\x02\u021E]\x03\x02\x02\x02\u021F\u021D" + - "\x03\x02\x02\x02\u0220\u0221\x05@!\x02\u0221\u0222\x07,\x02\x02\u0222" + - "\u0223\x05Z.\x02\u0223_\x03\x02\x02\x02\u0224\u0225\x07\x03\x02\x02\u0225" + - "\u0226\x05B\"\x02\u0226\u0228\x05p9\x02\u0227\u0229\x05d3\x02\u0228\u0227" + - "\x03\x02\x02\x02\u0228\u0229\x03\x02\x02\x02\u0229a\x03\x02\x02\x02\u022A" + - "\u022B\x07\x04\x02\x02\u022B\u022C\x05B\"\x02\u022C\u022D\x05p9\x02\u022D" + - "c\x03\x02\x02\x02\u022E\u0233\x05f4\x02\u022F\u0230\x07\"\x02\x02\u0230" + - "\u0232\x05f4\x02\u0231\u022F\x03\x02\x02\x02\u0232\u0235\x03\x02\x02\x02" + - "\u0233\u0231\x03\x02\x02\x02\u0233\u0234\x03\x02\x02\x02\u0234e\x03\x02" + - "\x02\x02\u0235\u0233\x03\x02\x02\x02\u0236\u0237\x05D#\x02\u0237\u0238" + - "\x07!\x02\x02\u0238"; - private static readonly _serializedATNSegment1: string = - "\u0239\x05J&\x02\u0239g\x03\x02\x02\x02\u023A\u023B\x073\x02\x02\u023B" + - "i\x03\x02\x02\x02\u023C\u023F\x07\x1D\x02\x02\u023D\u023F\x07\x1C\x02" + - "\x02\u023E\u023C\x03\x02\x02\x02\u023E\u023D\x03\x02\x02\x02\u023Fk\x03" + - "\x02\x02\x02\u0240\u0241\x07\x1D\x02\x02\u0241m\x03\x02\x02\x02\u0242" + - "\u0243\x07\x1C\x02\x02\u0243o\x03\x02\x02\x02\u0244\u0245\x07\x1B\x02" + - "\x02\u0245q\x03\x02\x02\x02\u0246\u0247\x074\x02\x02\u0247s\x03\x02\x02" + - "\x02\u0248\u0249\x07\x06\x02\x02\u0249\u024A\x05v<\x02\u024Au\x03\x02" + - "\x02\x02\u024B\u024C\x07%\x02\x02\u024C\u024D\x05\x04\x03\x02\u024D\u024E" + - "\x07&\x02\x02\u024Ew\x03\x02\x02\x02\u024F\u0250\x07\x11\x02\x02\u0250" + - "\u0254\x071\x02\x02\u0251\u0252\x07\x11\x02\x02\u0252\u0254\x072\x02\x02" + - "\u0253\u024F\x03\x02\x02\x02\u0253\u0251\x03\x02\x02\x02\u0254y\x03\x02" + - "\x02\x02<\x85\x8C\x9B\xA1\xA9\xAC\xB1\xC2\xCB\xD1\xDA\xDD\xE4\xE8\xF0" + - "\xF2\xF9\u0101\u0103\u0108\u010F\u0114\u0118\u0125\u0128\u0133\u0136\u0143" + - "\u014B\u014D\u015E\u0161\u0165\u016F\u0177\u0183\u0187\u0190\u019A\u019E" + - "\u01A7\u01AF\u01B6\u01BE\u01D1\u01DC\u01E7\u01EC\u01F0\u01FB\u0200\u0204" + - "\u0214\u021D\u0228\u0233\u023E\u0253"; - public static readonly _serializedATN: string = Utils.join( - [ - esql_parser._serializedATNSegment0, - esql_parser._serializedATNSegment1, - ], - "", - ); + "\x04,\t,\x04-\t-\x03\x02\x03\x02\x03\x02\x03\x03\x03\x03\x03\x03\x03\x03" + + "\x03\x03\x03\x03\x07\x03d\n\x03\f\x03\x0E\x03g\v\x03\x03\x04\x03\x04\x03" + + "\x04\x05\x04l\n\x04\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03" + + "\x05\x03\x05\x03\x05\x03\x05\x03\x05\x03\x05\x05\x05z\n\x05\x03\x06\x03" + + "\x06\x03\x06\x03\x07\x03\x07\x03\x07\x03\x07\x03\x07\x03\x07\x03\x07\x05" + + "\x07\x86\n\x07\x03\x07\x03\x07\x03\x07\x03\x07\x03\x07\x07\x07\x8D\n\x07" + + "\f\x07\x0E\x07\x90\v\x07\x03\x07\x03\x07\x03\x07\x03\x07\x03\x07\x05\x07" + + "\x97\n\x07\x03\x07\x03\x07\x05\x07\x9B\n\x07\x03\x07\x03\x07\x03\x07\x03" + + "\x07\x03\x07\x03\x07\x07\x07\xA3\n\x07\f\x07\x0E\x07\xA6\v\x07\x03\b\x03" + + "\b\x05\b\xAA\n\b\x03\b\x03\b\x03\b\x03\b\x03\b\x05\b\xB1\n\b\x03\b\x03" + + "\b\x03\b\x05\b\xB6\n\b\x03\t\x03\t\x03\t\x03\t\x03\t\x05\t\xBD\n\t\x03" + + "\n\x03\n\x03\n\x03\n\x05\n\xC3\n\n\x03\n\x03\n\x03\n\x03\n\x03\n\x03\n" + + "\x07\n\xCB\n\n\f\n\x0E\n\xCE\v\n\x03\v\x03\v\x03\v\x03\v\x03\v\x03\v\x03" + + "\v\x05\v\xD7\n\v\x03\f\x03\f\x03\f\x03\f\x03\f\x03\f\x07\f\xDF\n\f\f\f" + + "\x0E\f\xE2\v\f\x05\f\xE4\n\f\x03\f\x03\f\x03\r\x03\r\x03\r\x03\x0E\x03" + + "\x0E\x03\x0E\x07\x0E\xEE\n\x0E\f\x0E\x0E\x0E\xF1\v\x0E\x03\x0F\x03\x0F" + + "\x03\x0F\x03\x0F\x03\x0F\x05\x0F\xF8\n\x0F\x03\x10\x03\x10\x03\x10\x03" + + "\x10\x07\x10\xFE\n\x10\f\x10\x0E\x10\u0101\v\x10\x03\x10\x05\x10\u0104" + + "\n\x10\x03\x11\x03\x11\x03\x11\x03\x11\x03\x11\x07\x11\u010B\n\x11\f\x11" + + "\x0E\x11\u010E\v\x11\x03\x11\x03\x11\x03\x12\x03\x12\x03\x12\x03\x13\x03" + + "\x13\x05\x13\u0117\n\x13\x03\x13\x03\x13\x05\x13\u011B\n\x13\x03\x14\x03" + + "\x14\x03\x14\x07\x14\u0120\n\x14\f\x14\x0E\x14\u0123\v\x14\x03\x15\x03" + + "\x15\x03\x16\x03\x16\x03\x16\x07\x16\u012A\n\x16\f\x16\x0E\x16\u012D\v" + + "\x16\x03\x17\x03\x17\x03\x18\x03\x18\x03\x18\x03\x18\x03\x18\x03\x18\x03" + + "\x18\x03\x18\x03\x18\x03\x18\x03\x18\x03\x18\x03\x18\x07\x18\u013E\n\x18" + + "\f\x18\x0E\x18\u0141\v\x18\x03\x18\x03\x18\x03\x18\x03\x18\x03\x18\x03" + + "\x18\x07\x18\u0149\n\x18\f\x18\x0E\x18\u014C\v\x18\x03\x18\x03\x18\x03" + + "\x18\x03\x18\x03\x18\x03\x18\x07\x18\u0154\n\x18\f\x18\x0E\x18\u0157\v" + + "\x18\x03\x18\x03\x18\x05\x18\u015B\n\x18\x03\x19\x03\x19\x03\x19\x03\x1A" + + "\x03\x1A\x03\x1A\x03\x1A\x07\x1A\u0164\n\x1A\f\x1A\x0E\x1A\u0167\v\x1A" + + "\x03\x1B\x03\x1B\x05\x1B\u016B\n\x1B\x03\x1B\x03\x1B\x05\x1B\u016F\n\x1B" + + "\x03\x1C\x03\x1C\x03\x1C\x03\x1C\x07\x1C\u0175\n\x1C\f\x1C\x0E\x1C\u0178" + + "\v\x1C\x03\x1C\x03\x1C\x03\x1C\x03\x1C\x07\x1C\u017E\n\x1C\f\x1C\x0E\x1C" + + "\u0181\v\x1C\x05\x1C\u0183\n\x1C\x03\x1D\x03\x1D\x03\x1D\x03\x1D\x07\x1D" + + "\u0189\n\x1D\f\x1D\x0E\x1D\u018C\v\x1D\x03\x1E\x03\x1E\x03\x1E\x03\x1E" + + "\x07\x1E\u0192\n\x1E\f\x1E\x0E\x1E\u0195\v\x1E\x03\x1F\x03\x1F\x03\x1F" + + "\x03\x1F\x03 \x03 \x03 \x03 \x05 \u019F\n \x03!\x03!\x03!\x03!\x03\"\x03" + + "\"\x03\"\x03#\x03#\x03#\x07#\u01AB\n#\f#\x0E#\u01AE\v#\x03$\x03$\x03$" + + "\x03$\x03%\x03%\x03&\x03&\x05&\u01B8\n&\x03\'\x05\'\u01BB\n\'\x03\'\x03" + + "\'\x03(\x05(\u01C0\n(\x03(\x03(\x03)\x03)\x03*\x03*\x03+\x03+\x03+\x03" + + "+\x05+\u01CC\n+\x03,\x03,\x03,\x03,\x05,\u01D2\n,\x03,\x03,\x03,\x03," + + "\x07,\u01D8\n,\f,\x0E,\u01DB\v,\x05,\u01DD\n,\x03-\x03-\x03-\x05-\u01E2" + + "\n-\x03-\x03-\x03-\x02\x02\x05\x04\f\x12.\x02\x02\x04\x02\x06\x02\b\x02" + + "\n\x02\f\x02\x0E\x02\x10\x02\x12\x02\x14\x02\x16\x02\x18\x02\x1A\x02\x1C" + + "\x02\x1E\x02 \x02\"\x02$\x02&\x02(\x02*\x02,\x02.\x020\x022\x024\x026" + + "\x028\x02:\x02<\x02>\x02@\x02B\x02D\x02F\x02H\x02J\x02L\x02N\x02P\x02" + + "R\x02T\x02V\x02X\x02\x02\n\x03\x02:;\x03\x02<>\x03\x02JK\x03\x02AB\x04" + + "\x02\x1D\x1D \x03\x02#$\x04\x02\"\"00\x03\x0249\x02\u0204\x02Z\x03\x02" + + "\x02\x02\x04]\x03\x02\x02\x02\x06k\x03\x02\x02\x02\by\x03\x02\x02\x02" + + "\n{\x03\x02\x02\x02\f\x9A\x03\x02\x02\x02\x0E\xB5\x03\x02\x02\x02\x10" + + "\xBC\x03\x02\x02\x02\x12\xC2\x03\x02\x02\x02\x14\xD6\x03\x02\x02\x02\x16" + + "\xD8\x03\x02\x02\x02\x18\xE7\x03\x02\x02\x02\x1A\xEA\x03\x02\x02\x02\x1C" + + "\xF7\x03\x02\x02\x02\x1E\xF9\x03\x02\x02\x02 \u0105\x03\x02\x02\x02\"" + + "\u0111\x03\x02\x02\x02$\u0114\x03\x02\x02\x02&\u011C\x03\x02\x02\x02(" + + "\u0124\x03\x02\x02\x02*\u0126\x03\x02\x02\x02,\u012E\x03\x02\x02\x02." + + "\u015A\x03\x02\x02\x020\u015C\x03\x02\x02\x022\u015F\x03\x02\x02\x024" + + "\u0168\x03\x02\x02\x026\u0182\x03\x02\x02\x028\u0184\x03\x02\x02\x02:" + + "\u018D\x03\x02\x02\x02<\u0196\x03\x02\x02\x02>\u019A\x03\x02\x02\x02@" + + "\u01A0\x03\x02\x02\x02B\u01A4\x03\x02\x02\x02D\u01A7\x03\x02\x02\x02F" + + "\u01AF\x03\x02\x02\x02H\u01B3\x03\x02\x02\x02J\u01B7\x03\x02\x02\x02L" + + "\u01BA\x03\x02\x02\x02N\u01BF\x03\x02\x02\x02P\u01C3\x03\x02\x02\x02R" + + "\u01C5\x03\x02\x02\x02T\u01CB\x03\x02\x02\x02V\u01CD\x03\x02\x02\x02X" + + "\u01E1\x03\x02\x02\x02Z[\x05\x04\x03\x02[\\\x07\x02\x02\x03\\\x03\x03" + + "\x02\x02\x02]^\b\x03\x01\x02^_\x05\x06\x04\x02_e\x03\x02\x02\x02`a\f\x03" + + "\x02\x02ab\x07\x17\x02\x02bd\x05\b\x05\x02c`\x03\x02\x02\x02dg\x03\x02" + + "\x02\x02ec\x03\x02\x02\x02ef\x03\x02\x02\x02f\x05\x03\x02\x02\x02ge\x03" + + "\x02\x02\x02hl\x05\x1E\x10\x02il\x05\x18\r\x02jl\x05T+\x02kh\x03\x02\x02" + + "\x02ki\x03\x02\x02\x02kj\x03\x02\x02\x02l\x07\x03\x02\x02\x02mz\x05\"" + + "\x12\x02nz\x050\x19\x02oz\x056\x1C\x02pz\x052\x1A\x02qz\x05$\x13\x02r" + + "z\x05\n\x06\x02sz\x058\x1D\x02tz\x05:\x1E\x02uz\x05> \x02vz\x05@!\x02" + + "wz\x05V,\x02xz\x05B\"\x02ym\x03\x02\x02\x02yn\x03\x02\x02\x02yo\x03\x02" + + "\x02\x02yp\x03\x02\x02\x02yq\x03\x02\x02\x02yr\x03\x02\x02\x02ys\x03\x02" + + "\x02\x02yt\x03\x02\x02\x02yu\x03\x02\x02\x02yv\x03\x02\x02\x02yw\x03\x02" + + "\x02\x02yx\x03\x02\x02\x02z\t\x03\x02\x02\x02{|\x07\x12\x02\x02|}\x05" + + "\f\x07\x02}\v\x03\x02\x02\x02~\x7F\b\x07\x01\x02\x7F\x80\x07)\x02\x02" + + "\x80\x9B\x05\f\x07\t\x81\x9B\x05\x10\t\x02\x82\x9B\x05\x0E\b\x02\x83\x85" + + "\x05\x10\t\x02\x84\x86\x07)\x02\x02\x85\x84\x03\x02\x02\x02\x85\x86\x03" + + "\x02\x02\x02\x86\x87\x03\x02\x02\x02\x87\x88\x07&\x02\x02\x88\x89\x07" + + "%\x02\x02\x89\x8E\x05\x10\t\x02\x8A\x8B\x07\x1F\x02\x02\x8B\x8D\x05\x10" + + "\t\x02\x8C\x8A\x03\x02\x02\x02\x8D\x90\x03\x02\x02\x02\x8E\x8C\x03\x02" + + "\x02\x02\x8E\x8F\x03\x02\x02\x02\x8F\x91\x03\x02\x02\x02\x90\x8E\x03\x02" + + "\x02\x02\x91\x92\x07/\x02\x02\x92\x9B\x03\x02\x02\x02\x93\x94\x05\x10" + + "\t\x02\x94\x96\x07\'\x02\x02\x95\x97\x07)\x02\x02\x96\x95\x03\x02\x02" + + "\x02\x96\x97\x03\x02\x02\x02\x97\x98\x03\x02\x02\x02\x98\x99\x07*\x02" + + "\x02\x99\x9B\x03\x02\x02\x02\x9A~\x03\x02\x02\x02\x9A\x81\x03\x02\x02" + + "\x02\x9A\x82\x03\x02\x02\x02\x9A\x83\x03\x02\x02\x02\x9A\x93\x03\x02\x02" + + "\x02\x9B\xA4\x03\x02\x02\x02\x9C\x9D\f\x06\x02\x02\x9D\x9E\x07\x1C\x02" + + "\x02\x9E\xA3\x05\f\x07\x07\x9F\xA0\f\x05\x02\x02\xA0\xA1\x07,\x02\x02" + + "\xA1\xA3\x05\f\x07\x06\xA2\x9C\x03\x02\x02\x02\xA2\x9F\x03\x02\x02\x02" + + "\xA3\xA6\x03\x02\x02\x02\xA4\xA2\x03\x02\x02\x02\xA4\xA5\x03\x02\x02\x02" + + "\xA5\r\x03\x02\x02\x02\xA6\xA4\x03\x02\x02\x02\xA7\xA9\x05\x10\t\x02\xA8" + + "\xAA\x07)\x02\x02\xA9\xA8\x03\x02\x02\x02\xA9\xAA\x03\x02\x02\x02\xAA" + + "\xAB\x03\x02\x02\x02\xAB\xAC\x07(\x02\x02\xAC\xAD\x05P)\x02\xAD\xB6\x03" + + "\x02\x02\x02\xAE\xB0\x05\x10\t\x02\xAF\xB1\x07)\x02\x02\xB0\xAF\x03\x02" + + "\x02\x02\xB0\xB1\x03\x02\x02\x02\xB1\xB2\x03\x02\x02\x02\xB2\xB3\x07." + + "\x02\x02\xB3\xB4\x05P)\x02\xB4\xB6\x03\x02\x02\x02\xB5\xA7\x03\x02\x02" + + "\x02\xB5\xAE\x03\x02\x02\x02\xB6\x0F\x03\x02\x02\x02\xB7\xBD\x05\x12\n" + + "\x02\xB8\xB9\x05\x12\n\x02\xB9\xBA\x05R*\x02\xBA\xBB\x05\x12\n\x02\xBB" + + "\xBD\x03\x02\x02\x02\xBC\xB7\x03\x02\x02\x02\xBC\xB8\x03\x02\x02\x02\xBD" + + "\x11\x03\x02\x02\x02\xBE\xBF\b\n\x01\x02\xBF\xC3\x05\x14\v\x02\xC0\xC1" + + "\t\x02\x02\x02\xC1\xC3\x05\x12\n\x05\xC2\xBE\x03\x02\x02\x02\xC2\xC0\x03" + + "\x02\x02\x02\xC3\xCC\x03\x02\x02\x02\xC4\xC5\f\x04\x02\x02\xC5\xC6\t\x03" + + "\x02\x02\xC6\xCB\x05\x12\n\x05\xC7\xC8\f\x03\x02\x02\xC8\xC9\t\x02\x02" + + "\x02\xC9\xCB\x05\x12\n\x04\xCA\xC4\x03\x02\x02\x02\xCA\xC7\x03\x02\x02" + + "\x02\xCB\xCE\x03\x02\x02\x02\xCC\xCA\x03\x02\x02\x02\xCC\xCD\x03\x02\x02" + + "\x02\xCD\x13\x03\x02\x02\x02\xCE\xCC\x03\x02\x02\x02\xCF\xD7\x05.\x18" + + "\x02\xD0\xD7\x05*\x16\x02\xD1\xD7\x05\x16\f\x02\xD2\xD3\x07%\x02\x02\xD3" + + "\xD4\x05\f\x07\x02\xD4\xD5\x07/\x02\x02\xD5\xD7\x03\x02\x02\x02\xD6\xCF" + + "\x03\x02\x02\x02\xD6\xD0\x03\x02\x02\x02\xD6\xD1\x03\x02\x02\x02\xD6\xD2" + + "\x03\x02\x02\x02\xD7\x15\x03\x02\x02\x02\xD8\xD9\x05,\x17\x02\xD9\xE3" + + "\x07%\x02\x02\xDA\xE4\x07<\x02\x02\xDB\xE0\x05\f\x07\x02\xDC\xDD\x07\x1F" + + "\x02\x02\xDD\xDF\x05\f\x07\x02\xDE\xDC\x03\x02\x02\x02\xDF\xE2\x03\x02" + + "\x02\x02\xE0\xDE\x03\x02\x02\x02\xE0\xE1\x03\x02\x02\x02\xE1\xE4\x03\x02" + + "\x02\x02\xE2\xE0\x03\x02\x02\x02\xE3\xDA\x03\x02\x02\x02\xE3\xDB\x03\x02" + + "\x02\x02\xE3\xE4\x03\x02\x02\x02\xE4\xE5\x03\x02\x02\x02\xE5\xE6\x07/" + + "\x02\x02\xE6\x17\x03\x02\x02\x02\xE7\xE8\x07\x0E\x02\x02\xE8\xE9\x05\x1A" + + "\x0E\x02\xE9\x19\x03\x02\x02\x02\xEA\xEF\x05\x1C\x0F\x02\xEB\xEC\x07\x1F" + + "\x02\x02\xEC\xEE\x05\x1C\x0F\x02\xED\xEB\x03\x02\x02\x02\xEE\xF1\x03\x02" + + "\x02\x02\xEF\xED\x03\x02\x02\x02\xEF\xF0\x03\x02\x02\x02\xF0\x1B\x03\x02" + + "\x02\x02\xF1\xEF\x03\x02\x02\x02\xF2\xF8\x05\f\x07\x02\xF3\xF4\x05*\x16" + + "\x02\xF4\xF5\x07\x1E\x02\x02\xF5\xF6\x05\f\x07\x02\xF6\xF8\x03\x02\x02" + + "\x02\xF7\xF2\x03\x02\x02\x02\xF7\xF3\x03\x02\x02\x02\xF8\x1D\x03\x02\x02" + + "\x02\xF9\xFA\x07\x07\x02\x02\xFA\xFF\x05(\x15\x02\xFB\xFC\x07\x1F\x02" + + "\x02\xFC\xFE\x05(\x15\x02\xFD\xFB\x03\x02\x02\x02\xFE\u0101\x03\x02\x02" + + "\x02\xFF\xFD\x03\x02\x02\x02\xFF\u0100\x03\x02\x02\x02\u0100\u0103\x03" + + "\x02\x02\x02\u0101\xFF\x03\x02\x02\x02\u0102\u0104\x05 \x11\x02\u0103" + + "\u0102\x03\x02\x02\x02\u0103\u0104\x03\x02\x02\x02\u0104\x1F\x03\x02\x02" + + "\x02\u0105\u0106\x07?\x02\x02\u0106\u0107\x07G\x02\x02\u0107\u010C\x05" + + "(\x15\x02\u0108\u0109\x07\x1F\x02\x02\u0109\u010B\x05(\x15\x02\u010A\u0108" + + "\x03\x02\x02\x02\u010B\u010E\x03\x02\x02\x02\u010C\u010A\x03\x02\x02\x02" + + "\u010C\u010D\x03\x02\x02\x02\u010D\u010F\x03\x02\x02\x02\u010E\u010C\x03" + + "\x02\x02\x02\u010F\u0110\x07@\x02\x02\u0110!\x03\x02\x02\x02\u0111\u0112" + + "\x07\x06\x02\x02\u0112\u0113\x05\x1A\x0E\x02\u0113#\x03\x02\x02\x02\u0114" + + "\u0116\x07\x11\x02\x02\u0115\u0117\x05\x1A\x0E\x02\u0116\u0115\x03\x02" + + "\x02\x02\u0116\u0117\x03\x02\x02\x02\u0117\u011A\x03\x02\x02\x02\u0118" + + "\u0119\x07\x1B\x02\x02\u0119\u011B\x05&\x14\x02\u011A\u0118\x03\x02\x02" + + "\x02\u011A\u011B\x03\x02\x02\x02\u011B%\x03\x02\x02\x02\u011C\u0121\x05" + + "*\x16\x02\u011D\u011E\x07\x1F\x02\x02\u011E\u0120\x05*\x16\x02\u011F\u011D" + + "\x03\x02\x02\x02\u0120\u0123\x03\x02\x02\x02\u0121\u011F\x03\x02\x02\x02" + + "\u0121\u0122\x03\x02\x02\x02\u0122\'\x03\x02\x02\x02\u0123\u0121\x03\x02" + + "\x02\x02\u0124\u0125\t\x04\x02\x02\u0125)\x03\x02\x02\x02\u0126\u012B" + + "\x05,\x17\x02\u0127\u0128\x07!\x02\x02\u0128\u012A\x05,\x17\x02\u0129" + + "\u0127\x03\x02\x02\x02\u012A\u012D\x03\x02\x02\x02\u012B\u0129\x03\x02" + + "\x02\x02\u012B\u012C\x03\x02\x02\x02\u012C+\x03\x02\x02\x02\u012D\u012B" + + "\x03\x02\x02\x02\u012E\u012F\t\x05\x02\x02\u012F-\x03\x02\x02\x02\u0130" + + "\u015B\x07*\x02\x02\u0131\u0132\x05N(\x02\u0132\u0133\x07A\x02\x02\u0133" + + "\u015B\x03\x02\x02\x02\u0134\u015B\x05L\'\x02\u0135\u015B\x05N(\x02\u0136" + + "\u015B\x05H%\x02\u0137\u015B\x07-\x02\x02\u0138\u015B\x05P)\x02\u0139" + + "\u013A\x07?\x02\x02\u013A\u013F\x05J&\x02\u013B\u013C\x07\x1F\x02\x02" + + "\u013C\u013E\x05J&\x02\u013D\u013B\x03\x02\x02\x02\u013E\u0141\x03\x02" + + "\x02\x02\u013F\u013D\x03\x02\x02\x02\u013F\u0140\x03\x02\x02\x02\u0140" + + "\u0142\x03\x02\x02\x02\u0141\u013F\x03\x02\x02\x02\u0142\u0143\x07@\x02" + + "\x02\u0143\u015B\x03\x02\x02\x02\u0144\u0145\x07?\x02\x02\u0145\u014A" + + "\x05H%\x02\u0146\u0147\x07\x1F\x02\x02\u0147\u0149\x05H%\x02\u0148\u0146" + + "\x03\x02\x02\x02\u0149\u014C\x03\x02\x02\x02\u014A\u0148\x03\x02\x02\x02" + + "\u014A\u014B\x03\x02\x02\x02\u014B\u014D\x03\x02\x02\x02\u014C\u014A\x03" + + "\x02\x02\x02\u014D\u014E\x07@\x02\x02\u014E\u015B\x03\x02\x02\x02\u014F" + + "\u0150\x07?\x02\x02\u0150\u0155\x05P)\x02\u0151\u0152\x07\x1F\x02\x02" + + "\u0152\u0154\x05P)\x02\u0153\u0151\x03\x02\x02\x02\u0154\u0157\x03\x02" + + "\x02\x02\u0155\u0153\x03\x02\x02\x02\u0155\u0156\x03\x02\x02\x02\u0156" + + "\u0158\x03\x02\x02\x02\u0157\u0155\x03\x02\x02\x02\u0158\u0159\x07@\x02" + + "\x02\u0159\u015B\x03\x02\x02\x02\u015A\u0130\x03\x02\x02\x02\u015A\u0131" + + "\x03\x02\x02\x02\u015A\u0134\x03\x02\x02\x02\u015A\u0135\x03\x02\x02\x02" + + "\u015A\u0136\x03\x02\x02\x02\u015A\u0137\x03\x02\x02\x02\u015A\u0138\x03" + + "\x02\x02\x02\u015A\u0139\x03\x02\x02\x02\u015A\u0144\x03\x02\x02\x02\u015A" + + "\u014F\x03\x02\x02\x02\u015B/\x03\x02\x02\x02\u015C\u015D\x07\n\x02\x02" + + "\u015D\u015E\x07\x19\x02\x02\u015E1\x03\x02\x02\x02\u015F\u0160\x07\x10" + + "\x02\x02\u0160\u0165\x054\x1B\x02\u0161\u0162\x07\x1F\x02\x02\u0162\u0164" + + "\x054\x1B\x02\u0163\u0161\x03\x02\x02\x02\u0164\u0167\x03\x02\x02\x02" + + "\u0165\u0163\x03\x02\x02\x02\u0165\u0166\x03\x02\x02\x02\u01663\x03\x02" + + "\x02\x02\u0167\u0165\x03\x02\x02\x02\u0168\u016A\x05\f\x07\x02\u0169\u016B" + + "\t\x06\x02\x02\u016A\u0169\x03\x02\x02\x02\u016A\u016B\x03\x02\x02\x02" + + "\u016B\u016E\x03\x02\x02\x02\u016C\u016D\x07+\x02\x02\u016D\u016F\t\x07" + + "\x02\x02\u016E\u016C\x03\x02\x02\x02\u016E\u016F\x03\x02\x02\x02\u016F" + + "5\x03\x02\x02\x02\u0170\u0171\x07\t\x02\x02\u0171\u0176\x05(\x15\x02\u0172" + + "\u0173\x07\x1F\x02\x02\u0173\u0175\x05(\x15\x02\u0174\u0172\x03\x02\x02" + + "\x02\u0175\u0178\x03\x02\x02\x02\u0176\u0174\x03\x02\x02\x02\u0176\u0177" + + "\x03\x02\x02\x02\u0177\u0183\x03\x02\x02\x02\u0178\u0176\x03\x02\x02\x02" + + "\u0179\u017A\x07\f\x02\x02\u017A\u017F\x05(\x15\x02\u017B\u017C\x07\x1F" + + "\x02\x02\u017C\u017E\x05(\x15\x02\u017D\u017B\x03\x02\x02\x02\u017E\u0181" + + "\x03\x02\x02\x02\u017F\u017D\x03\x02\x02\x02\u017F\u0180\x03\x02\x02\x02" + + "\u0180\u0183\x03\x02\x02\x02\u0181\u017F\x03\x02\x02\x02\u0182\u0170\x03" + + "\x02\x02\x02\u0182\u0179\x03\x02\x02\x02\u01837\x03\x02\x02\x02\u0184" + + "\u0185\x07\x04\x02\x02\u0185\u018A\x05(\x15\x02\u0186\u0187\x07\x1F\x02" + + "\x02\u0187\u0189\x05(\x15\x02\u0188\u0186\x03\x02\x02\x02\u0189\u018C" + + "\x03\x02\x02\x02\u018A\u0188\x03\x02\x02\x02\u018A\u018B\x03\x02\x02\x02" + + "\u018B9\x03\x02\x02\x02\u018C\u018A\x03\x02\x02\x02\u018D\u018E\x07\r" + + "\x02\x02\u018E\u0193\x05<\x1F\x02\u018F\u0190\x07\x1F\x02\x02\u0190\u0192" + + "\x05<\x1F\x02\u0191\u018F\x03\x02\x02\x02\u0192\u0195\x03\x02\x02\x02" + + "\u0193\u0191\x03\x02\x02\x02\u0193\u0194\x03\x02\x02\x02\u0194;\x03\x02" + + "\x02\x02\u0195\u0193\x03\x02\x02\x02\u0196\u0197\x05(\x15\x02\u0197\u0198" + + "\x07F\x02\x02\u0198\u0199\x05(\x15\x02\u0199=\x03\x02\x02\x02\u019A\u019B" + + "\x07\x03\x02\x02\u019B\u019C\x05\x14\v\x02\u019C\u019E\x05P)\x02\u019D" + + "\u019F\x05D#\x02\u019E\u019D\x03\x02\x02\x02\u019E\u019F\x03\x02\x02\x02" + + "\u019F?\x03\x02\x02\x02\u01A0\u01A1\x07\b\x02\x02\u01A1\u01A2\x05\x14" + + "\v\x02\u01A2\u01A3\x05P)\x02\u01A3A\x03\x02\x02\x02\u01A4\u01A5\x07\v" + + "\x02\x02\u01A5\u01A6\x05(\x15\x02\u01A6C\x03\x02\x02\x02\u01A7\u01AC\x05" + + "F$\x02\u01A8\u01A9\x07\x1F\x02\x02\u01A9\u01AB\x05F$\x02\u01AA\u01A8\x03" + + "\x02\x02\x02\u01AB\u01AE\x03\x02\x02\x02\u01AC\u01AA\x03\x02\x02\x02\u01AC" + + "\u01AD\x03\x02\x02\x02\u01ADE\x03\x02\x02\x02\u01AE\u01AC\x03\x02\x02" + + "\x02\u01AF\u01B0\x05,\x17\x02\u01B0\u01B1\x07\x1E\x02\x02\u01B1\u01B2" + + "\x05.\x18\x02\u01B2G\x03\x02\x02\x02\u01B3\u01B4\t\b\x02\x02\u01B4I\x03" + + "\x02\x02\x02\u01B5\u01B8\x05L\'\x02\u01B6\u01B8\x05N(\x02\u01B7\u01B5" + + "\x03\x02\x02\x02\u01B7\u01B6\x03\x02\x02\x02\u01B8K\x03\x02\x02\x02\u01B9" + + "\u01BB\t\x02\x02\x02\u01BA\u01B9\x03\x02\x02\x02\u01BA\u01BB\x03\x02\x02" + + "\x02\u01BB\u01BC\x03\x02\x02\x02\u01BC\u01BD\x07\x1A\x02\x02\u01BDM\x03" + + "\x02\x02\x02\u01BE\u01C0\t\x02\x02\x02\u01BF\u01BE\x03\x02\x02\x02\u01BF" + + "\u01C0\x03\x02\x02\x02\u01C0\u01C1\x03\x02\x02\x02\u01C1\u01C2\x07\x19" + + "\x02\x02\u01C2O\x03\x02\x02\x02\u01C3\u01C4\x07\x18\x02\x02\u01C4Q\x03" + + "\x02\x02\x02\u01C5\u01C6\t\t\x02\x02\u01C6S\x03\x02\x02\x02\u01C7\u01C8" + + "\x07\x0F\x02\x02\u01C8\u01CC\x071\x02\x02\u01C9\u01CA\x07\x0F\x02\x02" + + "\u01CA\u01CC\x072\x02\x02\u01CB\u01C7\x03\x02\x02\x02\u01CB\u01C9\x03" + + "\x02\x02\x02\u01CCU\x03\x02\x02\x02\u01CD\u01CE\x07\x05\x02\x02\u01CE" + + "\u01D1\x05(\x15\x02\u01CF\u01D0\x07H\x02\x02\u01D0\u01D2\x05(\x15\x02" + + "\u01D1\u01CF\x03\x02\x02\x02\u01D1\u01D2\x03\x02\x02\x02\u01D2\u01DC\x03" + + "\x02\x02\x02\u01D3\u01D4\x07I\x02\x02\u01D4\u01D9\x05X-\x02\u01D5\u01D6" + + "\x07\x1F\x02\x02\u01D6\u01D8\x05X-\x02\u01D7\u01D5\x03\x02\x02\x02\u01D8" + + "\u01DB\x03\x02\x02\x02\u01D9\u01D7\x03\x02\x02\x02\u01D9\u01DA\x03\x02" + + "\x02\x02\u01DA\u01DD\x03\x02\x02\x02\u01DB\u01D9\x03\x02\x02\x02\u01DC" + + "\u01D3\x03\x02\x02\x02\u01DC\u01DD\x03\x02\x02\x02\u01DDW\x03\x02\x02" + + "\x02\u01DE\u01DF\x05(\x15\x02\u01DF\u01E0\x07\x1E\x02\x02\u01E0\u01E2" + + "\x03\x02\x02\x02\u01E1\u01DE\x03\x02\x02\x02\u01E1\u01E2\x03\x02\x02\x02" + + "\u01E2\u01E3\x03\x02\x02\x02\u01E3\u01E4\x05(\x15\x02\u01E4Y\x03\x02\x02" + + "\x024eky\x85\x8E\x96\x9A\xA2\xA4\xA9\xB0\xB5\xBC\xC2\xCA\xCC\xD6\xE0\xE3" + + "\xEF\xF7\xFF\u0103\u010C\u0116\u011A\u0121\u012B\u013F\u014A\u0155\u015A" + + "\u0165\u016A\u016E\u0176\u017F\u0182\u018A\u0193\u019E\u01AC\u01B7\u01BA" + + "\u01BF\u01CB\u01D1\u01D9\u01DC\u01E1"; public static __ATN: ATN; public static get _ATN(): ATN { if (!esql_parser.__ATN) { @@ -3621,9 +2981,6 @@ export class CompositeQueryContext extends QueryContext { export class SourceCommandContext extends ParserRuleContext { - public explainCommand(): ExplainCommandContext | undefined { - return this.tryGetRuleContext(0, ExplainCommandContext); - } public fromCommand(): FromCommandContext | undefined { return this.tryGetRuleContext(0, FromCommandContext); } @@ -3660,39 +3017,36 @@ export class ProcessingCommandContext extends ParserRuleContext { public limitCommand(): LimitCommandContext | undefined { return this.tryGetRuleContext(0, LimitCommandContext); } - public projectCommand(): ProjectCommandContext | undefined { - return this.tryGetRuleContext(0, ProjectCommandContext); - } public keepCommand(): KeepCommandContext | undefined { return this.tryGetRuleContext(0, KeepCommandContext); } - public renameCommand(): RenameCommandContext | undefined { - return this.tryGetRuleContext(0, RenameCommandContext); + public sortCommand(): SortCommandContext | undefined { + return this.tryGetRuleContext(0, SortCommandContext); + } + public statsCommand(): StatsCommandContext | undefined { + return this.tryGetRuleContext(0, StatsCommandContext); + } + public whereCommand(): WhereCommandContext | undefined { + return this.tryGetRuleContext(0, WhereCommandContext); } public dropCommand(): DropCommandContext | undefined { return this.tryGetRuleContext(0, DropCommandContext); } + public renameCommand(): RenameCommandContext | undefined { + return this.tryGetRuleContext(0, RenameCommandContext); + } public dissectCommand(): DissectCommandContext | undefined { return this.tryGetRuleContext(0, DissectCommandContext); } public grokCommand(): GrokCommandContext | undefined { return this.tryGetRuleContext(0, GrokCommandContext); } - public sortCommand(): SortCommandContext | undefined { - return this.tryGetRuleContext(0, SortCommandContext); - } - public statsCommand(): StatsCommandContext | undefined { - return this.tryGetRuleContext(0, StatsCommandContext); - } - public whereCommand(): WhereCommandContext | undefined { - return this.tryGetRuleContext(0, WhereCommandContext); + public enrichCommand(): EnrichCommandContext | undefined { + return this.tryGetRuleContext(0, EnrichCommandContext); } public mvExpandCommand(): MvExpandCommandContext | undefined { return this.tryGetRuleContext(0, MvExpandCommandContext); } - public enrichCommand(): EnrichCommandContext | undefined { - return this.tryGetRuleContext(0, EnrichCommandContext); - } constructor(parent: ParserRuleContext | undefined, invokingState: number) { super(parent, invokingState); } @@ -3713,153 +3067,138 @@ export class ProcessingCommandContext extends ParserRuleContext { } -export class EnrichCommandContext extends ParserRuleContext { - public _policyName: EnrichIdentifierContext; - public _matchField: EnrichFieldIdentifierContext; - public ENRICH(): TerminalNode { return this.getToken(esql_parser.ENRICH, 0); } - public enrichIdentifier(): EnrichIdentifierContext { - return this.getRuleContext(0, EnrichIdentifierContext); - } - public ON(): TerminalNode | undefined { return this.tryGetToken(esql_parser.ON, 0); } - public WITH(): TerminalNode | undefined { return this.tryGetToken(esql_parser.WITH, 0); } - public enrichWithClause(): EnrichWithClauseContext[]; - public enrichWithClause(i: number): EnrichWithClauseContext; - public enrichWithClause(i?: number): EnrichWithClauseContext | EnrichWithClauseContext[] { - if (i === undefined) { - return this.getRuleContexts(EnrichWithClauseContext); - } else { - return this.getRuleContext(i, EnrichWithClauseContext); - } - } - public enrichFieldIdentifier(): EnrichFieldIdentifierContext | undefined { - return this.tryGetRuleContext(0, EnrichFieldIdentifierContext); - } - public COMMA(): TerminalNode[]; - public COMMA(i: number): TerminalNode; - public COMMA(i?: number): TerminalNode | TerminalNode[] { - if (i === undefined) { - return this.getTokens(esql_parser.COMMA); - } else { - return this.getToken(esql_parser.COMMA, i); - } +export class WhereCommandContext extends ParserRuleContext { + public WHERE(): TerminalNode { return this.getToken(esql_parser.WHERE, 0); } + public booleanExpression(): BooleanExpressionContext { + return this.getRuleContext(0, BooleanExpressionContext); } constructor(parent: ParserRuleContext | undefined, invokingState: number) { super(parent, invokingState); } // @Override - public get ruleIndex(): number { return esql_parser.RULE_enrichCommand; } + public get ruleIndex(): number { return esql_parser.RULE_whereCommand; } // @Override public enterRule(listener: esql_parserListener): void { - if (listener.enterEnrichCommand) { - listener.enterEnrichCommand(this); + if (listener.enterWhereCommand) { + listener.enterWhereCommand(this); } } // @Override public exitRule(listener: esql_parserListener): void { - if (listener.exitEnrichCommand) { - listener.exitEnrichCommand(this); + if (listener.exitWhereCommand) { + listener.exitWhereCommand(this); } } } -export class EnrichWithClauseContext extends ParserRuleContext { - public _newName: EnrichFieldIdentifierContext; - public _enrichField: EnrichFieldIdentifierContext; - public enrichFieldIdentifier(): EnrichFieldIdentifierContext[]; - public enrichFieldIdentifier(i: number): EnrichFieldIdentifierContext; - public enrichFieldIdentifier(i?: number): EnrichFieldIdentifierContext | EnrichFieldIdentifierContext[] { - if (i === undefined) { - return this.getRuleContexts(EnrichFieldIdentifierContext); - } else { - return this.getRuleContext(i, EnrichFieldIdentifierContext); - } - } - public ASSIGN(): TerminalNode | undefined { return this.tryGetToken(esql_parser.ASSIGN, 0); } +export class BooleanExpressionContext extends ParserRuleContext { constructor(parent: ParserRuleContext | undefined, invokingState: number) { super(parent, invokingState); } // @Override - public get ruleIndex(): number { return esql_parser.RULE_enrichWithClause; } + public get ruleIndex(): number { return esql_parser.RULE_booleanExpression; } + public copyFrom(ctx: BooleanExpressionContext): void { + super.copyFrom(ctx); + } +} +export class LogicalNotContext extends BooleanExpressionContext { + public NOT(): TerminalNode { return this.getToken(esql_parser.NOT, 0); } + public booleanExpression(): BooleanExpressionContext { + return this.getRuleContext(0, BooleanExpressionContext); + } + constructor(ctx: BooleanExpressionContext) { + super(ctx.parent, ctx.invokingState); + this.copyFrom(ctx); + } // @Override public enterRule(listener: esql_parserListener): void { - if (listener.enterEnrichWithClause) { - listener.enterEnrichWithClause(this); + if (listener.enterLogicalNot) { + listener.enterLogicalNot(this); } } // @Override public exitRule(listener: esql_parserListener): void { - if (listener.exitEnrichWithClause) { - listener.exitEnrichWithClause(this); + if (listener.exitLogicalNot) { + listener.exitLogicalNot(this); } } } - - -export class MvExpandCommandContext extends ParserRuleContext { - public MV_EXPAND(): TerminalNode { return this.getToken(esql_parser.MV_EXPAND, 0); } - public qualifiedNames(): QualifiedNamesContext { - return this.getRuleContext(0, QualifiedNamesContext); +export class BooleanDefaultContext extends BooleanExpressionContext { + public valueExpression(): ValueExpressionContext { + return this.getRuleContext(0, ValueExpressionContext); } - constructor(parent: ParserRuleContext | undefined, invokingState: number) { - super(parent, invokingState); + constructor(ctx: BooleanExpressionContext) { + super(ctx.parent, ctx.invokingState); + this.copyFrom(ctx); } // @Override - public get ruleIndex(): number { return esql_parser.RULE_mvExpandCommand; } + public enterRule(listener: esql_parserListener): void { + if (listener.enterBooleanDefault) { + listener.enterBooleanDefault(this); + } + } + // @Override + public exitRule(listener: esql_parserListener): void { + if (listener.exitBooleanDefault) { + listener.exitBooleanDefault(this); + } + } +} +export class RegexExpressionContext extends BooleanExpressionContext { + public regexBooleanExpression(): RegexBooleanExpressionContext { + return this.getRuleContext(0, RegexBooleanExpressionContext); + } + constructor(ctx: BooleanExpressionContext) { + super(ctx.parent, ctx.invokingState); + this.copyFrom(ctx); + } // @Override public enterRule(listener: esql_parserListener): void { - if (listener.enterMvExpandCommand) { - listener.enterMvExpandCommand(this); + if (listener.enterRegexExpression) { + listener.enterRegexExpression(this); } } // @Override public exitRule(listener: esql_parserListener): void { - if (listener.exitMvExpandCommand) { - listener.exitMvExpandCommand(this); + if (listener.exitRegexExpression) { + listener.exitRegexExpression(this); } } } - - -export class WhereCommandContext extends ParserRuleContext { - public WHERE(): TerminalNode { return this.getToken(esql_parser.WHERE, 0); } - public whereBooleanExpression(): WhereBooleanExpressionContext { - return this.getRuleContext(0, WhereBooleanExpressionContext); +export class LogicalBinaryContext extends BooleanExpressionContext { + public _left: BooleanExpressionContext; + public _operator: Token; + public _right: BooleanExpressionContext; + public booleanExpression(): BooleanExpressionContext[]; + public booleanExpression(i: number): BooleanExpressionContext; + public booleanExpression(i?: number): BooleanExpressionContext | BooleanExpressionContext[] { + if (i === undefined) { + return this.getRuleContexts(BooleanExpressionContext); + } else { + return this.getRuleContext(i, BooleanExpressionContext); + } } - constructor(parent: ParserRuleContext | undefined, invokingState: number) { - super(parent, invokingState); + public AND(): TerminalNode | undefined { return this.tryGetToken(esql_parser.AND, 0); } + public OR(): TerminalNode | undefined { return this.tryGetToken(esql_parser.OR, 0); } + constructor(ctx: BooleanExpressionContext) { + super(ctx.parent, ctx.invokingState); + this.copyFrom(ctx); } // @Override - public get ruleIndex(): number { return esql_parser.RULE_whereCommand; } - // @Override public enterRule(listener: esql_parserListener): void { - if (listener.enterWhereCommand) { - listener.enterWhereCommand(this); + if (listener.enterLogicalBinary) { + listener.enterLogicalBinary(this); } } // @Override public exitRule(listener: esql_parserListener): void { - if (listener.exitWhereCommand) { - listener.exitWhereCommand(this); + if (listener.exitLogicalBinary) { + listener.exitLogicalBinary(this); } } } - - -export class WhereBooleanExpressionContext extends ParserRuleContext { - public _left: WhereBooleanExpressionContext; - public _operator: Token; - public _right: WhereBooleanExpressionContext; - public NOT(): TerminalNode | undefined { return this.tryGetToken(esql_parser.NOT, 0); } - public whereBooleanExpression(): WhereBooleanExpressionContext[]; - public whereBooleanExpression(i: number): WhereBooleanExpressionContext; - public whereBooleanExpression(i?: number): WhereBooleanExpressionContext | WhereBooleanExpressionContext[] { - if (i === undefined) { - return this.getRuleContexts(WhereBooleanExpressionContext); - } else { - return this.getRuleContext(i, WhereBooleanExpressionContext); - } - } +export class LogicalInContext extends BooleanExpressionContext { public valueExpression(): ValueExpressionContext[]; public valueExpression(i: number): ValueExpressionContext; public valueExpression(i?: number): ValueExpressionContext | ValueExpressionContext[] { @@ -3869,14 +3208,10 @@ export class WhereBooleanExpressionContext extends ParserRuleContext { return this.getRuleContext(i, ValueExpressionContext); } } - public regexBooleanExpression(): RegexBooleanExpressionContext | undefined { - return this.tryGetRuleContext(0, RegexBooleanExpressionContext); - } - public AND(): TerminalNode | undefined { return this.tryGetToken(esql_parser.AND, 0); } - public OR(): TerminalNode | undefined { return this.tryGetToken(esql_parser.OR, 0); } - public IN(): TerminalNode | undefined { return this.tryGetToken(esql_parser.IN, 0); } - public LP(): TerminalNode | undefined { return this.tryGetToken(esql_parser.LP, 0); } - public RP(): TerminalNode | undefined { return this.tryGetToken(esql_parser.RP, 0); } + public IN(): TerminalNode { return this.getToken(esql_parser.IN, 0); } + public LP(): TerminalNode { return this.getToken(esql_parser.LP, 0); } + public RP(): TerminalNode { return this.getToken(esql_parser.RP, 0); } + public NOT(): TerminalNode | undefined { return this.tryGetToken(esql_parser.NOT, 0); } public COMMA(): TerminalNode[]; public COMMA(i: number): TerminalNode; public COMMA(i?: number): TerminalNode | TerminalNode[] { @@ -3886,75 +3221,44 @@ export class WhereBooleanExpressionContext extends ParserRuleContext { return this.getToken(esql_parser.COMMA, i); } } - public WHERE_FUNCTIONS(): TerminalNode | undefined { return this.tryGetToken(esql_parser.WHERE_FUNCTIONS, 0); } - public qualifiedName(): QualifiedNameContext | undefined { - return this.tryGetRuleContext(0, QualifiedNameContext); - } - public functionExpressionArgument(): FunctionExpressionArgumentContext[]; - public functionExpressionArgument(i: number): FunctionExpressionArgumentContext; - public functionExpressionArgument(i?: number): FunctionExpressionArgumentContext | FunctionExpressionArgumentContext[] { - if (i === undefined) { - return this.getRuleContexts(FunctionExpressionArgumentContext); - } else { - return this.getRuleContext(i, FunctionExpressionArgumentContext); - } - } - public IS(): TerminalNode | undefined { return this.tryGetToken(esql_parser.IS, 0); } - public NULL(): TerminalNode | undefined { return this.tryGetToken(esql_parser.NULL, 0); } - constructor(parent: ParserRuleContext | undefined, invokingState: number) { - super(parent, invokingState); + constructor(ctx: BooleanExpressionContext) { + super(ctx.parent, ctx.invokingState); + this.copyFrom(ctx); } // @Override - public get ruleIndex(): number { return esql_parser.RULE_whereBooleanExpression; } - // @Override public enterRule(listener: esql_parserListener): void { - if (listener.enterWhereBooleanExpression) { - listener.enterWhereBooleanExpression(this); + if (listener.enterLogicalIn) { + listener.enterLogicalIn(this); } } // @Override public exitRule(listener: esql_parserListener): void { - if (listener.exitWhereBooleanExpression) { - listener.exitWhereBooleanExpression(this); + if (listener.exitLogicalIn) { + listener.exitLogicalIn(this); } } } - - -export class BooleanExpressionContext extends ParserRuleContext { - public _left: BooleanExpressionContext; - public _operator: Token; - public _right: BooleanExpressionContext; - public NOT(): TerminalNode | undefined { return this.tryGetToken(esql_parser.NOT, 0); } - public booleanExpression(): BooleanExpressionContext[]; - public booleanExpression(i: number): BooleanExpressionContext; - public booleanExpression(i?: number): BooleanExpressionContext | BooleanExpressionContext[] { - if (i === undefined) { - return this.getRuleContexts(BooleanExpressionContext); - } else { - return this.getRuleContext(i, BooleanExpressionContext); - } - } - public valueExpression(): ValueExpressionContext | undefined { - return this.tryGetRuleContext(0, ValueExpressionContext); +export class IsNullContext extends BooleanExpressionContext { + public valueExpression(): ValueExpressionContext { + return this.getRuleContext(0, ValueExpressionContext); } - public AND(): TerminalNode | undefined { return this.tryGetToken(esql_parser.AND, 0); } - public OR(): TerminalNode | undefined { return this.tryGetToken(esql_parser.OR, 0); } - constructor(parent: ParserRuleContext | undefined, invokingState: number) { - super(parent, invokingState); + public IS(): TerminalNode { return this.getToken(esql_parser.IS, 0); } + public NULL(): TerminalNode { return this.getToken(esql_parser.NULL, 0); } + public NOT(): TerminalNode | undefined { return this.tryGetToken(esql_parser.NOT, 0); } + constructor(ctx: BooleanExpressionContext) { + super(ctx.parent, ctx.invokingState); + this.copyFrom(ctx); } // @Override - public get ruleIndex(): number { return esql_parser.RULE_booleanExpression; } - // @Override public enterRule(listener: esql_parserListener): void { - if (listener.enterBooleanExpression) { - listener.enterBooleanExpression(this); + if (listener.enterIsNull) { + listener.enterIsNull(this); } } // @Override public exitRule(listener: esql_parserListener): void { - if (listener.exitBooleanExpression) { - listener.exitBooleanExpression(this); + if (listener.exitIsNull) { + listener.exitIsNull(this); } } } @@ -3993,33 +3297,37 @@ export class RegexBooleanExpressionContext extends ParserRuleContext { export class ValueExpressionContext extends ParserRuleContext { - public operatorExpression(): OperatorExpressionContext | undefined { - return this.tryGetRuleContext(0, OperatorExpressionContext); - } - public comparison(): ComparisonContext | undefined { - return this.tryGetRuleContext(0, ComparisonContext); - } constructor(parent: ParserRuleContext | undefined, invokingState: number) { super(parent, invokingState); } // @Override public get ruleIndex(): number { return esql_parser.RULE_valueExpression; } + public copyFrom(ctx: ValueExpressionContext): void { + super.copyFrom(ctx); + } +} +export class ValueExpressionDefaultContext extends ValueExpressionContext { + public operatorExpression(): OperatorExpressionContext { + return this.getRuleContext(0, OperatorExpressionContext); + } + constructor(ctx: ValueExpressionContext) { + super(ctx.parent, ctx.invokingState); + this.copyFrom(ctx); + } // @Override public enterRule(listener: esql_parserListener): void { - if (listener.enterValueExpression) { - listener.enterValueExpression(this); + if (listener.enterValueExpressionDefault) { + listener.enterValueExpressionDefault(this); } } // @Override public exitRule(listener: esql_parserListener): void { - if (listener.exitValueExpression) { - listener.exitValueExpression(this); + if (listener.exitValueExpressionDefault) { + listener.exitValueExpressionDefault(this); } } } - - -export class ComparisonContext extends ParserRuleContext { +export class ComparisonContext extends ValueExpressionContext { public _left: OperatorExpressionContext; public _right: OperatorExpressionContext; public comparisonOperator(): ComparisonOperatorContext { @@ -4034,12 +3342,11 @@ export class ComparisonContext extends ParserRuleContext { return this.getRuleContext(i, OperatorExpressionContext); } } - constructor(parent: ParserRuleContext | undefined, invokingState: number) { - super(parent, invokingState); + constructor(ctx: ValueExpressionContext) { + super(ctx.parent, ctx.invokingState); + this.copyFrom(ctx); } // @Override - public get ruleIndex(): number { return esql_parser.RULE_comparison; } - // @Override public enterRule(listener: esql_parserListener): void { if (listener.enterComparison) { listener.enterComparison(this); @@ -4054,178 +3361,203 @@ export class ComparisonContext extends ParserRuleContext { } -export class MathFnContext extends ParserRuleContext { - public functionIdentifier(): FunctionIdentifierContext { - return this.getRuleContext(0, FunctionIdentifierContext); - } - public LP(): TerminalNode { return this.getToken(esql_parser.LP, 0); } - public RP(): TerminalNode { return this.getToken(esql_parser.RP, 0); } - public functionExpressionArgument(): FunctionExpressionArgumentContext[]; - public functionExpressionArgument(i: number): FunctionExpressionArgumentContext; - public functionExpressionArgument(i?: number): FunctionExpressionArgumentContext | FunctionExpressionArgumentContext[] { - if (i === undefined) { - return this.getRuleContexts(FunctionExpressionArgumentContext); - } else { - return this.getRuleContext(i, FunctionExpressionArgumentContext); - } - } - public COMMA(): TerminalNode[]; - public COMMA(i: number): TerminalNode; - public COMMA(i?: number): TerminalNode | TerminalNode[] { - if (i === undefined) { - return this.getTokens(esql_parser.COMMA); - } else { - return this.getToken(esql_parser.COMMA, i); - } - } +export class OperatorExpressionContext extends ParserRuleContext { constructor(parent: ParserRuleContext | undefined, invokingState: number) { super(parent, invokingState); } // @Override - public get ruleIndex(): number { return esql_parser.RULE_mathFn; } + public get ruleIndex(): number { return esql_parser.RULE_operatorExpression; } + public copyFrom(ctx: OperatorExpressionContext): void { + super.copyFrom(ctx); + } +} +export class OperatorExpressionDefaultContext extends OperatorExpressionContext { + public primaryExpression(): PrimaryExpressionContext { + return this.getRuleContext(0, PrimaryExpressionContext); + } + constructor(ctx: OperatorExpressionContext) { + super(ctx.parent, ctx.invokingState); + this.copyFrom(ctx); + } // @Override public enterRule(listener: esql_parserListener): void { - if (listener.enterMathFn) { - listener.enterMathFn(this); + if (listener.enterOperatorExpressionDefault) { + listener.enterOperatorExpressionDefault(this); } } // @Override public exitRule(listener: esql_parserListener): void { - if (listener.exitMathFn) { - listener.exitMathFn(this); + if (listener.exitOperatorExpressionDefault) { + listener.exitOperatorExpressionDefault(this); } } } - - -export class MathEvalFnContext extends ParserRuleContext { - public mathFunctionIdentifier(): MathFunctionIdentifierContext { - return this.getRuleContext(0, MathFunctionIdentifierContext); +export class ArithmeticUnaryContext extends OperatorExpressionContext { + public _operator: Token; + public operatorExpression(): OperatorExpressionContext { + return this.getRuleContext(0, OperatorExpressionContext); } - public LP(): TerminalNode { return this.getToken(esql_parser.LP, 0); } - public RP(): TerminalNode { return this.getToken(esql_parser.RP, 0); } - public mathFunctionExpressionArgument(): MathFunctionExpressionArgumentContext[]; - public mathFunctionExpressionArgument(i: number): MathFunctionExpressionArgumentContext; - public mathFunctionExpressionArgument(i?: number): MathFunctionExpressionArgumentContext | MathFunctionExpressionArgumentContext[] { - if (i === undefined) { - return this.getRuleContexts(MathFunctionExpressionArgumentContext); - } else { - return this.getRuleContext(i, MathFunctionExpressionArgumentContext); + public MINUS(): TerminalNode | undefined { return this.tryGetToken(esql_parser.MINUS, 0); } + public PLUS(): TerminalNode | undefined { return this.tryGetToken(esql_parser.PLUS, 0); } + constructor(ctx: OperatorExpressionContext) { + super(ctx.parent, ctx.invokingState); + this.copyFrom(ctx); + } + // @Override + public enterRule(listener: esql_parserListener): void { + if (listener.enterArithmeticUnary) { + listener.enterArithmeticUnary(this); } } - public COMMA(): TerminalNode[]; - public COMMA(i: number): TerminalNode; - public COMMA(i?: number): TerminalNode | TerminalNode[] { + // @Override + public exitRule(listener: esql_parserListener): void { + if (listener.exitArithmeticUnary) { + listener.exitArithmeticUnary(this); + } + } +} +export class ArithmeticBinaryContext extends OperatorExpressionContext { + public _left: OperatorExpressionContext; + public _operator: Token; + public _right: OperatorExpressionContext; + public operatorExpression(): OperatorExpressionContext[]; + public operatorExpression(i: number): OperatorExpressionContext; + public operatorExpression(i?: number): OperatorExpressionContext | OperatorExpressionContext[] { if (i === undefined) { - return this.getTokens(esql_parser.COMMA); + return this.getRuleContexts(OperatorExpressionContext); } else { - return this.getToken(esql_parser.COMMA, i); + return this.getRuleContext(i, OperatorExpressionContext); } } - constructor(parent: ParserRuleContext | undefined, invokingState: number) { - super(parent, invokingState); + public ASTERISK(): TerminalNode | undefined { return this.tryGetToken(esql_parser.ASTERISK, 0); } + public SLASH(): TerminalNode | undefined { return this.tryGetToken(esql_parser.SLASH, 0); } + public PERCENT(): TerminalNode | undefined { return this.tryGetToken(esql_parser.PERCENT, 0); } + public PLUS(): TerminalNode | undefined { return this.tryGetToken(esql_parser.PLUS, 0); } + public MINUS(): TerminalNode | undefined { return this.tryGetToken(esql_parser.MINUS, 0); } + constructor(ctx: OperatorExpressionContext) { + super(ctx.parent, ctx.invokingState); + this.copyFrom(ctx); } // @Override - public get ruleIndex(): number { return esql_parser.RULE_mathEvalFn; } - // @Override public enterRule(listener: esql_parserListener): void { - if (listener.enterMathEvalFn) { - listener.enterMathEvalFn(this); + if (listener.enterArithmeticBinary) { + listener.enterArithmeticBinary(this); } } // @Override public exitRule(listener: esql_parserListener): void { - if (listener.exitMathEvalFn) { - listener.exitMathEvalFn(this); + if (listener.exitArithmeticBinary) { + listener.exitArithmeticBinary(this); } } } -export class DateExpressionContext extends ParserRuleContext { - public _quantifier: NumberContext; - public DATE_LITERAL(): TerminalNode { return this.getToken(esql_parser.DATE_LITERAL, 0); } - public number(): NumberContext { - return this.getRuleContext(0, NumberContext); - } +export class PrimaryExpressionContext extends ParserRuleContext { constructor(parent: ParserRuleContext | undefined, invokingState: number) { super(parent, invokingState); } // @Override - public get ruleIndex(): number { return esql_parser.RULE_dateExpression; } + public get ruleIndex(): number { return esql_parser.RULE_primaryExpression; } + public copyFrom(ctx: PrimaryExpressionContext): void { + super.copyFrom(ctx); + } +} +export class ConstantDefaultContext extends PrimaryExpressionContext { + public constant(): ConstantContext { + return this.getRuleContext(0, ConstantContext); + } + constructor(ctx: PrimaryExpressionContext) { + super(ctx.parent, ctx.invokingState); + this.copyFrom(ctx); + } // @Override public enterRule(listener: esql_parserListener): void { - if (listener.enterDateExpression) { - listener.enterDateExpression(this); + if (listener.enterConstantDefault) { + listener.enterConstantDefault(this); } } // @Override public exitRule(listener: esql_parserListener): void { - if (listener.exitDateExpression) { - listener.exitDateExpression(this); + if (listener.exitConstantDefault) { + listener.exitConstantDefault(this); } } } - - -export class OperatorExpressionContext extends ParserRuleContext { - public _left: OperatorExpressionContext; - public _operator: Token; - public _right: OperatorExpressionContext; - public primaryExpression(): PrimaryExpressionContext | undefined { - return this.tryGetRuleContext(0, PrimaryExpressionContext); +export class DereferenceContext extends PrimaryExpressionContext { + public qualifiedName(): QualifiedNameContext { + return this.getRuleContext(0, QualifiedNameContext); } - public mathFn(): MathFnContext | undefined { - return this.tryGetRuleContext(0, MathFnContext); + constructor(ctx: PrimaryExpressionContext) { + super(ctx.parent, ctx.invokingState); + this.copyFrom(ctx); } - public mathEvalFn(): MathEvalFnContext | undefined { - return this.tryGetRuleContext(0, MathEvalFnContext); + // @Override + public enterRule(listener: esql_parserListener): void { + if (listener.enterDereference) { + listener.enterDereference(this); + } } - public operatorExpression(): OperatorExpressionContext[]; - public operatorExpression(i: number): OperatorExpressionContext; - public operatorExpression(i?: number): OperatorExpressionContext | OperatorExpressionContext[] { - if (i === undefined) { - return this.getRuleContexts(OperatorExpressionContext); - } else { - return this.getRuleContext(i, OperatorExpressionContext); + // @Override + public exitRule(listener: esql_parserListener): void { + if (listener.exitDereference) { + listener.exitDereference(this); } } - public MINUS(): TerminalNode | undefined { return this.tryGetToken(esql_parser.MINUS, 0); } - public PLUS(): TerminalNode | undefined { return this.tryGetToken(esql_parser.PLUS, 0); } - public ASTERISK(): TerminalNode | undefined { return this.tryGetToken(esql_parser.ASTERISK, 0); } - public SLASH(): TerminalNode | undefined { return this.tryGetToken(esql_parser.SLASH, 0); } - public PERCENT(): TerminalNode | undefined { return this.tryGetToken(esql_parser.PERCENT, 0); } - constructor(parent: ParserRuleContext | undefined, invokingState: number) { - super(parent, invokingState); +} +export class FunctionContext extends PrimaryExpressionContext { + public functionExpression(): FunctionExpressionContext { + return this.getRuleContext(0, FunctionExpressionContext); + } + constructor(ctx: PrimaryExpressionContext) { + super(ctx.parent, ctx.invokingState); + this.copyFrom(ctx); } // @Override - public get ruleIndex(): number { return esql_parser.RULE_operatorExpression; } + public enterRule(listener: esql_parserListener): void { + if (listener.enterFunction) { + listener.enterFunction(this); + } + } + // @Override + public exitRule(listener: esql_parserListener): void { + if (listener.exitFunction) { + listener.exitFunction(this); + } + } +} +export class ParenthesizedExpressionContext extends PrimaryExpressionContext { + public LP(): TerminalNode { return this.getToken(esql_parser.LP, 0); } + public booleanExpression(): BooleanExpressionContext { + return this.getRuleContext(0, BooleanExpressionContext); + } + public RP(): TerminalNode { return this.getToken(esql_parser.RP, 0); } + constructor(ctx: PrimaryExpressionContext) { + super(ctx.parent, ctx.invokingState); + this.copyFrom(ctx); + } // @Override public enterRule(listener: esql_parserListener): void { - if (listener.enterOperatorExpression) { - listener.enterOperatorExpression(this); + if (listener.enterParenthesizedExpression) { + listener.enterParenthesizedExpression(this); } } // @Override public exitRule(listener: esql_parserListener): void { - if (listener.exitOperatorExpression) { - listener.exitOperatorExpression(this); + if (listener.exitParenthesizedExpression) { + listener.exitParenthesizedExpression(this); } } } -export class PrimaryExpressionContext extends ParserRuleContext { - public constant(): ConstantContext | undefined { - return this.tryGetRuleContext(0, ConstantContext); - } - public qualifiedName(): QualifiedNameContext | undefined { - return this.tryGetRuleContext(0, QualifiedNameContext); - } - public dateExpression(): DateExpressionContext | undefined { - return this.tryGetRuleContext(0, DateExpressionContext); +export class FunctionExpressionContext extends ParserRuleContext { + public identifier(): IdentifierContext { + return this.getRuleContext(0, IdentifierContext); } - public LP(): TerminalNode | undefined { return this.tryGetToken(esql_parser.LP, 0); } + public LP(): TerminalNode { return this.getToken(esql_parser.LP, 0); } + public RP(): TerminalNode { return this.getToken(esql_parser.RP, 0); } + public ASTERISK(): TerminalNode | undefined { return this.tryGetToken(esql_parser.ASTERISK, 0); } public booleanExpression(): BooleanExpressionContext[]; public booleanExpression(i: number): BooleanExpressionContext; public booleanExpression(i?: number): BooleanExpressionContext | BooleanExpressionContext[] { @@ -4235,10 +3567,6 @@ export class PrimaryExpressionContext extends ParserRuleContext { return this.getRuleContext(i, BooleanExpressionContext); } } - public RP(): TerminalNode | undefined { return this.tryGetToken(esql_parser.RP, 0); } - public identifier(): IdentifierContext | undefined { - return this.tryGetRuleContext(0, IdentifierContext); - } public COMMA(): TerminalNode[]; public COMMA(i: number): TerminalNode; public COMMA(i?: number): TerminalNode | TerminalNode[] { @@ -4252,17 +3580,17 @@ export class PrimaryExpressionContext extends ParserRuleContext { super(parent, invokingState); } // @Override - public get ruleIndex(): number { return esql_parser.RULE_primaryExpression; } + public get ruleIndex(): number { return esql_parser.RULE_functionExpression; } // @Override public enterRule(listener: esql_parserListener): void { - if (listener.enterPrimaryExpression) { - listener.enterPrimaryExpression(this); + if (listener.enterFunctionExpression) { + listener.enterFunctionExpression(this); } } // @Override public exitRule(listener: esql_parserListener): void { - if (listener.exitPrimaryExpression) { - listener.exitPrimaryExpression(this); + if (listener.exitFunctionExpression) { + listener.exitFunctionExpression(this); } } } @@ -4336,72 +3664,25 @@ export class FieldContext extends ParserRuleContext { public booleanExpression(): BooleanExpressionContext { return this.getRuleContext(0, BooleanExpressionContext); } - public userVariable(): UserVariableContext | undefined { - return this.tryGetRuleContext(0, UserVariableContext); - } - public ASSIGN(): TerminalNode | undefined { return this.tryGetToken(esql_parser.ASSIGN, 0); } - constructor(parent: ParserRuleContext | undefined, invokingState: number) { - super(parent, invokingState); - } - // @Override - public get ruleIndex(): number { return esql_parser.RULE_field; } - // @Override - public enterRule(listener: esql_parserListener): void { - if (listener.enterField) { - listener.enterField(this); - } - } - // @Override - public exitRule(listener: esql_parserListener): void { - if (listener.exitField) { - listener.exitField(this); - } - } -} - - -export class EnrichFieldIdentifierContext extends ParserRuleContext { - public ENR_UNQUOTED_IDENTIFIER(): TerminalNode | undefined { return this.tryGetToken(esql_parser.ENR_UNQUOTED_IDENTIFIER, 0); } - public ENR_QUOTED_IDENTIFIER(): TerminalNode | undefined { return this.tryGetToken(esql_parser.ENR_QUOTED_IDENTIFIER, 0); } - constructor(parent: ParserRuleContext | undefined, invokingState: number) { - super(parent, invokingState); - } - // @Override - public get ruleIndex(): number { return esql_parser.RULE_enrichFieldIdentifier; } - // @Override - public enterRule(listener: esql_parserListener): void { - if (listener.enterEnrichFieldIdentifier) { - listener.enterEnrichFieldIdentifier(this); - } - } - // @Override - public exitRule(listener: esql_parserListener): void { - if (listener.exitEnrichFieldIdentifier) { - listener.exitEnrichFieldIdentifier(this); - } - } -} - - -export class UserVariableContext extends ParserRuleContext { - public identifier(): IdentifierContext { - return this.getRuleContext(0, IdentifierContext); + public qualifiedName(): QualifiedNameContext | undefined { + return this.tryGetRuleContext(0, QualifiedNameContext); } + public ASSIGN(): TerminalNode | undefined { return this.tryGetToken(esql_parser.ASSIGN, 0); } constructor(parent: ParserRuleContext | undefined, invokingState: number) { super(parent, invokingState); } // @Override - public get ruleIndex(): number { return esql_parser.RULE_userVariable; } + public get ruleIndex(): number { return esql_parser.RULE_field; } // @Override public enterRule(listener: esql_parserListener): void { - if (listener.enterUserVariable) { - listener.enterUserVariable(this); + if (listener.enterField) { + listener.enterField(this); } } // @Override public exitRule(listener: esql_parserListener): void { - if (listener.exitUserVariable) { - listener.exitUserVariable(this); + if (listener.exitField) { + listener.exitField(this); } } } @@ -4523,8 +3804,8 @@ export class StatsCommandContext extends ParserRuleContext { return this.tryGetRuleContext(0, FieldsContext); } public BY(): TerminalNode | undefined { return this.tryGetToken(esql_parser.BY, 0); } - public qualifiedNames(): QualifiedNamesContext | undefined { - return this.tryGetRuleContext(0, QualifiedNamesContext); + public grouping(): GroupingContext | undefined { + return this.tryGetRuleContext(0, GroupingContext); } constructor(parent: ParserRuleContext | undefined, invokingState: number) { super(parent, invokingState); @@ -4546,116 +3827,63 @@ export class StatsCommandContext extends ParserRuleContext { } -export class SourceIdentifierContext extends ParserRuleContext { - public SRC_UNQUOTED_IDENTIFIER(): TerminalNode | undefined { return this.tryGetToken(esql_parser.SRC_UNQUOTED_IDENTIFIER, 0); } - public SRC_QUOTED_IDENTIFIER(): TerminalNode | undefined { return this.tryGetToken(esql_parser.SRC_QUOTED_IDENTIFIER, 0); } - constructor(parent: ParserRuleContext | undefined, invokingState: number) { - super(parent, invokingState); - } - // @Override - public get ruleIndex(): number { return esql_parser.RULE_sourceIdentifier; } - // @Override - public enterRule(listener: esql_parserListener): void { - if (listener.enterSourceIdentifier) { - listener.enterSourceIdentifier(this); - } - } - // @Override - public exitRule(listener: esql_parserListener): void { - if (listener.exitSourceIdentifier) { - listener.exitSourceIdentifier(this); - } - } -} - - -export class EnrichIdentifierContext extends ParserRuleContext { - public ENR_UNQUOTED_IDENTIFIER(): TerminalNode | undefined { return this.tryGetToken(esql_parser.ENR_UNQUOTED_IDENTIFIER, 0); } - public ENR_QUOTED_IDENTIFIER(): TerminalNode | undefined { return this.tryGetToken(esql_parser.ENR_QUOTED_IDENTIFIER, 0); } - constructor(parent: ParserRuleContext | undefined, invokingState: number) { - super(parent, invokingState); - } - // @Override - public get ruleIndex(): number { return esql_parser.RULE_enrichIdentifier; } - // @Override - public enterRule(listener: esql_parserListener): void { - if (listener.enterEnrichIdentifier) { - listener.enterEnrichIdentifier(this); +export class GroupingContext extends ParserRuleContext { + public qualifiedName(): QualifiedNameContext[]; + public qualifiedName(i: number): QualifiedNameContext; + public qualifiedName(i?: number): QualifiedNameContext | QualifiedNameContext[] { + if (i === undefined) { + return this.getRuleContexts(QualifiedNameContext); + } else { + return this.getRuleContext(i, QualifiedNameContext); } } - // @Override - public exitRule(listener: esql_parserListener): void { - if (listener.exitEnrichIdentifier) { - listener.exitEnrichIdentifier(this); + public COMMA(): TerminalNode[]; + public COMMA(i: number): TerminalNode; + public COMMA(i?: number): TerminalNode | TerminalNode[] { + if (i === undefined) { + return this.getTokens(esql_parser.COMMA); + } else { + return this.getToken(esql_parser.COMMA, i); } } -} - - -export class FunctionExpressionArgumentContext extends ParserRuleContext { - public qualifiedName(): QualifiedNameContext | undefined { - return this.tryGetRuleContext(0, QualifiedNameContext); - } - public string(): StringContext | undefined { - return this.tryGetRuleContext(0, StringContext); - } - public number(): NumberContext | undefined { - return this.tryGetRuleContext(0, NumberContext); - } constructor(parent: ParserRuleContext | undefined, invokingState: number) { super(parent, invokingState); } // @Override - public get ruleIndex(): number { return esql_parser.RULE_functionExpressionArgument; } + public get ruleIndex(): number { return esql_parser.RULE_grouping; } // @Override public enterRule(listener: esql_parserListener): void { - if (listener.enterFunctionExpressionArgument) { - listener.enterFunctionExpressionArgument(this); + if (listener.enterGrouping) { + listener.enterGrouping(this); } } // @Override public exitRule(listener: esql_parserListener): void { - if (listener.exitFunctionExpressionArgument) { - listener.exitFunctionExpressionArgument(this); + if (listener.exitGrouping) { + listener.exitGrouping(this); } } } -export class MathFunctionExpressionArgumentContext extends ParserRuleContext { - public qualifiedName(): QualifiedNameContext | undefined { - return this.tryGetRuleContext(0, QualifiedNameContext); - } - public string(): StringContext | undefined { - return this.tryGetRuleContext(0, StringContext); - } - public number(): NumberContext | undefined { - return this.tryGetRuleContext(0, NumberContext); - } - public operatorExpression(): OperatorExpressionContext | undefined { - return this.tryGetRuleContext(0, OperatorExpressionContext); - } - public dateExpression(): DateExpressionContext | undefined { - return this.tryGetRuleContext(0, DateExpressionContext); - } - public comparison(): ComparisonContext | undefined { - return this.tryGetRuleContext(0, ComparisonContext); - } +export class SourceIdentifierContext extends ParserRuleContext { + public SRC_UNQUOTED_IDENTIFIER(): TerminalNode | undefined { return this.tryGetToken(esql_parser.SRC_UNQUOTED_IDENTIFIER, 0); } + public SRC_QUOTED_IDENTIFIER(): TerminalNode | undefined { return this.tryGetToken(esql_parser.SRC_QUOTED_IDENTIFIER, 0); } constructor(parent: ParserRuleContext | undefined, invokingState: number) { super(parent, invokingState); } // @Override - public get ruleIndex(): number { return esql_parser.RULE_mathFunctionExpressionArgument; } + public get ruleIndex(): number { return esql_parser.RULE_sourceIdentifier; } // @Override public enterRule(listener: esql_parserListener): void { - if (listener.enterMathFunctionExpressionArgument) { - listener.enterMathFunctionExpressionArgument(this); + if (listener.enterSourceIdentifier) { + listener.enterSourceIdentifier(this); } } // @Override public exitRule(listener: esql_parserListener): void { - if (listener.exitMathFunctionExpressionArgument) { - listener.exitMathFunctionExpressionArgument(this); + if (listener.exitSourceIdentifier) { + listener.exitSourceIdentifier(this); } } } @@ -4700,115 +3928,185 @@ export class QualifiedNameContext extends ParserRuleContext { } -export class QualifiedNamesContext extends ParserRuleContext { - public qualifiedName(): QualifiedNameContext[]; - public qualifiedName(i: number): QualifiedNameContext; - public qualifiedName(i?: number): QualifiedNameContext | QualifiedNameContext[] { - if (i === undefined) { - return this.getRuleContexts(QualifiedNameContext); - } else { - return this.getRuleContext(i, QualifiedNameContext); - } - } - public COMMA(): TerminalNode[]; - public COMMA(i: number): TerminalNode; - public COMMA(i?: number): TerminalNode | TerminalNode[] { - if (i === undefined) { - return this.getTokens(esql_parser.COMMA); - } else { - return this.getToken(esql_parser.COMMA, i); - } - } +export class IdentifierContext extends ParserRuleContext { + public UNQUOTED_IDENTIFIER(): TerminalNode | undefined { return this.tryGetToken(esql_parser.UNQUOTED_IDENTIFIER, 0); } + public QUOTED_IDENTIFIER(): TerminalNode | undefined { return this.tryGetToken(esql_parser.QUOTED_IDENTIFIER, 0); } constructor(parent: ParserRuleContext | undefined, invokingState: number) { super(parent, invokingState); } // @Override - public get ruleIndex(): number { return esql_parser.RULE_qualifiedNames; } + public get ruleIndex(): number { return esql_parser.RULE_identifier; } // @Override public enterRule(listener: esql_parserListener): void { - if (listener.enterQualifiedNames) { - listener.enterQualifiedNames(this); + if (listener.enterIdentifier) { + listener.enterIdentifier(this); } } // @Override public exitRule(listener: esql_parserListener): void { - if (listener.exitQualifiedNames) { - listener.exitQualifiedNames(this); + if (listener.exitIdentifier) { + listener.exitIdentifier(this); } } } -export class IdentifierContext extends ParserRuleContext { - public UNQUOTED_IDENTIFIER(): TerminalNode | undefined { return this.tryGetToken(esql_parser.UNQUOTED_IDENTIFIER, 0); } - public QUOTED_IDENTIFIER(): TerminalNode | undefined { return this.tryGetToken(esql_parser.QUOTED_IDENTIFIER, 0); } - public ASTERISK(): TerminalNode | undefined { return this.tryGetToken(esql_parser.ASTERISK, 0); } +export class ConstantContext extends ParserRuleContext { constructor(parent: ParserRuleContext | undefined, invokingState: number) { super(parent, invokingState); } // @Override - public get ruleIndex(): number { return esql_parser.RULE_identifier; } + public get ruleIndex(): number { return esql_parser.RULE_constant; } + public copyFrom(ctx: ConstantContext): void { + super.copyFrom(ctx); + } +} +export class NullLiteralContext extends ConstantContext { + public NULL(): TerminalNode { return this.getToken(esql_parser.NULL, 0); } + constructor(ctx: ConstantContext) { + super(ctx.parent, ctx.invokingState); + this.copyFrom(ctx); + } // @Override public enterRule(listener: esql_parserListener): void { - if (listener.enterIdentifier) { - listener.enterIdentifier(this); + if (listener.enterNullLiteral) { + listener.enterNullLiteral(this); } } // @Override public exitRule(listener: esql_parserListener): void { - if (listener.exitIdentifier) { - listener.exitIdentifier(this); + if (listener.exitNullLiteral) { + listener.exitNullLiteral(this); } } } - - -export class MathFunctionIdentifierContext extends ParserRuleContext { - public MATH_FUNCTION(): TerminalNode { return this.getToken(esql_parser.MATH_FUNCTION, 0); } - constructor(parent: ParserRuleContext | undefined, invokingState: number) { - super(parent, invokingState); +export class QualifiedIntegerLiteralContext extends ConstantContext { + public integerValue(): IntegerValueContext { + return this.getRuleContext(0, IntegerValueContext); + } + public UNQUOTED_IDENTIFIER(): TerminalNode { return this.getToken(esql_parser.UNQUOTED_IDENTIFIER, 0); } + constructor(ctx: ConstantContext) { + super(ctx.parent, ctx.invokingState); + this.copyFrom(ctx); } // @Override - public get ruleIndex(): number { return esql_parser.RULE_mathFunctionIdentifier; } + public enterRule(listener: esql_parserListener): void { + if (listener.enterQualifiedIntegerLiteral) { + listener.enterQualifiedIntegerLiteral(this); + } + } + // @Override + public exitRule(listener: esql_parserListener): void { + if (listener.exitQualifiedIntegerLiteral) { + listener.exitQualifiedIntegerLiteral(this); + } + } +} +export class DecimalLiteralContext extends ConstantContext { + public decimalValue(): DecimalValueContext { + return this.getRuleContext(0, DecimalValueContext); + } + constructor(ctx: ConstantContext) { + super(ctx.parent, ctx.invokingState); + this.copyFrom(ctx); + } // @Override public enterRule(listener: esql_parserListener): void { - if (listener.enterMathFunctionIdentifier) { - listener.enterMathFunctionIdentifier(this); + if (listener.enterDecimalLiteral) { + listener.enterDecimalLiteral(this); } } // @Override public exitRule(listener: esql_parserListener): void { - if (listener.exitMathFunctionIdentifier) { - listener.exitMathFunctionIdentifier(this); + if (listener.exitDecimalLiteral) { + listener.exitDecimalLiteral(this); } } } - - -export class FunctionIdentifierContext extends ParserRuleContext { - public UNARY_FUNCTION(): TerminalNode { return this.getToken(esql_parser.UNARY_FUNCTION, 0); } - constructor(parent: ParserRuleContext | undefined, invokingState: number) { - super(parent, invokingState); +export class IntegerLiteralContext extends ConstantContext { + public integerValue(): IntegerValueContext { + return this.getRuleContext(0, IntegerValueContext); + } + constructor(ctx: ConstantContext) { + super(ctx.parent, ctx.invokingState); + this.copyFrom(ctx); + } + // @Override + public enterRule(listener: esql_parserListener): void { + if (listener.enterIntegerLiteral) { + listener.enterIntegerLiteral(this); + } + } + // @Override + public exitRule(listener: esql_parserListener): void { + if (listener.exitIntegerLiteral) { + listener.exitIntegerLiteral(this); + } + } +} +export class BooleanLiteralContext extends ConstantContext { + public booleanValue(): BooleanValueContext { + return this.getRuleContext(0, BooleanValueContext); + } + constructor(ctx: ConstantContext) { + super(ctx.parent, ctx.invokingState); + this.copyFrom(ctx); + } + // @Override + public enterRule(listener: esql_parserListener): void { + if (listener.enterBooleanLiteral) { + listener.enterBooleanLiteral(this); + } } // @Override - public get ruleIndex(): number { return esql_parser.RULE_functionIdentifier; } + public exitRule(listener: esql_parserListener): void { + if (listener.exitBooleanLiteral) { + listener.exitBooleanLiteral(this); + } + } +} +export class InputParamContext extends ConstantContext { + public PARAM(): TerminalNode { return this.getToken(esql_parser.PARAM, 0); } + constructor(ctx: ConstantContext) { + super(ctx.parent, ctx.invokingState); + this.copyFrom(ctx); + } // @Override public enterRule(listener: esql_parserListener): void { - if (listener.enterFunctionIdentifier) { - listener.enterFunctionIdentifier(this); + if (listener.enterInputParam) { + listener.enterInputParam(this); } } // @Override public exitRule(listener: esql_parserListener): void { - if (listener.exitFunctionIdentifier) { - listener.exitFunctionIdentifier(this); + if (listener.exitInputParam) { + listener.exitInputParam(this); } } } - - -export class ConstantContext extends ParserRuleContext { - public NULL(): TerminalNode | undefined { return this.tryGetToken(esql_parser.NULL, 0); } +export class StringLiteralContext extends ConstantContext { + public string(): StringContext { + return this.getRuleContext(0, StringContext); + } + constructor(ctx: ConstantContext) { + super(ctx.parent, ctx.invokingState); + this.copyFrom(ctx); + } + // @Override + public enterRule(listener: esql_parserListener): void { + if (listener.enterStringLiteral) { + listener.enterStringLiteral(this); + } + } + // @Override + public exitRule(listener: esql_parserListener): void { + if (listener.exitStringLiteral) { + listener.exitStringLiteral(this); + } + } +} +export class NumericArrayLiteralContext extends ConstantContext { + public OPENING_BRACKET(): TerminalNode { return this.getToken(esql_parser.OPENING_BRACKET, 0); } public numericValue(): NumericValueContext[]; public numericValue(i: number): NumericValueContext; public numericValue(i?: number): NumericValueContext | NumericValueContext[] { @@ -4818,6 +4116,35 @@ export class ConstantContext extends ParserRuleContext { return this.getRuleContext(i, NumericValueContext); } } + public CLOSING_BRACKET(): TerminalNode { return this.getToken(esql_parser.CLOSING_BRACKET, 0); } + public COMMA(): TerminalNode[]; + public COMMA(i: number): TerminalNode; + public COMMA(i?: number): TerminalNode | TerminalNode[] { + if (i === undefined) { + return this.getTokens(esql_parser.COMMA); + } else { + return this.getToken(esql_parser.COMMA, i); + } + } + constructor(ctx: ConstantContext) { + super(ctx.parent, ctx.invokingState); + this.copyFrom(ctx); + } + // @Override + public enterRule(listener: esql_parserListener): void { + if (listener.enterNumericArrayLiteral) { + listener.enterNumericArrayLiteral(this); + } + } + // @Override + public exitRule(listener: esql_parserListener): void { + if (listener.exitNumericArrayLiteral) { + listener.exitNumericArrayLiteral(this); + } + } +} +export class BooleanArrayLiteralContext extends ConstantContext { + public OPENING_BRACKET(): TerminalNode { return this.getToken(esql_parser.OPENING_BRACKET, 0); } public booleanValue(): BooleanValueContext[]; public booleanValue(i: number): BooleanValueContext; public booleanValue(i?: number): BooleanValueContext | BooleanValueContext[] { @@ -4827,17 +4154,7 @@ export class ConstantContext extends ParserRuleContext { return this.getRuleContext(i, BooleanValueContext); } } - public string(): StringContext[]; - public string(i: number): StringContext; - public string(i?: number): StringContext | StringContext[] { - if (i === undefined) { - return this.getRuleContexts(StringContext); - } else { - return this.getRuleContext(i, StringContext); - } - } - public OPENING_BRACKET(): TerminalNode | undefined { return this.tryGetToken(esql_parser.OPENING_BRACKET, 0); } - public CLOSING_BRACKET(): TerminalNode | undefined { return this.tryGetToken(esql_parser.CLOSING_BRACKET, 0); } + public CLOSING_BRACKET(): TerminalNode { return this.getToken(esql_parser.CLOSING_BRACKET, 0); } public COMMA(): TerminalNode[]; public COMMA(i: number): TerminalNode; public COMMA(i?: number): TerminalNode | TerminalNode[] { @@ -4847,48 +4164,58 @@ export class ConstantContext extends ParserRuleContext { return this.getToken(esql_parser.COMMA, i); } } - constructor(parent: ParserRuleContext | undefined, invokingState: number) { - super(parent, invokingState); + constructor(ctx: ConstantContext) { + super(ctx.parent, ctx.invokingState); + this.copyFrom(ctx); } // @Override - public get ruleIndex(): number { return esql_parser.RULE_constant; } - // @Override public enterRule(listener: esql_parserListener): void { - if (listener.enterConstant) { - listener.enterConstant(this); + if (listener.enterBooleanArrayLiteral) { + listener.enterBooleanArrayLiteral(this); } } // @Override public exitRule(listener: esql_parserListener): void { - if (listener.exitConstant) { - listener.exitConstant(this); + if (listener.exitBooleanArrayLiteral) { + listener.exitBooleanArrayLiteral(this); } } } - - -export class NumericValueContext extends ParserRuleContext { - public decimalValue(): DecimalValueContext | undefined { - return this.tryGetRuleContext(0, DecimalValueContext); +export class StringArrayLiteralContext extends ConstantContext { + public OPENING_BRACKET(): TerminalNode { return this.getToken(esql_parser.OPENING_BRACKET, 0); } + public string(): StringContext[]; + public string(i: number): StringContext; + public string(i?: number): StringContext | StringContext[] { + if (i === undefined) { + return this.getRuleContexts(StringContext); + } else { + return this.getRuleContext(i, StringContext); + } } - public integerValue(): IntegerValueContext | undefined { - return this.tryGetRuleContext(0, IntegerValueContext); + public CLOSING_BRACKET(): TerminalNode { return this.getToken(esql_parser.CLOSING_BRACKET, 0); } + public COMMA(): TerminalNode[]; + public COMMA(i: number): TerminalNode; + public COMMA(i?: number): TerminalNode | TerminalNode[] { + if (i === undefined) { + return this.getTokens(esql_parser.COMMA); + } else { + return this.getToken(esql_parser.COMMA, i); + } } - constructor(parent: ParserRuleContext | undefined, invokingState: number) { - super(parent, invokingState); + constructor(ctx: ConstantContext) { + super(ctx.parent, ctx.invokingState); + this.copyFrom(ctx); } // @Override - public get ruleIndex(): number { return esql_parser.RULE_numericValue; } - // @Override public enterRule(listener: esql_parserListener): void { - if (listener.enterNumericValue) { - listener.enterNumericValue(this); + if (listener.enterStringArrayLiteral) { + listener.enterStringArrayLiteral(this); } } // @Override public exitRule(listener: esql_parserListener): void { - if (listener.exitNumericValue) { - listener.exitNumericValue(this); + if (listener.exitStringArrayLiteral) { + listener.exitStringArrayLiteral(this); } } } @@ -4958,12 +4285,16 @@ export class SortCommandContext extends ParserRuleContext { export class OrderExpressionContext extends ParserRuleContext { + public _ordering: Token; + public _nullOrdering: Token; public booleanExpression(): BooleanExpressionContext { return this.getRuleContext(0, BooleanExpressionContext); } - public ORDERING(): TerminalNode | undefined { return this.tryGetToken(esql_parser.ORDERING, 0); } - public NULLS_ORDERING(): TerminalNode | undefined { return this.tryGetToken(esql_parser.NULLS_ORDERING, 0); } - public NULLS_ORDERING_DIRECTION(): TerminalNode | undefined { return this.tryGetToken(esql_parser.NULLS_ORDERING_DIRECTION, 0); } + public NULLS(): TerminalNode | undefined { return this.tryGetToken(esql_parser.NULLS, 0); } + public ASC(): TerminalNode | undefined { return this.tryGetToken(esql_parser.ASC, 0); } + public DESC(): TerminalNode | undefined { return this.tryGetToken(esql_parser.DESC, 0); } + public FIRST(): TerminalNode | undefined { return this.tryGetToken(esql_parser.FIRST, 0); } + public LAST(): TerminalNode | undefined { return this.tryGetToken(esql_parser.LAST, 0); } constructor(parent: ParserRuleContext | undefined, invokingState: number) { super(parent, invokingState); } @@ -4984,36 +4315,27 @@ export class OrderExpressionContext extends ParserRuleContext { } -export class ProjectCommandContext extends ParserRuleContext { - public PROJECT(): TerminalNode { return this.getToken(esql_parser.PROJECT, 0); } - public qualifiedNames(): QualifiedNamesContext { - return this.getRuleContext(0, QualifiedNamesContext); - } - constructor(parent: ParserRuleContext | undefined, invokingState: number) { - super(parent, invokingState); - } - // @Override - public get ruleIndex(): number { return esql_parser.RULE_projectCommand; } - // @Override - public enterRule(listener: esql_parserListener): void { - if (listener.enterProjectCommand) { - listener.enterProjectCommand(this); +export class KeepCommandContext extends ParserRuleContext { + public KEEP(): TerminalNode | undefined { return this.tryGetToken(esql_parser.KEEP, 0); } + public sourceIdentifier(): SourceIdentifierContext[]; + public sourceIdentifier(i: number): SourceIdentifierContext; + public sourceIdentifier(i?: number): SourceIdentifierContext | SourceIdentifierContext[] { + if (i === undefined) { + return this.getRuleContexts(SourceIdentifierContext); + } else { + return this.getRuleContext(i, SourceIdentifierContext); } } - // @Override - public exitRule(listener: esql_parserListener): void { - if (listener.exitProjectCommand) { - listener.exitProjectCommand(this); + public COMMA(): TerminalNode[]; + public COMMA(i: number): TerminalNode; + public COMMA(i?: number): TerminalNode | TerminalNode[] { + if (i === undefined) { + return this.getTokens(esql_parser.COMMA); + } else { + return this.getToken(esql_parser.COMMA, i); } } -} - - -export class KeepCommandContext extends ParserRuleContext { - public KEEP(): TerminalNode { return this.getToken(esql_parser.KEEP, 0); } - public qualifiedNames(): QualifiedNamesContext { - return this.getRuleContext(0, QualifiedNamesContext); - } + public PROJECT(): TerminalNode | undefined { return this.tryGetToken(esql_parser.PROJECT, 0); } constructor(parent: ParserRuleContext | undefined, invokingState: number) { super(parent, invokingState); } @@ -5036,63 +4358,39 @@ export class KeepCommandContext extends ParserRuleContext { export class DropCommandContext extends ParserRuleContext { public DROP(): TerminalNode { return this.getToken(esql_parser.DROP, 0); } - public qualifiedNames(): QualifiedNamesContext { - return this.getRuleContext(0, QualifiedNamesContext); - } - constructor(parent: ParserRuleContext | undefined, invokingState: number) { - super(parent, invokingState); - } - // @Override - public get ruleIndex(): number { return esql_parser.RULE_dropCommand; } - // @Override - public enterRule(listener: esql_parserListener): void { - if (listener.enterDropCommand) { - listener.enterDropCommand(this); - } - } - // @Override - public exitRule(listener: esql_parserListener): void { - if (listener.exitDropCommand) { - listener.exitDropCommand(this); - } - } -} - - -export class RenameVariableContext extends ParserRuleContext { - public identifier(): IdentifierContext[]; - public identifier(i: number): IdentifierContext; - public identifier(i?: number): IdentifierContext | IdentifierContext[] { + public sourceIdentifier(): SourceIdentifierContext[]; + public sourceIdentifier(i: number): SourceIdentifierContext; + public sourceIdentifier(i?: number): SourceIdentifierContext | SourceIdentifierContext[] { if (i === undefined) { - return this.getRuleContexts(IdentifierContext); + return this.getRuleContexts(SourceIdentifierContext); } else { - return this.getRuleContext(i, IdentifierContext); + return this.getRuleContext(i, SourceIdentifierContext); } } - public DOT(): TerminalNode[]; - public DOT(i: number): TerminalNode; - public DOT(i?: number): TerminalNode | TerminalNode[] { + public COMMA(): TerminalNode[]; + public COMMA(i: number): TerminalNode; + public COMMA(i?: number): TerminalNode | TerminalNode[] { if (i === undefined) { - return this.getTokens(esql_parser.DOT); + return this.getTokens(esql_parser.COMMA); } else { - return this.getToken(esql_parser.DOT, i); + return this.getToken(esql_parser.COMMA, i); } } constructor(parent: ParserRuleContext | undefined, invokingState: number) { super(parent, invokingState); } // @Override - public get ruleIndex(): number { return esql_parser.RULE_renameVariable; } + public get ruleIndex(): number { return esql_parser.RULE_dropCommand; } // @Override public enterRule(listener: esql_parserListener): void { - if (listener.enterRenameVariable) { - listener.enterRenameVariable(this); + if (listener.enterDropCommand) { + listener.enterDropCommand(this); } } // @Override public exitRule(listener: esql_parserListener): void { - if (listener.exitRenameVariable) { - listener.exitRenameVariable(this); + if (listener.exitDropCommand) { + listener.exitDropCommand(this); } } } @@ -5139,12 +4437,17 @@ export class RenameCommandContext extends ParserRuleContext { export class RenameClauseContext extends ParserRuleContext { - public qualifiedName(): QualifiedNameContext { - return this.getRuleContext(0, QualifiedNameContext); - } + public _oldName: SourceIdentifierContext; + public _newName: SourceIdentifierContext; public AS(): TerminalNode { return this.getToken(esql_parser.AS, 0); } - public renameVariable(): RenameVariableContext { - return this.getRuleContext(0, RenameVariableContext); + public sourceIdentifier(): SourceIdentifierContext[]; + public sourceIdentifier(i: number): SourceIdentifierContext; + public sourceIdentifier(i?: number): SourceIdentifierContext | SourceIdentifierContext[] { + if (i === undefined) { + return this.getRuleContexts(SourceIdentifierContext); + } else { + return this.getRuleContext(i, SourceIdentifierContext); + } } constructor(parent: ParserRuleContext | undefined, invokingState: number) { super(parent, invokingState); @@ -5168,8 +4471,8 @@ export class RenameClauseContext extends ParserRuleContext { export class DissectCommandContext extends ParserRuleContext { public DISSECT(): TerminalNode { return this.getToken(esql_parser.DISSECT, 0); } - public qualifiedNames(): QualifiedNamesContext { - return this.getRuleContext(0, QualifiedNamesContext); + public primaryExpression(): PrimaryExpressionContext { + return this.getRuleContext(0, PrimaryExpressionContext); } public string(): StringContext { return this.getRuleContext(0, StringContext); @@ -5199,8 +4502,8 @@ export class DissectCommandContext extends ParserRuleContext { export class GrokCommandContext extends ParserRuleContext { public GROK(): TerminalNode { return this.getToken(esql_parser.GROK, 0); } - public qualifiedNames(): QualifiedNamesContext { - return this.getRuleContext(0, QualifiedNamesContext); + public primaryExpression(): PrimaryExpressionContext { + return this.getRuleContext(0, PrimaryExpressionContext); } public string(): StringContext { return this.getRuleContext(0, StringContext); @@ -5225,6 +4528,31 @@ export class GrokCommandContext extends ParserRuleContext { } +export class MvExpandCommandContext extends ParserRuleContext { + public MV_EXPAND(): TerminalNode { return this.getToken(esql_parser.MV_EXPAND, 0); } + public sourceIdentifier(): SourceIdentifierContext { + return this.getRuleContext(0, SourceIdentifierContext); + } + constructor(parent: ParserRuleContext | undefined, invokingState: number) { + super(parent, invokingState); + } + // @Override + public get ruleIndex(): number { return esql_parser.RULE_mvExpandCommand; } + // @Override + public enterRule(listener: esql_parserListener): void { + if (listener.enterMvExpandCommand) { + listener.enterMvExpandCommand(this); + } + } + // @Override + public exitRule(listener: esql_parserListener): void { + if (listener.exitMvExpandCommand) { + listener.exitMvExpandCommand(this); + } + } +} + + export class CommandOptionsContext extends ParserRuleContext { public commandOption(): CommandOptionContext[]; public commandOption(i: number): CommandOptionContext; @@ -5293,7 +4621,8 @@ export class CommandOptionContext extends ParserRuleContext { export class BooleanValueContext extends ParserRuleContext { - public BOOLEAN_VALUE(): TerminalNode { return this.getToken(esql_parser.BOOLEAN_VALUE, 0); } + public TRUE(): TerminalNode | undefined { return this.tryGetToken(esql_parser.TRUE, 0); } + public FALSE(): TerminalNode | undefined { return this.tryGetToken(esql_parser.FALSE, 0); } constructor(parent: ParserRuleContext | undefined, invokingState: number) { super(parent, invokingState); } @@ -5314,51 +4643,28 @@ export class BooleanValueContext extends ParserRuleContext { } -export class NumberContext extends ParserRuleContext { - constructor(parent: ParserRuleContext | undefined, invokingState: number) { - super(parent, invokingState); - } - // @Override - public get ruleIndex(): number { return esql_parser.RULE_number; } - public copyFrom(ctx: NumberContext): void { - super.copyFrom(ctx); +export class NumericValueContext extends ParserRuleContext { + public decimalValue(): DecimalValueContext | undefined { + return this.tryGetRuleContext(0, DecimalValueContext); } -} -export class DecimalLiteralContext extends NumberContext { - public DECIMAL_LITERAL(): TerminalNode { return this.getToken(esql_parser.DECIMAL_LITERAL, 0); } - constructor(ctx: NumberContext) { - super(ctx.parent, ctx.invokingState); - this.copyFrom(ctx); + public integerValue(): IntegerValueContext | undefined { + return this.tryGetRuleContext(0, IntegerValueContext); } - // @Override - public enterRule(listener: esql_parserListener): void { - if (listener.enterDecimalLiteral) { - listener.enterDecimalLiteral(this); - } + constructor(parent: ParserRuleContext | undefined, invokingState: number) { + super(parent, invokingState); } // @Override - public exitRule(listener: esql_parserListener): void { - if (listener.exitDecimalLiteral) { - listener.exitDecimalLiteral(this); - } - } -} -export class IntegerLiteralContext extends NumberContext { - public INTEGER_LITERAL(): TerminalNode { return this.getToken(esql_parser.INTEGER_LITERAL, 0); } - constructor(ctx: NumberContext) { - super(ctx.parent, ctx.invokingState); - this.copyFrom(ctx); - } + public get ruleIndex(): number { return esql_parser.RULE_numericValue; } // @Override public enterRule(listener: esql_parserListener): void { - if (listener.enterIntegerLiteral) { - listener.enterIntegerLiteral(this); + if (listener.enterNumericValue) { + listener.enterNumericValue(this); } } // @Override public exitRule(listener: esql_parserListener): void { - if (listener.exitIntegerLiteral) { - listener.exitIntegerLiteral(this); + if (listener.exitNumericValue) { + listener.exitNumericValue(this); } } } @@ -5366,6 +4672,8 @@ export class IntegerLiteralContext extends NumberContext { export class DecimalValueContext extends ParserRuleContext { public DECIMAL_LITERAL(): TerminalNode { return this.getToken(esql_parser.DECIMAL_LITERAL, 0); } + public PLUS(): TerminalNode | undefined { return this.tryGetToken(esql_parser.PLUS, 0); } + public MINUS(): TerminalNode | undefined { return this.tryGetToken(esql_parser.MINUS, 0); } constructor(parent: ParserRuleContext | undefined, invokingState: number) { super(parent, invokingState); } @@ -5388,6 +4696,8 @@ export class DecimalValueContext extends ParserRuleContext { export class IntegerValueContext extends ParserRuleContext { public INTEGER_LITERAL(): TerminalNode { return this.getToken(esql_parser.INTEGER_LITERAL, 0); } + public PLUS(): TerminalNode | undefined { return this.tryGetToken(esql_parser.PLUS, 0); } + public MINUS(): TerminalNode | undefined { return this.tryGetToken(esql_parser.MINUS, 0); } constructor(parent: ParserRuleContext | undefined, invokingState: number) { super(parent, invokingState); } @@ -5431,7 +4741,12 @@ export class StringContext extends ParserRuleContext { export class ComparisonOperatorContext extends ParserRuleContext { - public COMPARISON_OPERATOR(): TerminalNode { return this.getToken(esql_parser.COMPARISON_OPERATOR, 0); } + public EQ(): TerminalNode | undefined { return this.tryGetToken(esql_parser.EQ, 0); } + public NEQ(): TerminalNode | undefined { return this.tryGetToken(esql_parser.NEQ, 0); } + public LT(): TerminalNode | undefined { return this.tryGetToken(esql_parser.LT, 0); } + public LTE(): TerminalNode | undefined { return this.tryGetToken(esql_parser.LTE, 0); } + public GT(): TerminalNode | undefined { return this.tryGetToken(esql_parser.GT, 0); } + public GTE(): TerminalNode | undefined { return this.tryGetToken(esql_parser.GTE, 0); } constructor(parent: ParserRuleContext | undefined, invokingState: number) { super(parent, invokingState); } @@ -5452,76 +4767,139 @@ export class ComparisonOperatorContext extends ParserRuleContext { } -export class ExplainCommandContext extends ParserRuleContext { - public EXPLAIN(): TerminalNode { return this.getToken(esql_parser.EXPLAIN, 0); } - public subqueryExpression(): SubqueryExpressionContext { - return this.getRuleContext(0, SubqueryExpressionContext); - } +export class ShowCommandContext extends ParserRuleContext { constructor(parent: ParserRuleContext | undefined, invokingState: number) { super(parent, invokingState); } // @Override - public get ruleIndex(): number { return esql_parser.RULE_explainCommand; } + public get ruleIndex(): number { return esql_parser.RULE_showCommand; } + public copyFrom(ctx: ShowCommandContext): void { + super.copyFrom(ctx); + } +} +export class ShowInfoContext extends ShowCommandContext { + public SHOW(): TerminalNode { return this.getToken(esql_parser.SHOW, 0); } + public INFO(): TerminalNode { return this.getToken(esql_parser.INFO, 0); } + constructor(ctx: ShowCommandContext) { + super(ctx.parent, ctx.invokingState); + this.copyFrom(ctx); + } + // @Override + public enterRule(listener: esql_parserListener): void { + if (listener.enterShowInfo) { + listener.enterShowInfo(this); + } + } + // @Override + public exitRule(listener: esql_parserListener): void { + if (listener.exitShowInfo) { + listener.exitShowInfo(this); + } + } +} +export class ShowFunctionsContext extends ShowCommandContext { + public SHOW(): TerminalNode { return this.getToken(esql_parser.SHOW, 0); } + public FUNCTIONS(): TerminalNode { return this.getToken(esql_parser.FUNCTIONS, 0); } + constructor(ctx: ShowCommandContext) { + super(ctx.parent, ctx.invokingState); + this.copyFrom(ctx); + } // @Override public enterRule(listener: esql_parserListener): void { - if (listener.enterExplainCommand) { - listener.enterExplainCommand(this); + if (listener.enterShowFunctions) { + listener.enterShowFunctions(this); } } // @Override public exitRule(listener: esql_parserListener): void { - if (listener.exitExplainCommand) { - listener.exitExplainCommand(this); + if (listener.exitShowFunctions) { + listener.exitShowFunctions(this); } } } -export class SubqueryExpressionContext extends ParserRuleContext { - public OPENING_BRACKET(): TerminalNode { return this.getToken(esql_parser.OPENING_BRACKET, 0); } - public query(): QueryContext { - return this.getRuleContext(0, QueryContext); +export class EnrichCommandContext extends ParserRuleContext { + public _policyName: SourceIdentifierContext; + public _matchField: SourceIdentifierContext; + public ENRICH(): TerminalNode { return this.getToken(esql_parser.ENRICH, 0); } + public sourceIdentifier(): SourceIdentifierContext[]; + public sourceIdentifier(i: number): SourceIdentifierContext; + public sourceIdentifier(i?: number): SourceIdentifierContext | SourceIdentifierContext[] { + if (i === undefined) { + return this.getRuleContexts(SourceIdentifierContext); + } else { + return this.getRuleContext(i, SourceIdentifierContext); + } + } + public ON(): TerminalNode | undefined { return this.tryGetToken(esql_parser.ON, 0); } + public WITH(): TerminalNode | undefined { return this.tryGetToken(esql_parser.WITH, 0); } + public enrichWithClause(): EnrichWithClauseContext[]; + public enrichWithClause(i: number): EnrichWithClauseContext; + public enrichWithClause(i?: number): EnrichWithClauseContext | EnrichWithClauseContext[] { + if (i === undefined) { + return this.getRuleContexts(EnrichWithClauseContext); + } else { + return this.getRuleContext(i, EnrichWithClauseContext); + } + } + public COMMA(): TerminalNode[]; + public COMMA(i: number): TerminalNode; + public COMMA(i?: number): TerminalNode | TerminalNode[] { + if (i === undefined) { + return this.getTokens(esql_parser.COMMA); + } else { + return this.getToken(esql_parser.COMMA, i); + } } - public CLOSING_BRACKET(): TerminalNode { return this.getToken(esql_parser.CLOSING_BRACKET, 0); } constructor(parent: ParserRuleContext | undefined, invokingState: number) { super(parent, invokingState); } // @Override - public get ruleIndex(): number { return esql_parser.RULE_subqueryExpression; } + public get ruleIndex(): number { return esql_parser.RULE_enrichCommand; } // @Override public enterRule(listener: esql_parserListener): void { - if (listener.enterSubqueryExpression) { - listener.enterSubqueryExpression(this); + if (listener.enterEnrichCommand) { + listener.enterEnrichCommand(this); } } // @Override public exitRule(listener: esql_parserListener): void { - if (listener.exitSubqueryExpression) { - listener.exitSubqueryExpression(this); + if (listener.exitEnrichCommand) { + listener.exitEnrichCommand(this); } } } -export class ShowCommandContext extends ParserRuleContext { - public SHOW(): TerminalNode { return this.getToken(esql_parser.SHOW, 0); } - public INFO(): TerminalNode | undefined { return this.tryGetToken(esql_parser.INFO, 0); } - public FUNCTIONS(): TerminalNode | undefined { return this.tryGetToken(esql_parser.FUNCTIONS, 0); } +export class EnrichWithClauseContext extends ParserRuleContext { + public _newName: SourceIdentifierContext; + public _enrichField: SourceIdentifierContext; + public sourceIdentifier(): SourceIdentifierContext[]; + public sourceIdentifier(i: number): SourceIdentifierContext; + public sourceIdentifier(i?: number): SourceIdentifierContext | SourceIdentifierContext[] { + if (i === undefined) { + return this.getRuleContexts(SourceIdentifierContext); + } else { + return this.getRuleContext(i, SourceIdentifierContext); + } + } + public ASSIGN(): TerminalNode | undefined { return this.tryGetToken(esql_parser.ASSIGN, 0); } constructor(parent: ParserRuleContext | undefined, invokingState: number) { super(parent, invokingState); } // @Override - public get ruleIndex(): number { return esql_parser.RULE_showCommand; } + public get ruleIndex(): number { return esql_parser.RULE_enrichWithClause; } // @Override public enterRule(listener: esql_parserListener): void { - if (listener.enterShowCommand) { - listener.enterShowCommand(this); + if (listener.enterEnrichWithClause) { + listener.enterEnrichWithClause(this); } } // @Override public exitRule(listener: esql_parserListener): void { - if (listener.exitShowCommand) { - listener.exitShowCommand(this); + if (listener.exitEnrichWithClause) { + listener.exitEnrichWithClause(this); } } } diff --git a/packages/kbn-monaco/src/esql/antlr/esql_parser_listener.ts b/packages/kbn-monaco/src/esql/antlr/esql_parser_listener.ts index f131d4072b773..ff67f81aeab09 100644 --- a/packages/kbn-monaco/src/esql/antlr/esql_parser_listener.ts +++ b/packages/kbn-monaco/src/esql/antlr/esql_parser_listener.ts @@ -4,70 +4,79 @@ import { ParseTreeListener } from "antlr4ts/tree/ParseTreeListener"; +import { ValueExpressionDefaultContext } from "./esql_parser"; +import { ComparisonContext } from "./esql_parser"; +import { NullLiteralContext } from "./esql_parser"; +import { QualifiedIntegerLiteralContext } from "./esql_parser"; import { DecimalLiteralContext } from "./esql_parser"; import { IntegerLiteralContext } from "./esql_parser"; +import { BooleanLiteralContext } from "./esql_parser"; +import { InputParamContext } from "./esql_parser"; +import { StringLiteralContext } from "./esql_parser"; +import { NumericArrayLiteralContext } from "./esql_parser"; +import { BooleanArrayLiteralContext } from "./esql_parser"; +import { StringArrayLiteralContext } from "./esql_parser"; +import { ShowInfoContext } from "./esql_parser"; +import { ShowFunctionsContext } from "./esql_parser"; +import { ConstantDefaultContext } from "./esql_parser"; +import { DereferenceContext } from "./esql_parser"; +import { FunctionContext } from "./esql_parser"; +import { ParenthesizedExpressionContext } from "./esql_parser"; import { SingleCommandQueryContext } from "./esql_parser"; import { CompositeQueryContext } from "./esql_parser"; +import { LogicalNotContext } from "./esql_parser"; +import { BooleanDefaultContext } from "./esql_parser"; +import { RegexExpressionContext } from "./esql_parser"; +import { LogicalBinaryContext } from "./esql_parser"; +import { LogicalInContext } from "./esql_parser"; +import { IsNullContext } from "./esql_parser"; +import { OperatorExpressionDefaultContext } from "./esql_parser"; +import { ArithmeticUnaryContext } from "./esql_parser"; +import { ArithmeticBinaryContext } from "./esql_parser"; import { SingleStatementContext } from "./esql_parser"; import { QueryContext } from "./esql_parser"; import { SourceCommandContext } from "./esql_parser"; import { ProcessingCommandContext } from "./esql_parser"; -import { EnrichCommandContext } from "./esql_parser"; -import { EnrichWithClauseContext } from "./esql_parser"; -import { MvExpandCommandContext } from "./esql_parser"; import { WhereCommandContext } from "./esql_parser"; -import { WhereBooleanExpressionContext } from "./esql_parser"; import { BooleanExpressionContext } from "./esql_parser"; import { RegexBooleanExpressionContext } from "./esql_parser"; import { ValueExpressionContext } from "./esql_parser"; -import { ComparisonContext } from "./esql_parser"; -import { MathFnContext } from "./esql_parser"; -import { MathEvalFnContext } from "./esql_parser"; -import { DateExpressionContext } from "./esql_parser"; import { OperatorExpressionContext } from "./esql_parser"; import { PrimaryExpressionContext } from "./esql_parser"; +import { FunctionExpressionContext } from "./esql_parser"; import { RowCommandContext } from "./esql_parser"; import { FieldsContext } from "./esql_parser"; import { FieldContext } from "./esql_parser"; -import { EnrichFieldIdentifierContext } from "./esql_parser"; -import { UserVariableContext } from "./esql_parser"; import { FromCommandContext } from "./esql_parser"; import { MetadataContext } from "./esql_parser"; import { EvalCommandContext } from "./esql_parser"; import { StatsCommandContext } from "./esql_parser"; +import { GroupingContext } from "./esql_parser"; import { SourceIdentifierContext } from "./esql_parser"; -import { EnrichIdentifierContext } from "./esql_parser"; -import { FunctionExpressionArgumentContext } from "./esql_parser"; -import { MathFunctionExpressionArgumentContext } from "./esql_parser"; import { QualifiedNameContext } from "./esql_parser"; -import { QualifiedNamesContext } from "./esql_parser"; import { IdentifierContext } from "./esql_parser"; -import { MathFunctionIdentifierContext } from "./esql_parser"; -import { FunctionIdentifierContext } from "./esql_parser"; import { ConstantContext } from "./esql_parser"; -import { NumericValueContext } from "./esql_parser"; import { LimitCommandContext } from "./esql_parser"; import { SortCommandContext } from "./esql_parser"; import { OrderExpressionContext } from "./esql_parser"; -import { ProjectCommandContext } from "./esql_parser"; import { KeepCommandContext } from "./esql_parser"; import { DropCommandContext } from "./esql_parser"; -import { RenameVariableContext } from "./esql_parser"; import { RenameCommandContext } from "./esql_parser"; import { RenameClauseContext } from "./esql_parser"; import { DissectCommandContext } from "./esql_parser"; import { GrokCommandContext } from "./esql_parser"; +import { MvExpandCommandContext } from "./esql_parser"; import { CommandOptionsContext } from "./esql_parser"; import { CommandOptionContext } from "./esql_parser"; import { BooleanValueContext } from "./esql_parser"; -import { NumberContext } from "./esql_parser"; +import { NumericValueContext } from "./esql_parser"; import { DecimalValueContext } from "./esql_parser"; import { IntegerValueContext } from "./esql_parser"; import { StringContext } from "./esql_parser"; import { ComparisonOperatorContext } from "./esql_parser"; -import { ExplainCommandContext } from "./esql_parser"; -import { SubqueryExpressionContext } from "./esql_parser"; import { ShowCommandContext } from "./esql_parser"; +import { EnrichCommandContext } from "./esql_parser"; +import { EnrichWithClauseContext } from "./esql_parser"; /** @@ -75,32 +84,240 @@ import { ShowCommandContext } from "./esql_parser"; * `esql_parser`. */ export interface esql_parserListener extends ParseTreeListener { + /** + * Enter a parse tree produced by the `valueExpressionDefault` + * labeled alternative in `esql_parser.valueExpression`. + * @param ctx the parse tree + */ + enterValueExpressionDefault?: (ctx: ValueExpressionDefaultContext) => void; + /** + * Exit a parse tree produced by the `valueExpressionDefault` + * labeled alternative in `esql_parser.valueExpression`. + * @param ctx the parse tree + */ + exitValueExpressionDefault?: (ctx: ValueExpressionDefaultContext) => void; + + /** + * Enter a parse tree produced by the `comparison` + * labeled alternative in `esql_parser.valueExpression`. + * @param ctx the parse tree + */ + enterComparison?: (ctx: ComparisonContext) => void; + /** + * Exit a parse tree produced by the `comparison` + * labeled alternative in `esql_parser.valueExpression`. + * @param ctx the parse tree + */ + exitComparison?: (ctx: ComparisonContext) => void; + + /** + * Enter a parse tree produced by the `nullLiteral` + * labeled alternative in `esql_parser.constant`. + * @param ctx the parse tree + */ + enterNullLiteral?: (ctx: NullLiteralContext) => void; + /** + * Exit a parse tree produced by the `nullLiteral` + * labeled alternative in `esql_parser.constant`. + * @param ctx the parse tree + */ + exitNullLiteral?: (ctx: NullLiteralContext) => void; + + /** + * Enter a parse tree produced by the `qualifiedIntegerLiteral` + * labeled alternative in `esql_parser.constant`. + * @param ctx the parse tree + */ + enterQualifiedIntegerLiteral?: (ctx: QualifiedIntegerLiteralContext) => void; + /** + * Exit a parse tree produced by the `qualifiedIntegerLiteral` + * labeled alternative in `esql_parser.constant`. + * @param ctx the parse tree + */ + exitQualifiedIntegerLiteral?: (ctx: QualifiedIntegerLiteralContext) => void; + /** * Enter a parse tree produced by the `decimalLiteral` - * labeled alternative in `esql_parser.number`. + * labeled alternative in `esql_parser.constant`. * @param ctx the parse tree */ enterDecimalLiteral?: (ctx: DecimalLiteralContext) => void; /** * Exit a parse tree produced by the `decimalLiteral` - * labeled alternative in `esql_parser.number`. + * labeled alternative in `esql_parser.constant`. * @param ctx the parse tree */ exitDecimalLiteral?: (ctx: DecimalLiteralContext) => void; /** * Enter a parse tree produced by the `integerLiteral` - * labeled alternative in `esql_parser.number`. + * labeled alternative in `esql_parser.constant`. * @param ctx the parse tree */ enterIntegerLiteral?: (ctx: IntegerLiteralContext) => void; /** * Exit a parse tree produced by the `integerLiteral` - * labeled alternative in `esql_parser.number`. + * labeled alternative in `esql_parser.constant`. * @param ctx the parse tree */ exitIntegerLiteral?: (ctx: IntegerLiteralContext) => void; + /** + * Enter a parse tree produced by the `booleanLiteral` + * labeled alternative in `esql_parser.constant`. + * @param ctx the parse tree + */ + enterBooleanLiteral?: (ctx: BooleanLiteralContext) => void; + /** + * Exit a parse tree produced by the `booleanLiteral` + * labeled alternative in `esql_parser.constant`. + * @param ctx the parse tree + */ + exitBooleanLiteral?: (ctx: BooleanLiteralContext) => void; + + /** + * Enter a parse tree produced by the `inputParam` + * labeled alternative in `esql_parser.constant`. + * @param ctx the parse tree + */ + enterInputParam?: (ctx: InputParamContext) => void; + /** + * Exit a parse tree produced by the `inputParam` + * labeled alternative in `esql_parser.constant`. + * @param ctx the parse tree + */ + exitInputParam?: (ctx: InputParamContext) => void; + + /** + * Enter a parse tree produced by the `stringLiteral` + * labeled alternative in `esql_parser.constant`. + * @param ctx the parse tree + */ + enterStringLiteral?: (ctx: StringLiteralContext) => void; + /** + * Exit a parse tree produced by the `stringLiteral` + * labeled alternative in `esql_parser.constant`. + * @param ctx the parse tree + */ + exitStringLiteral?: (ctx: StringLiteralContext) => void; + + /** + * Enter a parse tree produced by the `numericArrayLiteral` + * labeled alternative in `esql_parser.constant`. + * @param ctx the parse tree + */ + enterNumericArrayLiteral?: (ctx: NumericArrayLiteralContext) => void; + /** + * Exit a parse tree produced by the `numericArrayLiteral` + * labeled alternative in `esql_parser.constant`. + * @param ctx the parse tree + */ + exitNumericArrayLiteral?: (ctx: NumericArrayLiteralContext) => void; + + /** + * Enter a parse tree produced by the `booleanArrayLiteral` + * labeled alternative in `esql_parser.constant`. + * @param ctx the parse tree + */ + enterBooleanArrayLiteral?: (ctx: BooleanArrayLiteralContext) => void; + /** + * Exit a parse tree produced by the `booleanArrayLiteral` + * labeled alternative in `esql_parser.constant`. + * @param ctx the parse tree + */ + exitBooleanArrayLiteral?: (ctx: BooleanArrayLiteralContext) => void; + + /** + * Enter a parse tree produced by the `stringArrayLiteral` + * labeled alternative in `esql_parser.constant`. + * @param ctx the parse tree + */ + enterStringArrayLiteral?: (ctx: StringArrayLiteralContext) => void; + /** + * Exit a parse tree produced by the `stringArrayLiteral` + * labeled alternative in `esql_parser.constant`. + * @param ctx the parse tree + */ + exitStringArrayLiteral?: (ctx: StringArrayLiteralContext) => void; + + /** + * Enter a parse tree produced by the `showInfo` + * labeled alternative in `esql_parser.showCommand`. + * @param ctx the parse tree + */ + enterShowInfo?: (ctx: ShowInfoContext) => void; + /** + * Exit a parse tree produced by the `showInfo` + * labeled alternative in `esql_parser.showCommand`. + * @param ctx the parse tree + */ + exitShowInfo?: (ctx: ShowInfoContext) => void; + + /** + * Enter a parse tree produced by the `showFunctions` + * labeled alternative in `esql_parser.showCommand`. + * @param ctx the parse tree + */ + enterShowFunctions?: (ctx: ShowFunctionsContext) => void; + /** + * Exit a parse tree produced by the `showFunctions` + * labeled alternative in `esql_parser.showCommand`. + * @param ctx the parse tree + */ + exitShowFunctions?: (ctx: ShowFunctionsContext) => void; + + /** + * Enter a parse tree produced by the `constantDefault` + * labeled alternative in `esql_parser.primaryExpression`. + * @param ctx the parse tree + */ + enterConstantDefault?: (ctx: ConstantDefaultContext) => void; + /** + * Exit a parse tree produced by the `constantDefault` + * labeled alternative in `esql_parser.primaryExpression`. + * @param ctx the parse tree + */ + exitConstantDefault?: (ctx: ConstantDefaultContext) => void; + + /** + * Enter a parse tree produced by the `dereference` + * labeled alternative in `esql_parser.primaryExpression`. + * @param ctx the parse tree + */ + enterDereference?: (ctx: DereferenceContext) => void; + /** + * Exit a parse tree produced by the `dereference` + * labeled alternative in `esql_parser.primaryExpression`. + * @param ctx the parse tree + */ + exitDereference?: (ctx: DereferenceContext) => void; + + /** + * Enter a parse tree produced by the `function` + * labeled alternative in `esql_parser.primaryExpression`. + * @param ctx the parse tree + */ + enterFunction?: (ctx: FunctionContext) => void; + /** + * Exit a parse tree produced by the `function` + * labeled alternative in `esql_parser.primaryExpression`. + * @param ctx the parse tree + */ + exitFunction?: (ctx: FunctionContext) => void; + + /** + * Enter a parse tree produced by the `parenthesizedExpression` + * labeled alternative in `esql_parser.primaryExpression`. + * @param ctx the parse tree + */ + enterParenthesizedExpression?: (ctx: ParenthesizedExpressionContext) => void; + /** + * Exit a parse tree produced by the `parenthesizedExpression` + * labeled alternative in `esql_parser.primaryExpression`. + * @param ctx the parse tree + */ + exitParenthesizedExpression?: (ctx: ParenthesizedExpressionContext) => void; + /** * Enter a parse tree produced by the `singleCommandQuery` * labeled alternative in `esql_parser.query`. @@ -128,180 +345,209 @@ export interface esql_parserListener extends ParseTreeListener { exitCompositeQuery?: (ctx: CompositeQueryContext) => void; /** - * Enter a parse tree produced by `esql_parser.singleStatement`. + * Enter a parse tree produced by the `logicalNot` + * labeled alternative in `esql_parser.booleanExpression`. * @param ctx the parse tree */ - enterSingleStatement?: (ctx: SingleStatementContext) => void; + enterLogicalNot?: (ctx: LogicalNotContext) => void; /** - * Exit a parse tree produced by `esql_parser.singleStatement`. + * Exit a parse tree produced by the `logicalNot` + * labeled alternative in `esql_parser.booleanExpression`. * @param ctx the parse tree */ - exitSingleStatement?: (ctx: SingleStatementContext) => void; + exitLogicalNot?: (ctx: LogicalNotContext) => void; /** - * Enter a parse tree produced by `esql_parser.query`. + * Enter a parse tree produced by the `booleanDefault` + * labeled alternative in `esql_parser.booleanExpression`. * @param ctx the parse tree */ - enterQuery?: (ctx: QueryContext) => void; + enterBooleanDefault?: (ctx: BooleanDefaultContext) => void; /** - * Exit a parse tree produced by `esql_parser.query`. + * Exit a parse tree produced by the `booleanDefault` + * labeled alternative in `esql_parser.booleanExpression`. * @param ctx the parse tree */ - exitQuery?: (ctx: QueryContext) => void; + exitBooleanDefault?: (ctx: BooleanDefaultContext) => void; /** - * Enter a parse tree produced by `esql_parser.sourceCommand`. + * Enter a parse tree produced by the `regexExpression` + * labeled alternative in `esql_parser.booleanExpression`. * @param ctx the parse tree */ - enterSourceCommand?: (ctx: SourceCommandContext) => void; + enterRegexExpression?: (ctx: RegexExpressionContext) => void; /** - * Exit a parse tree produced by `esql_parser.sourceCommand`. + * Exit a parse tree produced by the `regexExpression` + * labeled alternative in `esql_parser.booleanExpression`. * @param ctx the parse tree */ - exitSourceCommand?: (ctx: SourceCommandContext) => void; + exitRegexExpression?: (ctx: RegexExpressionContext) => void; /** - * Enter a parse tree produced by `esql_parser.processingCommand`. + * Enter a parse tree produced by the `logicalBinary` + * labeled alternative in `esql_parser.booleanExpression`. * @param ctx the parse tree */ - enterProcessingCommand?: (ctx: ProcessingCommandContext) => void; + enterLogicalBinary?: (ctx: LogicalBinaryContext) => void; /** - * Exit a parse tree produced by `esql_parser.processingCommand`. + * Exit a parse tree produced by the `logicalBinary` + * labeled alternative in `esql_parser.booleanExpression`. * @param ctx the parse tree */ - exitProcessingCommand?: (ctx: ProcessingCommandContext) => void; + exitLogicalBinary?: (ctx: LogicalBinaryContext) => void; /** - * Enter a parse tree produced by `esql_parser.enrichCommand`. + * Enter a parse tree produced by the `logicalIn` + * labeled alternative in `esql_parser.booleanExpression`. * @param ctx the parse tree */ - enterEnrichCommand?: (ctx: EnrichCommandContext) => void; + enterLogicalIn?: (ctx: LogicalInContext) => void; /** - * Exit a parse tree produced by `esql_parser.enrichCommand`. + * Exit a parse tree produced by the `logicalIn` + * labeled alternative in `esql_parser.booleanExpression`. * @param ctx the parse tree */ - exitEnrichCommand?: (ctx: EnrichCommandContext) => void; + exitLogicalIn?: (ctx: LogicalInContext) => void; /** - * Enter a parse tree produced by `esql_parser.enrichWithClause`. + * Enter a parse tree produced by the `isNull` + * labeled alternative in `esql_parser.booleanExpression`. * @param ctx the parse tree */ - enterEnrichWithClause?: (ctx: EnrichWithClauseContext) => void; + enterIsNull?: (ctx: IsNullContext) => void; /** - * Exit a parse tree produced by `esql_parser.enrichWithClause`. + * Exit a parse tree produced by the `isNull` + * labeled alternative in `esql_parser.booleanExpression`. * @param ctx the parse tree */ - exitEnrichWithClause?: (ctx: EnrichWithClauseContext) => void; + exitIsNull?: (ctx: IsNullContext) => void; /** - * Enter a parse tree produced by `esql_parser.mvExpandCommand`. + * Enter a parse tree produced by the `operatorExpressionDefault` + * labeled alternative in `esql_parser.operatorExpression`. * @param ctx the parse tree */ - enterMvExpandCommand?: (ctx: MvExpandCommandContext) => void; + enterOperatorExpressionDefault?: (ctx: OperatorExpressionDefaultContext) => void; /** - * Exit a parse tree produced by `esql_parser.mvExpandCommand`. + * Exit a parse tree produced by the `operatorExpressionDefault` + * labeled alternative in `esql_parser.operatorExpression`. * @param ctx the parse tree */ - exitMvExpandCommand?: (ctx: MvExpandCommandContext) => void; + exitOperatorExpressionDefault?: (ctx: OperatorExpressionDefaultContext) => void; /** - * Enter a parse tree produced by `esql_parser.whereCommand`. + * Enter a parse tree produced by the `arithmeticUnary` + * labeled alternative in `esql_parser.operatorExpression`. * @param ctx the parse tree */ - enterWhereCommand?: (ctx: WhereCommandContext) => void; + enterArithmeticUnary?: (ctx: ArithmeticUnaryContext) => void; /** - * Exit a parse tree produced by `esql_parser.whereCommand`. + * Exit a parse tree produced by the `arithmeticUnary` + * labeled alternative in `esql_parser.operatorExpression`. * @param ctx the parse tree */ - exitWhereCommand?: (ctx: WhereCommandContext) => void; + exitArithmeticUnary?: (ctx: ArithmeticUnaryContext) => void; /** - * Enter a parse tree produced by `esql_parser.whereBooleanExpression`. + * Enter a parse tree produced by the `arithmeticBinary` + * labeled alternative in `esql_parser.operatorExpression`. * @param ctx the parse tree */ - enterWhereBooleanExpression?: (ctx: WhereBooleanExpressionContext) => void; + enterArithmeticBinary?: (ctx: ArithmeticBinaryContext) => void; /** - * Exit a parse tree produced by `esql_parser.whereBooleanExpression`. + * Exit a parse tree produced by the `arithmeticBinary` + * labeled alternative in `esql_parser.operatorExpression`. * @param ctx the parse tree */ - exitWhereBooleanExpression?: (ctx: WhereBooleanExpressionContext) => void; + exitArithmeticBinary?: (ctx: ArithmeticBinaryContext) => void; /** - * Enter a parse tree produced by `esql_parser.booleanExpression`. + * Enter a parse tree produced by `esql_parser.singleStatement`. * @param ctx the parse tree */ - enterBooleanExpression?: (ctx: BooleanExpressionContext) => void; + enterSingleStatement?: (ctx: SingleStatementContext) => void; /** - * Exit a parse tree produced by `esql_parser.booleanExpression`. + * Exit a parse tree produced by `esql_parser.singleStatement`. * @param ctx the parse tree */ - exitBooleanExpression?: (ctx: BooleanExpressionContext) => void; + exitSingleStatement?: (ctx: SingleStatementContext) => void; /** - * Enter a parse tree produced by `esql_parser.regexBooleanExpression`. + * Enter a parse tree produced by `esql_parser.query`. * @param ctx the parse tree */ - enterRegexBooleanExpression?: (ctx: RegexBooleanExpressionContext) => void; + enterQuery?: (ctx: QueryContext) => void; /** - * Exit a parse tree produced by `esql_parser.regexBooleanExpression`. + * Exit a parse tree produced by `esql_parser.query`. * @param ctx the parse tree */ - exitRegexBooleanExpression?: (ctx: RegexBooleanExpressionContext) => void; + exitQuery?: (ctx: QueryContext) => void; /** - * Enter a parse tree produced by `esql_parser.valueExpression`. + * Enter a parse tree produced by `esql_parser.sourceCommand`. * @param ctx the parse tree */ - enterValueExpression?: (ctx: ValueExpressionContext) => void; + enterSourceCommand?: (ctx: SourceCommandContext) => void; /** - * Exit a parse tree produced by `esql_parser.valueExpression`. + * Exit a parse tree produced by `esql_parser.sourceCommand`. * @param ctx the parse tree */ - exitValueExpression?: (ctx: ValueExpressionContext) => void; + exitSourceCommand?: (ctx: SourceCommandContext) => void; /** - * Enter a parse tree produced by `esql_parser.comparison`. + * Enter a parse tree produced by `esql_parser.processingCommand`. * @param ctx the parse tree */ - enterComparison?: (ctx: ComparisonContext) => void; + enterProcessingCommand?: (ctx: ProcessingCommandContext) => void; /** - * Exit a parse tree produced by `esql_parser.comparison`. + * Exit a parse tree produced by `esql_parser.processingCommand`. * @param ctx the parse tree */ - exitComparison?: (ctx: ComparisonContext) => void; + exitProcessingCommand?: (ctx: ProcessingCommandContext) => void; /** - * Enter a parse tree produced by `esql_parser.mathFn`. + * Enter a parse tree produced by `esql_parser.whereCommand`. + * @param ctx the parse tree + */ + enterWhereCommand?: (ctx: WhereCommandContext) => void; + /** + * Exit a parse tree produced by `esql_parser.whereCommand`. + * @param ctx the parse tree + */ + exitWhereCommand?: (ctx: WhereCommandContext) => void; + + /** + * Enter a parse tree produced by `esql_parser.booleanExpression`. * @param ctx the parse tree */ - enterMathFn?: (ctx: MathFnContext) => void; + enterBooleanExpression?: (ctx: BooleanExpressionContext) => void; /** - * Exit a parse tree produced by `esql_parser.mathFn`. + * Exit a parse tree produced by `esql_parser.booleanExpression`. * @param ctx the parse tree */ - exitMathFn?: (ctx: MathFnContext) => void; + exitBooleanExpression?: (ctx: BooleanExpressionContext) => void; /** - * Enter a parse tree produced by `esql_parser.mathEvalFn`. + * Enter a parse tree produced by `esql_parser.regexBooleanExpression`. * @param ctx the parse tree */ - enterMathEvalFn?: (ctx: MathEvalFnContext) => void; + enterRegexBooleanExpression?: (ctx: RegexBooleanExpressionContext) => void; /** - * Exit a parse tree produced by `esql_parser.mathEvalFn`. + * Exit a parse tree produced by `esql_parser.regexBooleanExpression`. * @param ctx the parse tree */ - exitMathEvalFn?: (ctx: MathEvalFnContext) => void; + exitRegexBooleanExpression?: (ctx: RegexBooleanExpressionContext) => void; /** - * Enter a parse tree produced by `esql_parser.dateExpression`. + * Enter a parse tree produced by `esql_parser.valueExpression`. * @param ctx the parse tree */ - enterDateExpression?: (ctx: DateExpressionContext) => void; + enterValueExpression?: (ctx: ValueExpressionContext) => void; /** - * Exit a parse tree produced by `esql_parser.dateExpression`. + * Exit a parse tree produced by `esql_parser.valueExpression`. * @param ctx the parse tree */ - exitDateExpression?: (ctx: DateExpressionContext) => void; + exitValueExpression?: (ctx: ValueExpressionContext) => void; /** * Enter a parse tree produced by `esql_parser.operatorExpression`. @@ -325,6 +571,17 @@ export interface esql_parserListener extends ParseTreeListener { */ exitPrimaryExpression?: (ctx: PrimaryExpressionContext) => void; + /** + * Enter a parse tree produced by `esql_parser.functionExpression`. + * @param ctx the parse tree + */ + enterFunctionExpression?: (ctx: FunctionExpressionContext) => void; + /** + * Exit a parse tree produced by `esql_parser.functionExpression`. + * @param ctx the parse tree + */ + exitFunctionExpression?: (ctx: FunctionExpressionContext) => void; + /** * Enter a parse tree produced by `esql_parser.rowCommand`. * @param ctx the parse tree @@ -358,28 +615,6 @@ export interface esql_parserListener extends ParseTreeListener { */ exitField?: (ctx: FieldContext) => void; - /** - * Enter a parse tree produced by `esql_parser.enrichFieldIdentifier`. - * @param ctx the parse tree - */ - enterEnrichFieldIdentifier?: (ctx: EnrichFieldIdentifierContext) => void; - /** - * Exit a parse tree produced by `esql_parser.enrichFieldIdentifier`. - * @param ctx the parse tree - */ - exitEnrichFieldIdentifier?: (ctx: EnrichFieldIdentifierContext) => void; - - /** - * Enter a parse tree produced by `esql_parser.userVariable`. - * @param ctx the parse tree - */ - enterUserVariable?: (ctx: UserVariableContext) => void; - /** - * Exit a parse tree produced by `esql_parser.userVariable`. - * @param ctx the parse tree - */ - exitUserVariable?: (ctx: UserVariableContext) => void; - /** * Enter a parse tree produced by `esql_parser.fromCommand`. * @param ctx the parse tree @@ -425,48 +660,26 @@ export interface esql_parserListener extends ParseTreeListener { exitStatsCommand?: (ctx: StatsCommandContext) => void; /** - * Enter a parse tree produced by `esql_parser.sourceIdentifier`. - * @param ctx the parse tree - */ - enterSourceIdentifier?: (ctx: SourceIdentifierContext) => void; - /** - * Exit a parse tree produced by `esql_parser.sourceIdentifier`. - * @param ctx the parse tree - */ - exitSourceIdentifier?: (ctx: SourceIdentifierContext) => void; - - /** - * Enter a parse tree produced by `esql_parser.enrichIdentifier`. - * @param ctx the parse tree - */ - enterEnrichIdentifier?: (ctx: EnrichIdentifierContext) => void; - /** - * Exit a parse tree produced by `esql_parser.enrichIdentifier`. - * @param ctx the parse tree - */ - exitEnrichIdentifier?: (ctx: EnrichIdentifierContext) => void; - - /** - * Enter a parse tree produced by `esql_parser.functionExpressionArgument`. + * Enter a parse tree produced by `esql_parser.grouping`. * @param ctx the parse tree */ - enterFunctionExpressionArgument?: (ctx: FunctionExpressionArgumentContext) => void; + enterGrouping?: (ctx: GroupingContext) => void; /** - * Exit a parse tree produced by `esql_parser.functionExpressionArgument`. + * Exit a parse tree produced by `esql_parser.grouping`. * @param ctx the parse tree */ - exitFunctionExpressionArgument?: (ctx: FunctionExpressionArgumentContext) => void; + exitGrouping?: (ctx: GroupingContext) => void; /** - * Enter a parse tree produced by `esql_parser.mathFunctionExpressionArgument`. + * Enter a parse tree produced by `esql_parser.sourceIdentifier`. * @param ctx the parse tree */ - enterMathFunctionExpressionArgument?: (ctx: MathFunctionExpressionArgumentContext) => void; + enterSourceIdentifier?: (ctx: SourceIdentifierContext) => void; /** - * Exit a parse tree produced by `esql_parser.mathFunctionExpressionArgument`. + * Exit a parse tree produced by `esql_parser.sourceIdentifier`. * @param ctx the parse tree */ - exitMathFunctionExpressionArgument?: (ctx: MathFunctionExpressionArgumentContext) => void; + exitSourceIdentifier?: (ctx: SourceIdentifierContext) => void; /** * Enter a parse tree produced by `esql_parser.qualifiedName`. @@ -479,17 +692,6 @@ export interface esql_parserListener extends ParseTreeListener { */ exitQualifiedName?: (ctx: QualifiedNameContext) => void; - /** - * Enter a parse tree produced by `esql_parser.qualifiedNames`. - * @param ctx the parse tree - */ - enterQualifiedNames?: (ctx: QualifiedNamesContext) => void; - /** - * Exit a parse tree produced by `esql_parser.qualifiedNames`. - * @param ctx the parse tree - */ - exitQualifiedNames?: (ctx: QualifiedNamesContext) => void; - /** * Enter a parse tree produced by `esql_parser.identifier`. * @param ctx the parse tree @@ -501,28 +703,6 @@ export interface esql_parserListener extends ParseTreeListener { */ exitIdentifier?: (ctx: IdentifierContext) => void; - /** - * Enter a parse tree produced by `esql_parser.mathFunctionIdentifier`. - * @param ctx the parse tree - */ - enterMathFunctionIdentifier?: (ctx: MathFunctionIdentifierContext) => void; - /** - * Exit a parse tree produced by `esql_parser.mathFunctionIdentifier`. - * @param ctx the parse tree - */ - exitMathFunctionIdentifier?: (ctx: MathFunctionIdentifierContext) => void; - - /** - * Enter a parse tree produced by `esql_parser.functionIdentifier`. - * @param ctx the parse tree - */ - enterFunctionIdentifier?: (ctx: FunctionIdentifierContext) => void; - /** - * Exit a parse tree produced by `esql_parser.functionIdentifier`. - * @param ctx the parse tree - */ - exitFunctionIdentifier?: (ctx: FunctionIdentifierContext) => void; - /** * Enter a parse tree produced by `esql_parser.constant`. * @param ctx the parse tree @@ -534,17 +714,6 @@ export interface esql_parserListener extends ParseTreeListener { */ exitConstant?: (ctx: ConstantContext) => void; - /** - * Enter a parse tree produced by `esql_parser.numericValue`. - * @param ctx the parse tree - */ - enterNumericValue?: (ctx: NumericValueContext) => void; - /** - * Exit a parse tree produced by `esql_parser.numericValue`. - * @param ctx the parse tree - */ - exitNumericValue?: (ctx: NumericValueContext) => void; - /** * Enter a parse tree produced by `esql_parser.limitCommand`. * @param ctx the parse tree @@ -578,17 +747,6 @@ export interface esql_parserListener extends ParseTreeListener { */ exitOrderExpression?: (ctx: OrderExpressionContext) => void; - /** - * Enter a parse tree produced by `esql_parser.projectCommand`. - * @param ctx the parse tree - */ - enterProjectCommand?: (ctx: ProjectCommandContext) => void; - /** - * Exit a parse tree produced by `esql_parser.projectCommand`. - * @param ctx the parse tree - */ - exitProjectCommand?: (ctx: ProjectCommandContext) => void; - /** * Enter a parse tree produced by `esql_parser.keepCommand`. * @param ctx the parse tree @@ -611,17 +769,6 @@ export interface esql_parserListener extends ParseTreeListener { */ exitDropCommand?: (ctx: DropCommandContext) => void; - /** - * Enter a parse tree produced by `esql_parser.renameVariable`. - * @param ctx the parse tree - */ - enterRenameVariable?: (ctx: RenameVariableContext) => void; - /** - * Exit a parse tree produced by `esql_parser.renameVariable`. - * @param ctx the parse tree - */ - exitRenameVariable?: (ctx: RenameVariableContext) => void; - /** * Enter a parse tree produced by `esql_parser.renameCommand`. * @param ctx the parse tree @@ -666,6 +813,17 @@ export interface esql_parserListener extends ParseTreeListener { */ exitGrokCommand?: (ctx: GrokCommandContext) => void; + /** + * Enter a parse tree produced by `esql_parser.mvExpandCommand`. + * @param ctx the parse tree + */ + enterMvExpandCommand?: (ctx: MvExpandCommandContext) => void; + /** + * Exit a parse tree produced by `esql_parser.mvExpandCommand`. + * @param ctx the parse tree + */ + exitMvExpandCommand?: (ctx: MvExpandCommandContext) => void; + /** * Enter a parse tree produced by `esql_parser.commandOptions`. * @param ctx the parse tree @@ -700,15 +858,15 @@ export interface esql_parserListener extends ParseTreeListener { exitBooleanValue?: (ctx: BooleanValueContext) => void; /** - * Enter a parse tree produced by `esql_parser.number`. + * Enter a parse tree produced by `esql_parser.numericValue`. * @param ctx the parse tree */ - enterNumber?: (ctx: NumberContext) => void; + enterNumericValue?: (ctx: NumericValueContext) => void; /** - * Exit a parse tree produced by `esql_parser.number`. + * Exit a parse tree produced by `esql_parser.numericValue`. * @param ctx the parse tree */ - exitNumber?: (ctx: NumberContext) => void; + exitNumericValue?: (ctx: NumericValueContext) => void; /** * Enter a parse tree produced by `esql_parser.decimalValue`. @@ -755,36 +913,36 @@ export interface esql_parserListener extends ParseTreeListener { exitComparisonOperator?: (ctx: ComparisonOperatorContext) => void; /** - * Enter a parse tree produced by `esql_parser.explainCommand`. + * Enter a parse tree produced by `esql_parser.showCommand`. * @param ctx the parse tree */ - enterExplainCommand?: (ctx: ExplainCommandContext) => void; + enterShowCommand?: (ctx: ShowCommandContext) => void; /** - * Exit a parse tree produced by `esql_parser.explainCommand`. + * Exit a parse tree produced by `esql_parser.showCommand`. * @param ctx the parse tree */ - exitExplainCommand?: (ctx: ExplainCommandContext) => void; + exitShowCommand?: (ctx: ShowCommandContext) => void; /** - * Enter a parse tree produced by `esql_parser.subqueryExpression`. + * Enter a parse tree produced by `esql_parser.enrichCommand`. * @param ctx the parse tree */ - enterSubqueryExpression?: (ctx: SubqueryExpressionContext) => void; + enterEnrichCommand?: (ctx: EnrichCommandContext) => void; /** - * Exit a parse tree produced by `esql_parser.subqueryExpression`. + * Exit a parse tree produced by `esql_parser.enrichCommand`. * @param ctx the parse tree */ - exitSubqueryExpression?: (ctx: SubqueryExpressionContext) => void; + exitEnrichCommand?: (ctx: EnrichCommandContext) => void; /** - * Enter a parse tree produced by `esql_parser.showCommand`. + * Enter a parse tree produced by `esql_parser.enrichWithClause`. * @param ctx the parse tree */ - enterShowCommand?: (ctx: ShowCommandContext) => void; + enterEnrichWithClause?: (ctx: EnrichWithClauseContext) => void; /** - * Exit a parse tree produced by `esql_parser.showCommand`. + * Exit a parse tree produced by `esql_parser.enrichWithClause`. * @param ctx the parse tree */ - exitShowCommand?: (ctx: ShowCommandContext) => void; + exitEnrichWithClause?: (ctx: EnrichWithClauseContext) => void; } diff --git a/packages/kbn-monaco/src/esql/index.ts b/packages/kbn-monaco/src/esql/index.ts index 46ae9fe7f6bcb..a3b5f3bec666d 100644 --- a/packages/kbn-monaco/src/esql/index.ts +++ b/packages/kbn-monaco/src/esql/index.ts @@ -8,5 +8,5 @@ export { ESQL_LANG_ID, ESQL_THEME_ID } from './lib/constants'; export { ESQLLang } from './language'; -export type { ESQLCustomAutocompleteCallbacks } from './lib/autocomplete/types'; +export type { ESQLCallbacks } from './lib/ast/shared/types'; export { buildESQlTheme } from './lib/monaco/esql_theme'; diff --git a/packages/kbn-monaco/src/esql/language.ts b/packages/kbn-monaco/src/esql/language.ts index cca4cb3718f06..956baa4405e0a 100644 --- a/packages/kbn-monaco/src/esql/language.ts +++ b/packages/kbn-monaco/src/esql/language.ts @@ -15,12 +15,12 @@ import type { ESQLWorker } from './worker/esql_worker'; import { DiagnosticsAdapter } from '../common/diagnostics_adapter'; import { WorkerProxyService } from '../common/worker_proxy'; -import { ESQLCompletionAdapter } from './lib/monaco/esql_completion_provider'; -import type { ESQLCustomAutocompleteCallbacks } from './lib/autocomplete/types'; +import type { ESQLCallbacks } from './lib/ast/shared/types'; +import { ESQLAstAdapter } from './lib/monaco/esql_ast_provider'; const workerProxyService = new WorkerProxyService(); -export const ESQLLang: CustomLangModuleType = { +export const ESQLLang: CustomLangModuleType = { ID: ESQL_LANG_ID, async onLanguage() { const { ESQLTokensProvider } = await import('./lib/monaco'); @@ -29,12 +29,18 @@ export const ESQLLang: CustomLangModuleType = { monaco.languages.setTokensProvider(ESQL_LANG_ID, new ESQLTokensProvider()); + // handle syntax errors via the diagnostic adapter + // but then enrich them via the separate validate function new DiagnosticsAdapter(ESQL_LANG_ID, (...uris) => workerProxyService.getWorker(uris)); }, languageConfiguration: { - brackets: [['(', ')']], + brackets: [ + ['(', ')'], + ['[', ']'], + ], autoClosingPairs: [ { open: '(', close: ')' }, + { open: '[', close: ']' }, { open: `'`, close: `'` }, { open: '"', close: '"' }, ], @@ -44,8 +50,65 @@ export const ESQLLang: CustomLangModuleType = { { open: '"', close: '"' }, ], }, - - getSuggestionProvider(callbacks?: ESQLCustomAutocompleteCallbacks) { - return new ESQLCompletionAdapter((...uris) => workerProxyService.getWorker(uris), callbacks); + validate: async (model: monaco.editor.ITextModel, code: string, callbacks?: ESQLCallbacks) => { + const astAdapter = new ESQLAstAdapter( + (...uris) => workerProxyService.getWorker(uris), + callbacks + ); + return await astAdapter.validate(model, code); + }, + getSignatureProvider: (callbacks?: ESQLCallbacks): monaco.languages.SignatureHelpProvider => { + return { + signatureHelpTriggerCharacters: [' ', '('], + async provideSignatureHelp( + model: monaco.editor.ITextModel, + position: monaco.Position, + _token: monaco.CancellationToken, + context: monaco.languages.SignatureHelpContext + ) { + const astAdapter = new ESQLAstAdapter( + (...uris) => workerProxyService.getWorker(uris), + callbacks + ); + return astAdapter.suggestSignature(model, position, context); + }, + }; + }, + getHoverProvider: (callbacks?: ESQLCallbacks): monaco.languages.HoverProvider => { + return { + async provideHover( + model: monaco.editor.ITextModel, + position: monaco.Position, + token: monaco.CancellationToken + ) { + const astAdapter = new ESQLAstAdapter( + (...uris) => workerProxyService.getWorker(uris), + callbacks + ); + return astAdapter.getHover(model, position, token); + }, + }; + }, + getSuggestionProvider: (callbacks?: ESQLCallbacks): monaco.languages.CompletionItemProvider => { + return { + triggerCharacters: [',', '(', '=', ' ', ''], + async provideCompletionItems( + model: monaco.editor.ITextModel, + position: monaco.Position, + context: monaco.languages.CompletionContext + ): Promise { + const astAdapter = new ESQLAstAdapter( + (...uris) => workerProxyService.getWorker(uris), + callbacks + ); + const suggestionEntries = await astAdapter.autocomplete(model, position, context); + return { + suggestions: suggestionEntries.suggestions.map((suggestion) => ({ + ...suggestion, + range: undefined as unknown as monaco.IRange, + })), + }; + }, + }; }, }; diff --git a/packages/kbn-monaco/src/esql/lib/antlr_facade.ts b/packages/kbn-monaco/src/esql/lib/antlr_facade.ts index e6bf97e443140..ffa69ba2a8f4c 100644 --- a/packages/kbn-monaco/src/esql/lib/antlr_facade.ts +++ b/packages/kbn-monaco/src/esql/lib/antlr_facade.ts @@ -6,19 +6,17 @@ * Side Public License, v 1. */ -import { CommonTokenStream, CodePointCharStream } from 'antlr4ts'; +import { CommonTokenStream, type CodePointCharStream, type ANTLRErrorListener } from 'antlr4ts'; import { esql_lexer as ESQLLexer } from '../antlr/esql_lexer'; import { esql_parser as ESQLParser } from '../antlr/esql_parser'; import type { esql_parserListener as ESQLParserListener } from '../antlr/esql_parser_listener'; -import type { ANTLREErrorListener } from '../../common/error_listener'; - export const ROOT_STATEMENT = 'singleStatement'; export const getParser = ( inputStream: CodePointCharStream, - errorListener: ANTLREErrorListener, + errorListener: ANTLRErrorListener, parseListener?: ESQLParserListener ) => { const lexer = getLexer(inputStream, errorListener); @@ -35,7 +33,10 @@ export const getParser = ( return parser; }; -export const getLexer = (inputStream: CodePointCharStream, errorListener: ANTLREErrorListener) => { +export const getLexer = ( + inputStream: CodePointCharStream, + errorListener: ANTLRErrorListener +) => { const lexer = new ESQLLexer(inputStream); lexer.removeErrorListeners(); diff --git a/packages/kbn-monaco/src/esql/lib/ast/ast_errors.ts b/packages/kbn-monaco/src/esql/lib/ast/ast_errors.ts new file mode 100644 index 0000000000000..01d16a46b2502 --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/ast/ast_errors.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { RecognitionException } from 'antlr4ts'; +import { esql_parser } from '../../antlr/esql_parser'; +import { getPosition } from './ast_position_utils'; + +function getExpectedSymbols(expectedTokens: RecognitionException['expectedTokens']) { + const tokenIds = expectedTokens?.toIntegerList().toArray() || []; + const list = []; + for (const tokenId of tokenIds) { + if (esql_parser.VOCABULARY.getSymbolicName(tokenId)) { + const symbol = esql_parser.VOCABULARY.getSymbolicName(tokenId); + list.push(symbol === 'EOF' ? `<${symbol}>` : symbol); + } + } + return list; +} + +export function createError(exception: RecognitionException) { + const token = exception.getOffendingToken(); + if (token) { + const expectedSymbols = getExpectedSymbols(exception.expectedTokens); + if ( + ['ASTERISK', 'UNQUOTED_IDENTIFIER', 'QUOTED_IDENTIFIER'].every( + (s, i) => expectedSymbols[i] === s + ) + ) { + return { + type: 'error' as const, + text: `Unknown column ${token.text}`, + location: getPosition(token), + }; + } + } + return { + type: 'error' as const, + text: token + ? `SyntaxError: expected {${getExpectedSymbols(exception.expectedTokens).join( + ', ' + )}} but found "${token.text}"` + : exception.message, + location: getPosition(token), + }; +} diff --git a/packages/kbn-monaco/src/esql/lib/ast/ast_factory.ts b/packages/kbn-monaco/src/esql/lib/ast/ast_factory.ts new file mode 100644 index 0000000000000..939d6d764f513 --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/ast/ast_factory.ts @@ -0,0 +1,249 @@ +/* + * Copyright 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 ShowInfoContext, + type ShowFunctionsContext, + type SingleStatementContext, + type RowCommandContext, + type FromCommandContext, + type EvalCommandContext, + type StatsCommandContext, + type LimitCommandContext, + type SortCommandContext, + type KeepCommandContext, + type DropCommandContext, + type RenameCommandContext, + type DissectCommandContext, + type GrokCommandContext, + type MvExpandCommandContext, + type ShowCommandContext, + type EnrichCommandContext, + type WhereCommandContext, + esql_parser, +} from '../../antlr/esql_parser'; +import { esql_parserListener as ESQLParserListener } from '../../antlr/esql_parser_listener'; +import { createCommand, createFunction, createOption, createLiteral } from './ast_helpers'; +import { getPosition } from './ast_position_utils'; +import { + collectAllSourceIdentifiers, + collectAllFieldsStatements, + visitByOption, + collectAllColumnIdentifiers, + visitRenameClauses, + visitDissect, + visitGrok, + collectBooleanExpression, + visitOrderExpression, + getPolicyName, + getMatchField, + getEnrichClauses, +} from './ast_walker'; +import type { ESQLAst } from './types'; + +export class AstListener implements ESQLParserListener { + private ast: ESQLAst = []; + + public getAst() { + return { ast: this.ast }; + } + + /** + * Exit a parse tree produced by the `showInfo` + * labeled alternative in `esql_parser.showCommand`. + * @param ctx the parse tree + */ + exitShowInfo(ctx: ShowInfoContext) { + const commandAst = createCommand('show', ctx); + + this.ast.push(commandAst); + commandAst.text = ctx.text; + commandAst?.args.push(createFunction('info', ctx, getPosition(ctx.INFO().symbol))); + } + + /** + * Exit a parse tree produced by the `showFunctions` + * labeled alternative in `esql_parser.showCommand`. + * @param ctx the parse tree + */ + exitShowFunctions(ctx: ShowFunctionsContext) { + const commandAst = createCommand('show', ctx); + this.ast.push(commandAst); + // update the text + commandAst.text = ctx.text; + commandAst?.args.push(createFunction('functions', ctx, getPosition(ctx.FUNCTIONS().symbol))); + } + + /** + * Enter a parse tree produced by `esql_parser.singleStatement`. + * @param ctx the parse tree + */ + enterSingleStatement(ctx: SingleStatementContext) { + this.ast = []; + } + + /** + * Exit a parse tree produced by `esql_parser.whereCommand`. + * @param ctx the parse tree + */ + exitWhereCommand(ctx: WhereCommandContext) { + const command = createCommand('where', ctx); + this.ast.push(command); + command.args.push(...collectBooleanExpression(ctx.booleanExpression())); + } + + /** + * Exit a parse tree produced by `esql_parser.rowCommand`. + * @param ctx the parse tree + */ + exitRowCommand(ctx: RowCommandContext) { + const command = createCommand('row', ctx); + this.ast.push(command); + command.args.push(...collectAllFieldsStatements(ctx.fields())); + } + + /** + * Exit a parse tree produced by `esql_parser.fromCommand`. + * @param ctx the parse tree + */ + exitFromCommand(ctx: FromCommandContext) { + const commandAst = createCommand('from', ctx); + this.ast.push(commandAst); + commandAst.args.push(...collectAllSourceIdentifiers(ctx)); + const metadataContext = ctx.metadata(); + if (metadataContext) { + const option = createOption(metadataContext.METADATA().text.toLowerCase(), metadataContext); + commandAst.args.push(option); + // skip for the moment as there's no easy way to get meta fields right now + // option.args.push(...collectAllColumnIdentifiers(metadataContext)); + } + } + + /** + * Exit a parse tree produced by `esql_parser.evalCommand`. + * @param ctx the parse tree + */ + exitEvalCommand(ctx: EvalCommandContext) { + const commandAst = createCommand('eval', ctx); + this.ast.push(commandAst); + commandAst.args.push(...collectAllFieldsStatements(ctx.fields())); + } + + /** + * Exit a parse tree produced by `esql_parser.statsCommand`. + * @param ctx the parse tree + */ + exitStatsCommand(ctx: StatsCommandContext) { + const command = createCommand('stats', ctx); + this.ast.push(command); + command.args.push(...collectAllFieldsStatements(ctx.fields()), ...visitByOption(ctx)); + } + + /** + * Exit a parse tree produced by `esql_parser.limitCommand`. + * @param ctx the parse tree + */ + exitLimitCommand(ctx: LimitCommandContext) { + const command = createCommand('limit', ctx); + this.ast.push(command); + if (ctx.tryGetToken(esql_parser.INTEGER_LITERAL, 0)) { + const literal = createLiteral('number', ctx.INTEGER_LITERAL()); + if (literal) { + command.args.push(literal); + } + } + } + + /** + * Exit a parse tree produced by `esql_parser.sortCommand`. + * @param ctx the parse tree + */ + exitSortCommand(ctx: SortCommandContext) { + const command = createCommand('sort', ctx); + this.ast.push(command); + command.args.push(...visitOrderExpression(ctx.orderExpression())); + } + + /** + * Exit a parse tree produced by `esql_parser.keepCommand`. + * @param ctx the parse tree + */ + exitKeepCommand(ctx: KeepCommandContext) { + const command = createCommand('keep', ctx); + this.ast.push(command); + command.args.push(...collectAllColumnIdentifiers(ctx)); + } + + /** + * Exit a parse tree produced by `esql_parser.dropCommand`. + * @param ctx the parse tree + */ + exitDropCommand(ctx: DropCommandContext) { + const command = createCommand('drop', ctx); + this.ast.push(command); + command.args.push(...collectAllColumnIdentifiers(ctx)); + } + + /** + * Exit a parse tree produced by `esql_parser.renameCommand`. + * @param ctx the parse tree + */ + exitRenameCommand(ctx: RenameCommandContext) { + const command = createCommand('rename', ctx); + this.ast.push(command); + command.args.push(...visitRenameClauses(ctx.renameClause())); + } + + /** + * Exit a parse tree produced by `esql_parser.dissectCommand`. + * @param ctx the parse tree + */ + exitDissectCommand(ctx: DissectCommandContext) { + const command = createCommand('dissect', ctx); + this.ast.push(command); + command.args.push(...visitDissect(ctx)); + } + + /** + * Exit a parse tree produced by `esql_parser.grokCommand`. + * @param ctx the parse tree + */ + exitGrokCommand(ctx: GrokCommandContext) { + const command = createCommand('grok', ctx); + this.ast.push(command); + command.args.push(...visitGrok(ctx)); + } + + /** + * Exit a parse tree produced by `esql_parser.mvExpandCommand`. + * @param ctx the parse tree + */ + exitMvExpandCommand(ctx: MvExpandCommandContext) { + const command = createCommand('mv_expand', ctx); + this.ast.push(command); + command.args.push(...collectAllColumnIdentifiers(ctx)); + } + + /** + * Enter a parse tree produced by `esql_parser.showCommand`. + * @param ctx the parse tree + */ + enterShowCommand(ctx: ShowCommandContext) { + const command = createCommand('show', ctx); + this.ast.push(command); + } + /** + * Exit a parse tree produced by `esql_parser.enrichCommand`. + * @param ctx the parse tree + */ + exitEnrichCommand(ctx: EnrichCommandContext) { + const command = createCommand('enrich', ctx); + this.ast.push(command); + command.args.push(...getPolicyName(ctx), ...getMatchField(ctx), ...getEnrichClauses(ctx)); + } +} diff --git a/packages/kbn-monaco/src/esql/lib/ast/ast_helpers.ts b/packages/kbn-monaco/src/esql/lib/ast/ast_helpers.ts new file mode 100644 index 0000000000000..d6e8ea7d0a038 --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/ast/ast_helpers.ts @@ -0,0 +1,240 @@ +/* + * Copyright 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 { ParserRuleContext } from 'antlr4ts/ParserRuleContext'; +import { ErrorNode } from 'antlr4ts/tree/ErrorNode'; +import type { TerminalNode } from 'antlr4ts/tree/TerminalNode'; +import type { + ArithmeticUnaryContext, + DecimalValueContext, + IntegerValueContext, + QualifiedIntegerLiteralContext, +} from '../../antlr/esql_parser'; +import { getPosition } from './ast_position_utils'; +import type { + ESQLCommand, + ESQLLiteral, + ESQLList, + ESQLTimeInterval, + ESQLLocation, + ESQLFunction, + ESQLSource, + ESQLColumn, + ESQLCommandOption, + ESQLAstItem, +} from './types'; + +export function nonNullable(v: T): v is NonNullable { + return v != null; +} + +export function createCommand(name: string, ctx: ParserRuleContext): ESQLCommand { + return { + type: 'command', + name, + text: ctx.text, + args: [], + location: getPosition(ctx.start, ctx.stop), + incomplete: Boolean(ctx.exception), + }; +} + +export function createList(ctx: ParserRuleContext, values: ESQLLiteral[]): ESQLList { + return { + type: 'list', + name: ctx.text, + values, + text: ctx.text, + location: getPosition(ctx.start, ctx.stop), + incomplete: Boolean(ctx.exception), + }; +} + +export function createNumericLiteral(ctx: DecimalValueContext | IntegerValueContext): ESQLLiteral { + const text = ctx.text; + return { + type: 'literal', + literalType: 'number', + text, + name: text, + value: Number(text), + location: getPosition(ctx.start, ctx.stop), + incomplete: Boolean(ctx.exception), + }; +} + +export function createFakeMultiplyLiteral(ctx: ArithmeticUnaryContext): ESQLLiteral { + return { + type: 'literal', + literalType: 'number', + text: ctx.text, + name: ctx.text, + value: ctx.PLUS() ? 1 : -1, + location: getPosition(ctx.start, ctx.stop), + incomplete: Boolean(ctx.exception), + }; +} + +export function createLiteral( + type: ESQLLiteral['literalType'], + node: TerminalNode | undefined +): ESQLLiteral | undefined { + if (!node) { + return; + } + const text = node.text; + return { + type: 'literal', + literalType: type, + text, + name: text, + value: type === 'number' ? Number(text) : text, + location: getPosition(node.symbol), + incomplete: / number +) { + let nextArg: ESQLAstItem | undefined = args[getNextItemIndex(args)]; + const location = { ...initialLocation }; + while (Array.isArray(nextArg) || nextArg) { + if (Array.isArray(nextArg)) { + nextArg = nextArg[getNextItemIndex(nextArg)]; + } else { + location[prop] = Math[prop](location[prop], nextArg.location[prop]); + if (nextArg.type === 'function') { + nextArg = nextArg.args[getNextItemIndex(nextArg.args)]; + } else { + nextArg = undefined; + } + } + } + return location[prop]; +} + +export function computeLocationExtends(fn: ESQLFunction) { + const location = fn.location; + if (fn.args) { + // get min location navigating in depth keeping the left/first arg + location.min = walkFunctionStructure(fn.args, location, 'min', () => 0); + // get max location navigating in depth keeping the right/last arg + location.max = walkFunctionStructure(fn.args, location, 'max', (args) => args.length - 1); + } + return location; +} + +// Note: do not import esql_parser or bundle size will grow up by ~500 kb +function getQuotedText(ctx: ParserRuleContext) { + return ( + ctx.tryGetToken(73 /* esql_parser.SRC_QUOTED_IDENTIFIER*/, 0) || + ctx.tryGetToken(64 /* esql_parser.QUOTED_IDENTIFIER */, 0) + ); +} + +function getUnquotedText(ctx: ParserRuleContext) { + return ( + ctx.tryGetToken(72 /* esql_parser.SRC_UNQUOTED_IDENTIFIER */, 0) || + ctx.tryGetToken(63 /* esql_parser.UNQUOTED_IDENTIFIER */, 0) + ); +} + +const TICKS_REGEX = /(`)/g; + +function isQuoted(text: string | undefined) { + return text && TICKS_REGEX.test(text); +} + +export function sanifyIdentifierString(ctx: ParserRuleContext) { + return ( + getUnquotedText(ctx)?.text || + getQuotedText(ctx)?.text.replace(TICKS_REGEX, '') || + ctx.text.replace(TICKS_REGEX, '') // for some reason some quoted text is not detected correctly by the parser + ); +} + +export function createSource( + ctx: ParserRuleContext, + type: 'index' | 'policy' = 'index' +): ESQLSource { + const text = sanifyIdentifierString(ctx); + return { + type: 'source', + name: text, + sourceType: type, + text, + location: getPosition(ctx.start, ctx.stop), + incomplete: Boolean(ctx.exception || text === ''), + }; +} + +export function createColumnStar(ctx: TerminalNode): ESQLColumn { + return { + type: 'column', + name: ctx.text, + text: ctx.text, + location: getPosition(ctx.symbol), + incomplete: ctx.text === '', + quoted: false, + }; +} + +export function createColumn(ctx: ParserRuleContext): ESQLColumn { + const text = sanifyIdentifierString(ctx); + const hasQuotes = Boolean(getQuotedText(ctx) || isQuoted(ctx.text)); + return { + type: 'column', + name: text, + text, + location: getPosition(ctx.start, ctx.stop), + incomplete: Boolean(ctx.exception || text === ''), + quoted: hasQuotes, + }; +} + +export function createOption(name: string, ctx: ParserRuleContext): ESQLCommandOption { + return { + type: 'option', + name, + text: ctx.text, + location: getPosition(ctx.start, ctx.stop), + args: [], + incomplete: Boolean(ctx.exception || ctx.children?.some((c) => c instanceof ErrorNode)), + }; +} diff --git a/packages/kbn-monaco/src/esql/lib/ast/ast_position_utils.ts b/packages/kbn-monaco/src/esql/lib/ast/ast_position_utils.ts new file mode 100644 index 0000000000000..73745b12f4908 --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/ast/ast_position_utils.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 type { Token } from 'antlr4ts'; + +export function getPosition(token: Token | undefined, lastToken?: Token | undefined) { + if (!token || token.startIndex < 0) { + return { min: 0, max: 0 }; + } + const endFirstToken = + token.stopIndex > -1 ? Math.max(token.stopIndex + 1, token.startIndex) : undefined; + const endLastToken = lastToken?.stopIndex; + return { + min: token.startIndex, + max: endLastToken ?? endFirstToken ?? Infinity, + }; +} diff --git a/packages/kbn-monaco/src/esql/lib/ast/ast_walker.ts b/packages/kbn-monaco/src/esql/lib/ast/ast_walker.ts new file mode 100644 index 0000000000000..677bc4694e05a --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/ast/ast_walker.ts @@ -0,0 +1,544 @@ +/* + * Copyright 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 { + ArithmeticBinaryContext, + ArithmeticUnaryContext, + BooleanArrayLiteralContext, + BooleanDefaultContext, + type BooleanExpressionContext, + BooleanLiteralContext, + BooleanValueContext, + type CommandOptionsContext, + ComparisonContext, + type ComparisonOperatorContext, + type ConstantContext, + ConstantDefaultContext, + DecimalLiteralContext, + DereferenceContext, + type DissectCommandContext, + type DropCommandContext, + type EnrichCommandContext, + esql_parser, + type FieldContext, + type FieldsContext, + type FromCommandContext, + FunctionContext, + type GrokCommandContext, + IntegerLiteralContext, + IsNullContext, + type KeepCommandContext, + LogicalBinaryContext, + LogicalInContext, + LogicalNotContext, + type MetadataContext, + type MvExpandCommandContext, + NullLiteralContext, + NumericArrayLiteralContext, + NumericValueContext, + type OperatorExpressionContext, + OperatorExpressionDefaultContext, + type OrderExpressionContext, + ParenthesizedExpressionContext, + type PrimaryExpressionContext, + QualifiedIntegerLiteralContext, + RegexBooleanExpressionContext, + type RenameClauseContext, + SourceIdentifierContext, + type StatsCommandContext, + StringArrayLiteralContext, + StringContext, + StringLiteralContext, + type ValueExpressionContext, + ValueExpressionDefaultContext, +} from '../../antlr/esql_parser'; +import { + createSource, + createColumn, + createOption, + nonNullable, + createFunction, + createLiteral, + createTimeUnit, + createFakeMultiplyLiteral, + createList, + createNumericLiteral, + sanifyIdentifierString, + computeLocationExtends, + createColumnStar, +} from './ast_helpers'; +import { getPosition } from './ast_position_utils'; +import type { + ESQLLiteral, + ESQLColumn, + ESQLFunction, + ESQLCommandOption, + ESQLAstItem, +} from './types'; + +export function collectAllSourceIdentifiers(ctx: FromCommandContext): ESQLAstItem[] { + return ctx.getRuleContexts(SourceIdentifierContext).map((sourceCtx) => createSource(sourceCtx)); +} + +export function collectAllColumnIdentifiers( + ctx: KeepCommandContext | DropCommandContext | MvExpandCommandContext | MetadataContext +): ESQLAstItem[] { + const identifiers = ( + Array.isArray(ctx.sourceIdentifier()) ? ctx.sourceIdentifier() : [ctx.sourceIdentifier()] + ) as SourceIdentifierContext[]; + const args: ESQLColumn[] = + identifiers + .filter((child) => child.text) + .map((sourceContext) => { + return createColumn(sourceContext); + }) ?? []; + return args; +} + +export function getPolicyName(ctx: EnrichCommandContext) { + if (!ctx._policyName) { + return []; + } + return [createSource(ctx._policyName, 'policy')]; +} + +export function getMatchField(ctx: EnrichCommandContext) { + if (!ctx._matchField) { + return []; + } + const identifier = ctx.sourceIdentifier(1); + if (identifier) { + const fn = createOption(ctx.ON()!.text.toLowerCase(), ctx); + if (identifier.text) { + fn.args.push(createColumn(identifier)); + } + // overwrite the location inferring the correct position + fn.location = getPosition(ctx.ON()!.symbol, ctx.WITH()?.symbol); + return [fn]; + } + return []; +} + +export function getEnrichClauses(ctx: EnrichCommandContext) { + const ast: ESQLCommandOption[] = []; + if (ctx.WITH()) { + const option = createOption(ctx.WITH()!.text.toLowerCase(), ctx); + ast.push(option); + const clauses = ctx.enrichWithClause(); + for (const clause of clauses) { + if (clause._enrichField) { + const args = [ + // if an explicit assign is not set, create a fake assign with + // both left and right value with the same column + clause.ASSIGN() ? createColumn(clause._newName) : createColumn(clause._enrichField), + createColumn(clause._enrichField), + ].filter(nonNullable); + if (args.length) { + const fn = createFunction('=', clause); + fn.args.push(args[0], [args[1]]); + option.args.push(fn); + } + } + } + option.location = getPosition(ctx.WITH()?.symbol); + } + + return ast; +} + +function visitLogicalNot(ctx: LogicalNotContext) { + const fn = createFunction('not', ctx); + fn.args.push(...collectBooleanExpression(ctx.booleanExpression())); + // update the location of the assign based on arguments + const argsLocationExtends = computeLocationExtends(fn); + fn.location = argsLocationExtends; + return fn; +} + +function visitLogicalAndsOrs(ctx: LogicalBinaryContext) { + const fn = createFunction(ctx.AND() ? 'and' : 'or', ctx); + fn.args.push(...collectBooleanExpression(ctx._left), ...collectBooleanExpression(ctx._right)); + // update the location of the assign based on arguments + const argsLocationExtends = computeLocationExtends(fn); + fn.location = argsLocationExtends; + return fn; +} + +function visitLogicalIns(ctx: LogicalInContext) { + const fn = createFunction(ctx.NOT() ? 'not_in' : 'in', ctx); + const [left, ...list] = ctx.valueExpression(); + const values = [visitValueExpression(left), list.map((ve) => visitValueExpression(ve))]; + for (const arg of values) { + if (arg) { + const filteredArgs = Array.isArray(arg) ? arg.filter(nonNullable) : [arg]; + fn.args.push(filteredArgs); + } + } + // update the location of the assign based on arguments + const argsLocationExtends = computeLocationExtends(fn); + fn.location = argsLocationExtends; + return fn; +} + +function getMathOperation(ctx: ArithmeticBinaryContext) { + return ( + ctx.PLUS()?.text || + ctx.MINUS()?.text || + ctx.ASTERISK()?.text || + ctx.SLASH()?.text || + ctx.PERCENT()?.text || + '' + ); +} + +function getComparisonName(ctx: ComparisonOperatorContext) { + return ( + ctx.EQ()?.text || + ctx.NEQ()?.text || + ctx.LT()?.text || + ctx.LTE()?.text || + ctx.GT()?.text || + ctx.GTE()?.text || + '' + ); +} + +function visitValueExpression(ctx: ValueExpressionContext) { + if (ctx instanceof ValueExpressionDefaultContext) { + return visitOperatorExpression(ctx.operatorExpression()); + } + if (ctx instanceof ComparisonContext) { + const comparisonNode = ctx.comparisonOperator(); + const comparisonFn = createFunction(getComparisonName(comparisonNode), comparisonNode); + comparisonFn.args.push( + visitOperatorExpression(ctx._left)!, + visitOperatorExpression(ctx._right)! + ); + // update the location of the comparisonFn based on arguments + const argsLocationExtends = computeLocationExtends(comparisonFn); + comparisonFn.location = argsLocationExtends; + + return comparisonFn; + } +} + +function visitOperatorExpression( + ctx: OperatorExpressionContext +): ESQLAstItem | ESQLAstItem[] | undefined { + if (ctx instanceof ArithmeticUnaryContext) { + const arg = visitOperatorExpression(ctx.operatorExpression()); + // this is a number sign thing + const fn = createFunction('multiply', ctx); + fn.args.push(createFakeMultiplyLiteral(ctx)); + if (arg) { + fn.args.push(arg); + } + return fn; + } + if (ctx instanceof ArithmeticBinaryContext) { + const fn = createFunction(getMathOperation(ctx), ctx); + const args = [visitOperatorExpression(ctx._left), visitOperatorExpression(ctx._right)]; + for (const arg of args) { + if (arg) { + fn.args.push(arg); + } + } + // update the location of the assign based on arguments + const argsLocationExtends = computeLocationExtends(fn); + fn.location = argsLocationExtends; + return fn; + } + if (ctx instanceof OperatorExpressionDefaultContext) { + return visitPrimaryExpression(ctx.primaryExpression()); + } +} + +function getBooleanValue(ctx: BooleanLiteralContext | BooleanValueContext) { + const parentNode = ctx instanceof BooleanLiteralContext ? ctx.booleanValue() : ctx; + const booleanTerminalNode = parentNode.TRUE() || parentNode.FALSE(); + return createLiteral('boolean', booleanTerminalNode!); +} + +function getConstant(ctx: ConstantContext | undefined): ESQLAstItem | undefined { + if (ctx instanceof NullLiteralContext) { + return createLiteral('string', ctx.NULL()); + } + if (ctx instanceof QualifiedIntegerLiteralContext) { + // despite the generic name, this is a date unit constant: + // e.g. 1 year, 15 months + return createTimeUnit(ctx); + } + if (ctx instanceof DecimalLiteralContext) { + return createNumericLiteral(ctx.decimalValue()); + } + if (ctx instanceof IntegerLiteralContext) { + return createNumericLiteral(ctx.integerValue()); + } + if (ctx instanceof BooleanLiteralContext) { + return getBooleanValue(ctx); + } + if (ctx instanceof StringLiteralContext) { + return createLiteral('string', ctx.string().STRING()); + } + if ( + ctx instanceof NumericArrayLiteralContext || + ctx instanceof BooleanArrayLiteralContext || + ctx instanceof StringArrayLiteralContext + ) { + const values: ESQLLiteral[] = []; + for (const numericValue of ctx.getRuleContexts(NumericValueContext)) { + const value = numericValue.decimalValue() || numericValue.integerValue(); + values.push(createNumericLiteral(value!)); + } + for (const booleanValue of ctx.getRuleContexts(BooleanValueContext)) { + values.push(getBooleanValue(booleanValue)!); + } + for (const string of ctx.getRuleContexts(StringContext)) { + const literal = createLiteral('string', string.STRING()); + if (literal) { + values.push(literal); + } + } + return createList(ctx, values); + } +} + +export function visitRenameClauses(clausesCtx: RenameClauseContext[]): ESQLAstItem[] { + return clausesCtx + .map((clause) => { + const asToken = clause.tryGetToken(esql_parser.AS, 0); + if (asToken) { + const fn = createOption(asToken.text.toLowerCase(), clause); + for (const arg of [clause._oldName, clause._newName]) { + if (arg?.text) { + fn.args.push(createColumn(arg)); + } + } + return fn; + } else if (clause._oldName?.text) { + return createColumn(clause._oldName); + } + }) + .filter(nonNullable); +} + +export function visitPrimaryExpression( + ctx: PrimaryExpressionContext +): ESQLAstItem | ESQLAstItem[] | undefined { + if (ctx instanceof ConstantDefaultContext) { + return getConstant(ctx.constant()); + } + if (ctx instanceof DereferenceContext) { + return createColumn(ctx.qualifiedName()); + } + if (ctx instanceof ParenthesizedExpressionContext) { + return collectBooleanExpression(ctx.booleanExpression()); + } + if (ctx instanceof FunctionContext) { + const functionExpressionCtx = ctx.functionExpression(); + const fn = createFunction(functionExpressionCtx.identifier().text.toLowerCase(), ctx); + const asteriskArg = functionExpressionCtx.ASTERISK() + ? createColumnStar(functionExpressionCtx.ASTERISK()!) + : undefined; + if (asteriskArg) { + fn.args.push(asteriskArg); + } + const functionArgs = functionExpressionCtx + .booleanExpression() + .flatMap(collectBooleanExpression) + .filter(nonNullable); + if (functionArgs.length) { + fn.args.push(...functionArgs); + } + return fn; + } +} + +export function collectLogicalExpression(ctx: BooleanExpressionContext) { + if (ctx instanceof LogicalNotContext) { + return [visitLogicalNot(ctx)]; + } + if (ctx instanceof LogicalBinaryContext) { + return [visitLogicalAndsOrs(ctx)]; + } + if (ctx instanceof LogicalInContext) { + return [visitLogicalIns(ctx)]; + } + return []; +} + +function collectRegexExpression(ctx: BooleanExpressionContext): ESQLFunction[] { + const regexes = ctx.getRuleContexts(RegexBooleanExpressionContext); + const ret: ESQLFunction[] = []; + return ret.concat( + regexes.map((regex) => { + const negate = regex.NOT(); + const likeType = regex._kind.text?.toLowerCase() || ''; + const fnName = `${negate ? 'not_' : ''}${likeType}`; + const fn = createFunction(fnName, regex); + const arg = visitValueExpression(regex.valueExpression()); + if (arg) { + fn.args.push(arg); + const literal = createLiteral('string', regex._pattern.STRING()); + if (literal) { + fn.args.push(literal); + } + } + return fn; + }) + ); +} + +function collectIsNullExpression(ctx: BooleanExpressionContext) { + if (!(ctx instanceof IsNullContext)) { + return []; + } + const negate = ctx.NOT(); + const fnName = `${negate ? 'not_' : ''}is_null`; + const fn = createFunction(fnName, ctx); + const arg = visitValueExpression(ctx.valueExpression()); + if (arg) { + fn.args.push(arg); + } + return [fn]; +} + +function collectDefaultExpression(ctx: BooleanExpressionContext) { + if (!(ctx instanceof BooleanDefaultContext)) { + return []; + } + const arg = visitValueExpression(ctx.valueExpression()); + return arg ? [arg] : []; +} + +export function collectBooleanExpression(ctx: BooleanExpressionContext | undefined): ESQLAstItem[] { + const ast: ESQLAstItem[] = []; + if (!ctx) { + return ast; + } + return ast.concat( + collectLogicalExpression(ctx), + collectRegexExpression(ctx), + collectIsNullExpression(ctx), + collectDefaultExpression(ctx) + ); +} + +export function visitField(ctx: FieldContext) { + if (ctx.qualifiedName() && ctx.ASSIGN()) { + const fn = createFunction(ctx.ASSIGN()!.text, ctx); + fn.args.push( + createColumn(ctx.qualifiedName()!), + collectBooleanExpression(ctx.booleanExpression()) + ); + // update the location of the assign based on arguments + const argsLocationExtends = computeLocationExtends(fn); + fn.location = argsLocationExtends; + return [fn]; + } + return collectBooleanExpression(ctx.booleanExpression()); +} + +export function collectAllFieldsStatements(ctx: FieldsContext | undefined): ESQLAstItem[] { + const ast: ESQLAstItem[] = []; + if (!ctx) { + return ast; + } + try { + for (const field of ctx.field()) { + ast.push(...visitField(field)); + } + } catch (e) { + // do nothing + } + return ast; +} + +export function visitByOption(ctx: StatsCommandContext) { + if (!ctx.BY()) { + return []; + } + const option = createOption(ctx.BY()!.text.toLowerCase(), ctx); + for (const qnCtx of ctx.grouping()?.qualifiedName() || []) { + if (qnCtx?.text?.length) { + option.args.push(createColumn(qnCtx)); + } + } + return [option]; +} + +export function visitOrderExpression(ctx: OrderExpressionContext[]) { + const ast = []; + for (const orderCtx of ctx) { + const expression = collectBooleanExpression(orderCtx.booleanExpression()); + if (orderCtx._ordering) { + const terminalNode = + orderCtx.tryGetToken(esql_parser.ASC, 0) || orderCtx.tryGetToken(esql_parser.DESC, 0); + const literal = createLiteral('string', terminalNode); + if (literal) { + expression.push(literal); + } + } + if (orderCtx.NULLS()) { + expression.push(createLiteral('string', orderCtx.NULLS()!)!); + if (orderCtx._nullOrdering) { + const innerTerminalNode = + orderCtx.tryGetToken(esql_parser.FIRST, 0) || orderCtx.tryGetToken(esql_parser.LAST, 0); + const literal = createLiteral('string', innerTerminalNode); + if (literal) { + expression.push(literal); + } + } + } + + if (expression.length) { + ast.push(...expression); + } + } + return ast; +} + +export function visitDissect(ctx: DissectCommandContext) { + const pattern = ctx.string().tryGetToken(esql_parser.STRING, 0); + return [ + visitPrimaryExpression(ctx.primaryExpression()), + createLiteral('string', pattern), + ...visitDissectOptions(ctx.commandOptions()), + ].filter(nonNullable); +} + +export function visitGrok(ctx: GrokCommandContext) { + const pattern = ctx.string().tryGetToken(esql_parser.STRING, 0); + return [visitPrimaryExpression(ctx.primaryExpression()), createLiteral('string', pattern)].filter( + nonNullable + ); +} + +function visitDissectOptions(ctx: CommandOptionsContext | undefined) { + if (!ctx) { + return []; + } + const options: ESQLCommandOption[] = []; + for (const optionCtx of ctx.commandOption()) { + const option = createOption( + sanifyIdentifierString(optionCtx.identifier()).toLowerCase(), + optionCtx + ); + options.push(option); + // it can throw while accessing constant for incomplete commands, so try catch it + try { + const optionValue = getConstant(optionCtx.constant()); + if (optionValue != null) { + option.args.push(optionValue); + } + } catch (e) { + // do nothing here + } + } + return options; +} diff --git a/packages/kbn-monaco/src/esql/lib/ast/autocomplete/autocomplete.test.ts b/packages/kbn-monaco/src/esql/lib/ast/autocomplete/autocomplete.test.ts new file mode 100644 index 0000000000000..bfb9ecaee8d80 --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/ast/autocomplete/autocomplete.test.ts @@ -0,0 +1,780 @@ +/* + * Copyright 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 { monaco } from '../../../../monaco_imports'; +import { CharStreams } from 'antlr4ts'; +import { suggest } from './autocomplete'; +import { getParser, ROOT_STATEMENT } from '../../antlr_facade'; +import { ESQLErrorListener } from '../../monaco/esql_error_listener'; +import { AstListener } from '../ast_factory'; +import { evalFunctionsDefinitions } from '../definitions/functions'; +import { builtinFunctions } from '../definitions/builtin'; +import { statsAggregationFunctionDefinitions } from '../definitions/aggs'; +import { chronoLiterals, timeLiterals } from '../definitions/literals'; +import { commandDefinitions } from '../definitions/commands'; + +const triggerCharacters = [',', '(', '=', ' ']; + +const fields = [ + ...['string', 'number', 'date', 'boolean', 'ip'].map((type) => ({ + name: `${type}Field`, + type, + })), + { name: 'any#Char$ field', type: 'number' }, + { name: 'kubernetes.something.something', type: 'number' }, + { + name: `listField`, + type: `list`, + }, +]; + +const indexes = ['a', 'index', 'otherIndex', '.secretIndex'].map((name) => ({ + name, + hidden: name.startsWith('.'), +})); +const policies = [ + { + name: 'policy', + sourceIndices: ['enrichIndex1'], + matchField: 'otherStringField', + enrichFields: ['otherField', 'yetAnotherField'], + }, +]; + +/** + * Utility to filter down the function list for the given type + * It is mainly driven by the return type, but it can be filtered upon with the last optional argument "paramsTypes" + * jsut make sure to pass the arguments in the right order + * @param command current command context + * @param expectedReturnType the expected type returned by the function + * @param functionCategories + * @param paramsTypes the function argument types (optional) + * @returns + */ +function getFunctionSignaturesByReturnType( + command: string, + expectedReturnType: string, + { agg, evalMath, builtin }: { agg?: boolean; evalMath?: boolean; builtin?: boolean } = {}, + paramsTypes?: string[], + ignored?: string[] +) { + const list = []; + if (agg) { + list.push(...statsAggregationFunctionDefinitions); + } + // eval functions (eval is a special keyword in JS) + if (evalMath) { + list.push(...evalFunctionsDefinitions); + } + if (builtin) { + list.push(...builtinFunctions); + } + return list + .filter(({ signatures, ignoreAsSuggestion, supportedCommands }) => { + if (ignoreAsSuggestion) { + return false; + } + if (!supportedCommands.includes(command)) { + return false; + } + const filteredByReturnType = signatures.some( + ({ returnType }) => expectedReturnType === 'any' || returnType === expectedReturnType + ); + if (!filteredByReturnType) { + return false; + } + if (paramsTypes?.length) { + return signatures.some(({ params }) => + paramsTypes.every( + (expectedType, i) => expectedType === 'any' || expectedType === params[i].type + ) + ); + } + return true; + }) + .filter(({ name }) => { + if (ignored?.length) { + return !ignored?.includes(name); + } + return true; + }) + .map(({ builtin: isBuiltinFn, name, signatures, ...defRest }) => + isBuiltinFn ? `${name} $0` : `${name}($0)` + ); +} + +function getFieldNamesByType(requestedType: string) { + return fields + .filter(({ type }) => requestedType === 'any' || type === requestedType) + .map(({ name }) => name); +} + +function getLiteralsByType(type: string) { + if (type === 'time_literal') { + // return only singular + return timeLiterals.map(({ name }) => `1 ${name}`).filter((s) => !/s$/.test(s)); + } + if (type === 'chrono_literal') { + return chronoLiterals.map(({ name }) => name); + } + return []; +} + +function createCustomCallbackMocks( + customFields: Array<{ name: string; type: string }> | undefined, + customSources: Array<{ name: string; hidden: boolean }> | undefined, + customPolicies: + | Array<{ + name: string; + sourceIndices: string[]; + matchField: string; + enrichFields: string[]; + }> + | undefined +) { + const finalFields = customFields || fields; + const finalSources = customSources || indexes; + const finalPolicies = customPolicies || policies; + return { + getFieldsFor: jest.fn(async () => finalFields), + getSources: jest.fn(async () => finalSources), + getPolicies: jest.fn(async () => finalPolicies), + }; +} + +function createModelAndPosition(text: string, offset: number) { + return { + model: { getValue: () => text } as monaco.editor.ITextModel, + position: { lineNumber: 1, column: offset } as monaco.Position, + }; +} + +function createSuggestContext(text: string, triggerCharacter?: string) { + if (triggerCharacter) { + return { triggerCharacter, triggerKind: 1 }; // any number is fine here + } + const foundTriggerCharIndexes = triggerCharacters.map((char) => text.lastIndexOf(char)); + const maxIndex = Math.max(...foundTriggerCharIndexes); + return { + triggerCharacter: text[maxIndex], + triggerKind: 1, + }; +} + +function getPolicyFields(policyName: string) { + return policies + .filter(({ name }) => name === policyName) + .flatMap(({ enrichFields }) => enrichFields); +} + +describe('autocomplete', () => { + const getAstAndErrors = async (text: string) => { + const errorListener = new ESQLErrorListener(); + const parseListener = new AstListener(); + const parser = getParser(CharStreams.fromString(text), errorListener, parseListener); + + parser[ROOT_STATEMENT](); + + return { ...parseListener.getAst(), errors: [] }; + }; + + type TestArgs = [ + string, + string[], + (string | number)?, + Parameters? + ]; + + const testSuggestionsFn = ( + statement: string, + expected: string[], + triggerCharacter: string | number = '', + customCallbacksArgs: Parameters = [ + undefined, + undefined, + undefined, + ], + { only, skip }: { only?: boolean; skip?: boolean } = {} + ) => { + const triggerCharacterString = + triggerCharacter == null || typeof triggerCharacter === 'string' + ? triggerCharacter + : statement[triggerCharacter + 1]; + const context = createSuggestContext(statement, triggerCharacterString); + const offset = + typeof triggerCharacter === 'string' + ? statement.lastIndexOf(context.triggerCharacter) + 2 + : triggerCharacter; + const testFn = only ? test.only : skip ? test.skip : test; + + testFn( + `${statement} (triggerChar: "${context.triggerCharacter}" @ ${offset})=> ["${expected.join( + '","' + )}"]`, + async () => { + const callbackMocks = createCustomCallbackMocks(...customCallbacksArgs); + const { model, position } = createModelAndPosition(statement, offset); + const suggestions = await suggest( + model, + position, + context, + async (text) => (text ? await getAstAndErrors(text) : { ast: [], errors: [] }), + callbackMocks + ); + expect(suggestions.map((i) => i.insertText)).toEqual(expected); + } + ); + }; + + // Enrich the function to work with .only and .skip as regular test function + const testSuggestions = Object.assign(testSuggestionsFn, { + skip: (...args: TestArgs) => { + const paddingArgs = ['', [undefined, undefined, undefined]].slice(args.length - 2); + return testSuggestionsFn( + ...((args.length > 1 ? [...args, ...paddingArgs] : args) as TestArgs), + { + skip: true, + } + ); + }, + only: (...args: TestArgs) => { + const paddingArgs = ['', [undefined, undefined, undefined]].slice(args.length - 2); + return testSuggestionsFn( + ...((args.length > 1 ? [...args, ...paddingArgs] : args) as TestArgs), + { + only: true, + } + ); + }, + }); + + const sourceCommands = ['row', 'from', 'show']; + + describe('New command', () => { + testSuggestions(' ', sourceCommands); + testSuggestions( + 'from a | ', + commandDefinitions + .filter(({ name }) => !sourceCommands.includes(name)) + .map(({ name }) => name) + ); + testSuggestions( + 'from a [metadata _id] | ', + commandDefinitions + .filter(({ name }) => !sourceCommands.includes(name)) + .map(({ name }) => name) + ); + testSuggestions( + 'from a | eval var0 = a | ', + commandDefinitions + .filter(({ name }) => !sourceCommands.includes(name)) + .map(({ name }) => name) + ); + testSuggestions( + 'from a [metadata _id] | eval var0 = a | ', + commandDefinitions + .filter(({ name }) => !sourceCommands.includes(name)) + .map(({ name }) => name) + ); + }); + + describe('from', () => { + const suggestedIndexes = indexes.filter(({ hidden }) => !hidden).map(({ name }) => name); + // Monaco will filter further down here + testSuggestions('f', sourceCommands); + testSuggestions('from ', suggestedIndexes); + testSuggestions('from a,', suggestedIndexes); + testSuggestions('from a, b ', ['[metadata $0 ]', '|', ',']); + testSuggestions('from *,', suggestedIndexes); + }); + + describe('where', () => { + const allEvalFns = getFunctionSignaturesByReturnType('where', 'any', { + evalMath: true, + }); + testSuggestions('from a | where ', [...getFieldNamesByType('any'), ...allEvalFns]); + testSuggestions('from a | eval var0 = 1 | where ', [ + ...getFieldNamesByType('any'), + ...allEvalFns, + 'var0', + ]); + testSuggestions('from a | where stringField ', [ + // all functions compatible with a stringField type + ...getFunctionSignaturesByReturnType( + 'where', + 'boolean', + { + builtin: true, + }, + ['string'] + ), + ]); + testSuggestions('from a | where stringField >= ', [ + ...getFieldNamesByType('string'), + ...getFunctionSignaturesByReturnType('where', 'string', { evalMath: true }), + ]); + testSuggestions('from a | where stringField >= stringField ', [ + ...getFunctionSignaturesByReturnType( + 'where', + 'boolean', + { + builtin: true, + }, + ['boolean'] + ), + '|', + ]); + for (const op of ['and', 'or']) { + testSuggestions(`from a | where stringField >= stringField ${op} `, [ + ...getFieldNamesByType('any'), + ...getFunctionSignaturesByReturnType('where', 'any', { evalMath: true }), + ]); + testSuggestions(`from a | where stringField >= stringField ${op} numberField `, [ + ...getFunctionSignaturesByReturnType('where', 'boolean', { builtin: true }, ['number']), + ]); + testSuggestions(`from a | where stringField >= stringField ${op} numberField == `, [ + ...getFieldNamesByType('number'), + ...getFunctionSignaturesByReturnType('where', 'number', { evalMath: true }), + ]); + } + testSuggestions('from a | stats a=avg(numberField) | where a ', [ + ...getFunctionSignaturesByReturnType('where', 'any', { builtin: true }, ['number']), + ]); + // Mind this test: suggestion is aware of previous commands when checking for fields + // in this case the numberField has been wiped by the STATS command and suggest cannot find it's type + // @TODO: verify this is the correct behaviour in this case or if we want a "generic" suggestion anyway + testSuggestions( + 'from a | stats a=avg(numberField) | where numberField ', + [], + '', + // make the fields suggest aware of the previous STATS, leave the other callbacks untouched + [[{ name: 'a', type: 'number' }], undefined, undefined] + ); + // The editor automatically inject the final bracket, so it is not useful to test with just open bracket + testSuggestions( + 'from a | where log10()', + [ + ...getFieldNamesByType('number'), + ...getFunctionSignaturesByReturnType('where', 'number', { evalMath: true }, undefined, [ + 'log10', + ]), + ], + '(' + ); + testSuggestions('from a | where log10(numberField) ', [ + ...getFunctionSignaturesByReturnType('where', 'number', { builtin: true }, ['number']), + ...getFunctionSignaturesByReturnType('where', 'boolean', { builtin: true }, ['number']), + ]); + testSuggestions( + 'from a | WHERE pow(numberField, )', + [ + ...getFieldNamesByType('number'), + ...getFunctionSignaturesByReturnType('where', 'number', { evalMath: true }, undefined, [ + 'pow', + ]), + ], + ',' + ); + }); + + describe('sort', () => { + testSuggestions('from a | sort ', getFieldNamesByType('any')); + testSuggestions('from a | sort stringField ', ['asc', 'desc', '|', ',']); + testSuggestions('from a | sort stringField desc ', ['nulls first', 'nulls last', '|', ',']); + // @TODO: improve here + // testSuggestions('from a | sort stringField desc ', ['first', 'last']); + }); + + describe('limit', () => { + testSuggestions('from a | limit ', ['10', '100', '1000']); + testSuggestions('from a | limit 4 ', ['|']); + }); + + describe('mv_expand', () => { + testSuggestions('from a | mv_expand ', ['listField']); + testSuggestions('from a | mv_expand a ', ['|']); + }); + + describe('rename', () => { + testSuggestions('from a | rename ', getFieldNamesByType('any')); + testSuggestions('from a | rename stringField ', ['as']); + testSuggestions('from a | rename stringField as ', ['var0']); + }); + + for (const command of ['keep', 'drop', 'project']) { + describe(command, () => { + testSuggestions(`from a | ${command} `, getFieldNamesByType('any')); + testSuggestions( + `from a | ${command} stringField, `, + getFieldNamesByType('any').filter((name) => name !== 'stringField') + ); + }); + } + + describe('stats', () => { + const allAggFunctions = getFunctionSignaturesByReturnType('stats', 'any', { + agg: true, + }); + testSuggestions('from a | stats ', ['var0 =', ...allAggFunctions]); + testSuggestions('from a | stats a ', ['= $0']); + testSuggestions('from a | stats a=', [...allAggFunctions]); + testSuggestions('from a | stats a=max(b) by ', getFieldNamesByType('any')); + testSuggestions('from a | stats a=max(b) BY ', getFieldNamesByType('any')); + testSuggestions('from a | stats a=c by d ', ['|', ',']); + testSuggestions('from a | stats a=c by d, ', getFieldNamesByType('any')); + testSuggestions('from a | stats a=max(b), ', ['var0 =', ...allAggFunctions]); + testSuggestions( + 'from a | stats a=min()', + fields.filter(({ type }) => type === 'number').map(({ name }) => name), + '(' + ); + testSuggestions('from a | stats a=min(b) ', ['by', '|', ',']); + testSuggestions('from a | stats a=min(b) by ', getFieldNamesByType('any')); + testSuggestions('from a | stats a=min(b),', ['var0 =', ...allAggFunctions]); + testSuggestions('from a | stats var0=min(b),var1=c,', ['var2 =', ...allAggFunctions]); + testSuggestions( + 'from a | stats a=min(b), b=max()', + fields.filter(({ type }) => type === 'number').map(({ name }) => name) + ); + // @TODO: remove last 2 suggestions if possible + testSuggestions('from a | eval var0=round(b), var1=round(c) | stats ', [ + 'var2 =', + ...allAggFunctions, + 'var0', + 'var1', + ]); + + // smoke testing with suggestions not at the end of the string + testSuggestions( + 'from a | stats a = min(b) | sort b', + ['by', '|', ','], + 27 /* " " after min(b) */ + ); + testSuggestions( + 'from a | stats avg(b) by stringField', + getFieldNamesByType('number'), + 21 /* b column in avg */ + ); + }); + + describe('enrich', () => { + for (const prevCommand of [ + '', + '| enrich other-policy ', + '| enrich other-policy on b ', + '| enrich other-policy with c ', + ]) { + testSuggestions(`from a ${prevCommand}| enrich `, ['policy']); + testSuggestions(`from a ${prevCommand}| enrich policy `, ['on', 'with', '|']); + testSuggestions(`from a ${prevCommand}| enrich policy on `, [ + 'stringField', + 'numberField', + 'dateField', + 'booleanField', + 'ipField', + 'any#Char$ field', + 'kubernetes.something.something', + 'listField', + ]); + testSuggestions(`from a ${prevCommand}| enrich policy on b `, ['with', '|', ',']); + testSuggestions(`from a ${prevCommand}| enrich policy on b with `, [ + 'var0 =', + ...getPolicyFields('policy'), + ]); + testSuggestions(`from a ${prevCommand}| enrich policy on b with var0 `, ['= $0', '|', ',']); + testSuggestions(`from a ${prevCommand}| enrich policy on b with var0 = `, [ + ...getPolicyFields('policy'), + ]); + testSuggestions(`from a ${prevCommand}| enrich policy on b with var0 = stringField `, [ + '|', + ',', + ]); + testSuggestions(`from a ${prevCommand}| enrich policy on b with var0 = stringField, `, [ + 'var1 =', + ...getPolicyFields('policy'), + ]); + testSuggestions(`from a ${prevCommand}| enrich policy on b with var0 = stringField, var1 `, [ + '= $0', + '|', + ',', + ]); + testSuggestions( + `from a ${prevCommand}| enrich policy on b with var0 = stringField, var1 = `, + [...getPolicyFields('policy')] + ); + testSuggestions(`from a ${prevCommand}| enrich policy with `, [ + 'var0 =', + ...getPolicyFields('policy'), + ]); + testSuggestions(`from a ${prevCommand}| enrich policy with stringField `, ['= $0', '|', ',']); + } + }); + + describe('eval', () => { + testSuggestions('from a | eval ', [ + 'var0 =', + ...getFieldNamesByType('any'), + ...getFunctionSignaturesByReturnType('eval', 'any', { evalMath: true }), + ]); + testSuggestions('from a | eval numberField ', [ + ...getFunctionSignaturesByReturnType('eval', 'any', { builtin: true }, ['number']), + '|', + ',', + ]); + testSuggestions('from a | eval a=', [ + ...getFunctionSignaturesByReturnType('eval', 'any', { evalMath: true }), + ]); + testSuggestions('from a | eval a=abs(numberField), b= ', [ + ...getFunctionSignaturesByReturnType('eval', 'any', { evalMath: true }), + ]); + testSuggestions('from a | eval a=numberField, ', [ + 'var0 =', + ...getFieldNamesByType('any'), + ...getFunctionSignaturesByReturnType('eval', 'any', { evalMath: true }), + 'a', + ]); + testSuggestions( + 'from a | eval a=round()', + [ + ...getFieldNamesByType('number'), + ...getFunctionSignaturesByReturnType('eval', 'number', { evalMath: true }, undefined, [ + 'round', + ]), + ], + '(' + ); + testSuggestions('from a | eval a=round(numberField) ', [ + ...getFunctionSignaturesByReturnType('eval', 'any', { builtin: true }, ['number']), + '|', + ',', + ]); + testSuggestions('from a | eval a=round(numberField),', [ + 'var0 =', + ...getFieldNamesByType('any'), + ...getFunctionSignaturesByReturnType('eval', 'any', { evalMath: true }), + 'a', + ]); + testSuggestions('from a | eval a=round(numberField) + ', [ + ...getFieldNamesByType('number'), + ...getFunctionSignaturesByReturnType('eval', 'number', { evalMath: true }), + 'a', // @TODO remove this + ]); + testSuggestions( + 'from a | stats avg(numberField) by stringField | eval ', + [ + 'var0 =', + ...getFunctionSignaturesByReturnType('eval', 'any', { evalMath: true }), + '`avg(numberField)`', + ], + ' ', + // make aware EVAL of the previous STATS command + [[], undefined, undefined] + ); + testSuggestions( + 'from a | eval abs(numberField) + 1 | eval ', + [ + 'var0 =', + ...getFieldNamesByType('any'), + ...getFunctionSignaturesByReturnType('eval', 'any', { evalMath: true }), + // @TODO: leverage the location data to get the original text + // For now return back the trimmed version: + // the ANTLR parser trims all text so that's what it's stored in the AST + '`abs(numberField)+1`', + ], + ' ' + ); + testSuggestions( + 'from a | stats avg(numberField) by stringField | eval ', + [ + 'var0 =', + ...getFunctionSignaturesByReturnType('eval', 'any', { evalMath: true }), + '`avg(numberField)`', + ], + ' ', + // make aware EVAL of the previous STATS command with the buggy field name from expression + [[{ name: 'avg_numberField_', type: 'number' }], undefined, undefined] + ); + testSuggestions( + 'from a | stats avg(numberField), avg(kubernetes.something.something) by stringField | eval ', + [ + 'var0 =', + ...getFunctionSignaturesByReturnType('eval', 'any', { evalMath: true }), + '`avg(numberField)`', + '`avg(kubernetes.something.something)`', + ], + ' ', + // make aware EVAL of the previous STATS command with the buggy field name from expression + [ + [ + { name: 'avg_numberField_', type: 'number' }, + { name: 'avg_kubernetes.something.something_', type: 'number' }, + ], + undefined, + undefined, + ] + ); + testSuggestions( + 'from a | eval a=round(numberField), b=round()', + [ + ...getFieldNamesByType('number'), + ...getFunctionSignaturesByReturnType('eval', 'number', { evalMath: true }, undefined, [ + 'round', + ]), + ], + '(' + ); + // test deep function nesting suggestions (and check that the same function is not suggested) + // round(round( + // round(round(round( + // etc... + for (const nesting of [1, 2, 3, 4]) { + testSuggestions( + `from a | eval a=${Array(nesting).fill('round(').join('')}`, + [ + ...getFieldNamesByType('number'), + ...getFunctionSignaturesByReturnType('eval', 'number', { evalMath: true }, undefined, [ + 'round', + ]), + ], + '(' + ); + } + + // Smoke testing for suggestions in previous position than the end of the statement + testSuggestions( + 'from a | eval var0 = abs(numberField) | eval abs(var0)', + [ + ...getFunctionSignaturesByReturnType('eval', 'any', { builtin: true }, ['number']), + '|', + ',', + ], + 38 /* " " after abs(b) */ + ); + testSuggestions( + 'from a | eval var0 = abs(b) | eval abs(var0)', + [ + ...getFieldNamesByType('number'), + ...getFunctionSignaturesByReturnType('eval', 'number', { evalMath: true }, undefined, [ + 'abs', + ]), + ], + 26 /* b column in abs */ + ); + + // Test suggestions for each possible param, within each signature variation, for each function + for (const fn of evalFunctionsDefinitions) { + // skip this fn for the moment as it's quite hard to test + if (fn.name !== 'auto_bucket') { + for (const signature of fn.signatures) { + signature.params.forEach((param, i) => { + if (i < signature.params.length - 1) { + const canHaveMoreArgs = + signature.params.filter(({ optional }, j) => !optional && j > i).length > i; + testSuggestions( + `from a | eval ${fn.name}(${Array(i).fill('field').join(', ')}${i ? ',' : ''} )`, + [ + ...getFieldNamesByType(param.type).map((f) => (canHaveMoreArgs ? `${f},` : f)), + ...getFunctionSignaturesByReturnType( + 'eval', + param.type, + { evalMath: true }, + undefined, + [fn.name] + ).map((l) => (canHaveMoreArgs ? `${l},` : l)), + ...getLiteralsByType(param.type).map((d) => (canHaveMoreArgs ? `${d},` : d)), + ] + ); + testSuggestions( + `from a | eval var0 = ${fn.name}(${Array(i).fill('field').join(', ')}${ + i ? ',' : '' + } )`, + [ + ...getFieldNamesByType(param.type).map((f) => (canHaveMoreArgs ? `${f},` : f)), + ...getFunctionSignaturesByReturnType( + 'eval', + param.type, + { evalMath: true }, + undefined, + [fn.name] + ).map((l) => (canHaveMoreArgs ? `${l},` : l)), + ...getLiteralsByType(param.type).map((d) => (canHaveMoreArgs ? `${d},` : d)), + ] + ); + } + }); + } + } + } + + describe('date math', () => { + const dateSuggestions = timeLiterals.map(({ name }) => name); + // If a literal number is detected then suggest also date period keywords + testSuggestions('from a | eval a = 1 ', [ + ...getFunctionSignaturesByReturnType('eval', 'any', { builtin: true }, ['number']), + ...dateSuggestions, + '|', + ',', + ]); + testSuggestions('from a | eval a = 1 year ', [ + ...getFunctionSignaturesByReturnType('eval', 'any', { builtin: true }, ['time_interval']), + '|', + ',', + ]); + testSuggestions('from a | eval a = 1 day + 2 ', [ + ...getFunctionSignaturesByReturnType('eval', 'any', { builtin: true }, ['number']), + ...dateSuggestions, + '|', + ',', + ]); + testSuggestions( + 'from a | eval var0=date_trunc()', + [...getLiteralsByType('time_literal').map((t) => `${t},`)], + '(' + ); + testSuggestions('from a | eval var0=date_trunc(2 )', [ + ...dateSuggestions.map((t) => `${t},`), + ',', + ]); + }); + }); + + describe('callbacks', () => { + it('should send the fields query without the last command', async () => { + const callbackMocks = createCustomCallbackMocks(undefined, undefined, undefined); + const statement = 'from a | drop stringField | eval var0 = abs(numberField) '; + const triggerOffset = statement.lastIndexOf(' '); + const context = createSuggestContext(statement, statement[triggerOffset]); + const { model, position } = createModelAndPosition(statement, triggerOffset + 2); + await suggest( + model, + position, + context, + async (text) => (text ? await getAstAndErrors(text) : { ast: [], errors: [] }), + callbackMocks + ); + expect(callbackMocks.getFieldsFor).toHaveBeenCalledWith({ + query: 'from a | drop stringField', + }); + }); + it('should send the fields query aware of the location', async () => { + const callbackMocks = createCustomCallbackMocks(undefined, undefined, undefined); + const statement = 'from a | drop | eval var0 = abs(numberField) '; + const triggerOffset = statement.lastIndexOf('p') + 1; // drop + const context = createSuggestContext(statement, statement[triggerOffset]); + const { model, position } = createModelAndPosition(statement, triggerOffset + 2); + await suggest( + model, + position, + context, + async (text) => (text ? await getAstAndErrors(text) : { ast: [], errors: [] }), + callbackMocks + ); + expect(callbackMocks.getFieldsFor).toHaveBeenCalledWith({ query: 'from a' }); + }); + }); +}); diff --git a/packages/kbn-monaco/src/esql/lib/ast/autocomplete/autocomplete.ts b/packages/kbn-monaco/src/esql/lib/ast/autocomplete/autocomplete.ts new file mode 100644 index 0000000000000..de1b07b39bed5 --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/ast/autocomplete/autocomplete.ts @@ -0,0 +1,1098 @@ +/* + * Copyright 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 uniqBy from 'lodash/uniqBy'; +import type { monaco } from '../../../../monaco_imports'; +import type { AutocompleteCommandDefinition } from './types'; +import { nonNullable } from '../ast_helpers'; +import { + columnExists, + getColumnHit, + getCommandDefinition, + getCommandOption, + getFunctionDefinition, + isAssignment, + isAssignmentComplete, + isColumnItem, + isFunctionItem, + isIncompleteItem, + isLiteralItem, + isOptionItem, + isRestartingExpression, + isSourceItem, + isTimeIntervalItem, + monacoPositionToOffset, +} from '../shared/helpers'; +import { collectVariables, excludeVariablesFromCurrentCommand } from '../shared/variables'; +import type { + AstProviderFn, + ESQLAst, + ESQLAstItem, + ESQLCommand, + ESQLCommandOption, + ESQLFunction, + ESQLSingleAstItem, +} from '../types'; +import type { ESQLPolicy, ESQLRealField, ESQLVariable, ReferenceMaps } from '../validation/types'; +import { + commaCompleteItem, + commandAutocompleteDefinitions, + getAssignmentDefinitionCompletitionItem, + getBuiltinCompatibleFunctionDefinition, + mathCommandDefinition, + pipeCompleteItem, +} from './complete_items'; +import { + buildFieldsDefinitions, + buildPoliciesDefinitions, + buildSourcesDefinitions, + buildNewVarDefinition, + buildNoPoliciesAvailableDefinition, + getCompatibleFunctionDefinition, + buildMatchingFieldsDefinition, + getCompatibleLiterals, + buildConstantsDefinitions, + buildVariablesDefinitions, + buildOptionDefinition, + TRIGGER_SUGGESTION_COMMAND, +} from './factories'; +import { EDITOR_MARKER } from '../shared/constants'; +import { getAstContext, removeMarkerArgFromArgsList } from '../shared/context'; +import { + getFieldsByTypeHelper, + getPolicyHelper, + getSourcesHelper, +} from '../shared/resources_helpers'; +import { ESQLCallbacks } from '../shared/types'; + +type GetSourceFn = () => Promise; +type GetFieldsByTypeFn = ( + type: string | string[], + ignored?: string[] +) => Promise; +type GetFieldsMapFn = () => Promise>; +type GetPoliciesFn = () => Promise; +type GetPolicyMetadataFn = (name: string) => Promise; + +function hasSameArgBothSides(assignFn: ESQLFunction) { + if (assignFn.name === '=' && isColumnItem(assignFn.args[0]) && assignFn.args[1]) { + const assignValue = assignFn.args[1]; + if (Array.isArray(assignValue) && isColumnItem(assignValue[0])) { + return assignFn.args[0].name === assignValue[0].name; + } + } +} + +function appendEnrichFields( + fieldsMap: Map, + policyMetadata: ESQLPolicy | undefined +) { + if (!policyMetadata) { + return fieldsMap; + } + // @TODO: improve this + const newMap: Map = new Map(fieldsMap); + for (const field of policyMetadata.enrichFields) { + newMap.set(field, { name: field, type: 'number' }); + } + return newMap; +} + +function getFinalSuggestions({ comma }: { comma?: boolean } = { comma: true }) { + const finalSuggestions = [pipeCompleteItem]; + if (comma) { + finalSuggestions.push(commaCompleteItem); + } + return finalSuggestions; +} + +function isMathFunction(char: string) { + return ['+', '-', '*', '/', '%', '='].some((op) => char === op); +} + +function isComma(char: string) { + return char === ','; +} + +function isSourceCommand({ label }: AutocompleteCommandDefinition) { + return ['from', 'row', 'show'].includes(String(label)); +} +/** + * This function count the number of unclosed brackets in order to + * locally fix the queryString to generate a valid AST + * A known limitation of this is that is not aware of commas "," or pipes "|" + * so it is not yet helpful on a multiple commands errors (a workaround it to pass each command here...) + * @param bracketType + * @param text + * @returns + */ +function countBracketsUnclosed(bracketType: '(' | '[', text: string) { + const stack = []; + const closingBrackets = { '(': ')', '[': ']' }; + for (const char of text) { + if (char === bracketType) { + stack.push(bracketType); + } else if (char === closingBrackets[bracketType]) { + stack.pop(); + } + } + return stack.length; +} + +export async function suggest( + model: monaco.editor.ITextModel, + position: monaco.Position, + context: monaco.languages.CompletionContext, + astProvider: AstProviderFn, + resourceRetriever?: ESQLCallbacks +): Promise { + // take the full text but then slice it to the current position + const fullText = model.getValue(); + const offset = monacoPositionToOffset(fullText, position); + const innerText = fullText.substring(0, offset); + + let finalText = innerText; + + // check if all brackets are closed, otherwise close them + const unclosedBrackets = countBracketsUnclosed('(', finalText); + // if it's a comma by the user or a forced trigger by a function argument suggestion + // add a marker to make the expression still valid + if ( + context.triggerCharacter === ',' || + (context.triggerKind === 0 && unclosedBrackets === 0) || + (context.triggerCharacter === ' ' && + // make this more robust + (isMathFunction(innerText[offset - 2]) || isComma(innerText[offset - 2]))) + ) { + finalText = `${innerText.substring(0, offset)}${EDITOR_MARKER}${innerText.substring(offset)}`; + } + if (unclosedBrackets > 0) { + // inject the closing brackets + finalText += Array(unclosedBrackets).fill(')').join(''); + } + + const { ast } = await astProvider(finalText); + + const astContext = getAstContext(innerText, ast, offset); + // build the correct query to fetch the list of fields + const queryForFields = buildQueryForFields(ast, finalText); + const { getFieldsByType, getFieldsMap } = getFieldsByTypeRetriever( + queryForFields, + resourceRetriever + ); + const getSources = getSourcesRetriever(resourceRetriever); + const { getPolicies, getPolicyMetadata } = getPolicyRetriever(resourceRetriever); + + if (astContext.type === 'newCommand') { + // propose main commands here + // filter source commands if already defined + const suggestions = commandAutocompleteDefinitions; + if (!ast.length) { + return suggestions.filter(isSourceCommand); + } + return suggestions.filter((def) => !isSourceCommand(def)); + } + + if (astContext.type === 'expression') { + // suggest next possible argument, or option + // otherwise a variable + return getExpressionSuggestionsByType( + innerText, + ast, + astContext, + getSources, + getFieldsByType, + getFieldsMap, + getPolicies, + getPolicyMetadata + ); + } + if (astContext.type === 'option') { + // need this wrap/unwrap thing to make TS happy + const { option, ...rest } = astContext; + if (option && isOptionItem(option)) { + return getOptionArgsSuggestions( + innerText, + ast, + { option, ...rest }, + getFieldsByType, + getFieldsMap, + getPolicyMetadata + ); + } + } + if (astContext.type === 'function') { + return getFunctionArgsSuggestions( + innerText, + ast, + astContext, + getFieldsByType, + getFieldsMap, + getPolicyMetadata + ); + } + return []; +} + +export function buildQueryForFields(ast: ESQLAst, queryString: string) { + const prevCommand = ast[Math.max(ast.length - 2, 0)]; + return prevCommand ? queryString.substring(0, prevCommand.location.max + 1) : queryString; +} + +function getFieldsByTypeRetriever(queryString: string, resourceRetriever?: ESQLCallbacks) { + const helpers = getFieldsByTypeHelper(queryString, resourceRetriever); + return { + getFieldsByType: async (expectedType: string | string[] = 'any', ignored: string[] = []) => { + const fields = await helpers.getFieldsByType(expectedType, ignored); + return buildFieldsDefinitions(fields); + }, + getFieldsMap: helpers.getFieldsMap, + }; +} + +function getPolicyRetriever(resourceRetriever?: ESQLCallbacks) { + const helpers = getPolicyHelper(resourceRetriever); + return { + getPolicies: async () => { + const policies = await helpers.getPolicies(); + return buildPoliciesDefinitions(policies); + }, + getPolicyMetadata: helpers.getPolicyMetadata, + }; +} + +function getSourcesRetriever(resourceRetriever?: ESQLCallbacks) { + const helper = getSourcesHelper(resourceRetriever); + return async () => { + const list = (await helper()) || []; + // hide indexes that start with . + return buildSourcesDefinitions(list.filter(({ hidden }) => !hidden).map(({ name }) => name)); + }; +} + +function findNewVariable(variables: Map) { + let autoGeneratedVariableCounter = 0; + let name = `var${autoGeneratedVariableCounter++}`; + while (variables.has(name)) { + name = `var${autoGeneratedVariableCounter++}`; + } + return name; +} + +function areCurrentArgsValid( + command: ESQLCommand, + node: ESQLAstItem, + references: Pick +) { + // unfortunately here we need to bake some command-specific logic + if (command.name === 'stats') { + if (node) { + // consider the following expressions not complete yet + // ... | stats a + // ... | stats a = + if (isColumnItem(node) || (isAssignment(node) && !isAssignmentComplete(node))) { + return false; + } + } + } + if (command.name === 'eval') { + if (node) { + if (isFunctionItem(node)) { + if (isAssignment(node)) { + return isAssignmentComplete(node); + } else { + return isFunctionArgComplete(node, references).complete; + } + } + } + } + if (command.name === 'where') { + if (node) { + if ( + isColumnItem(node) || + (isFunctionItem(node) && !isFunctionArgComplete(node, references).complete) + ) { + return false; + } else { + return ( + extractFinalTypeFromArg(node, references) === + getCommandDefinition(command.name).signature.params[0].type + ); + } + } + } + if (command.name === 'rename') { + if (node) { + if (isColumnItem(node)) { + return true; + } + } + } + return true; +} + +function extractFinalTypeFromArg( + arg: ESQLAstItem, + references: Pick +): string | undefined { + if (Array.isArray(arg)) { + return extractFinalTypeFromArg(arg[0], references); + } + if (isColumnItem(arg) || isLiteralItem(arg)) { + if (isLiteralItem(arg)) { + return arg.literalType; + } + if (isColumnItem(arg)) { + const hit = getColumnHit(arg.name, references); + if (hit) { + return hit.type; + } + } + } + if (isTimeIntervalItem(arg)) { + return arg.type; + } + if (isFunctionItem(arg)) { + const fnDef = getFunctionDefinition(arg.name); + if (fnDef) { + // @TODO: improve this to better filter down the correct return type based on existing arguments + // just mind that this can be highly recursive... + return fnDef.signatures[0].returnType; + } + } +} + +// @TODO: refactor this to be shared with validation +function isFunctionArgComplete( + arg: ESQLFunction, + references: Pick +) { + const fnDefinition = getFunctionDefinition(arg.name)!; + const cleanedArgs = removeMarkerArgFromArgsList(arg)!.args; + const argLengthCheck = fnDefinition.signatures.some((def) => { + if (def.infiniteParams && cleanedArgs.length > 0) { + return true; + } + if (def.minParams && cleanedArgs.length >= def.minParams) { + return true; + } + if (cleanedArgs.length === def.params.length) { + return true; + } + return cleanedArgs.length >= def.params.filter(({ optional }) => !optional).length; + }); + if (!argLengthCheck) { + return { complete: false, reason: 'fewArgs' }; + } + const hasCorrectTypes = fnDefinition.signatures.some((def) => { + return arg.args.every((a, index) => { + if (def.infiniteParams) { + return true; + } + return def.params[index].type === extractFinalTypeFromArg(a, references); + }); + }); + if (!hasCorrectTypes) { + return { complete: false, reason: 'wrongTypes' }; + } + return { complete: true }; +} + +async function getExpressionSuggestionsByType( + innerText: string, + commands: ESQLCommand[], + { + command, + node, + }: { + command: ESQLCommand; + node: ESQLSingleAstItem | undefined; + }, + getSources: GetSourceFn, + getFieldsByType: GetFieldsByTypeFn, + getFieldsMap: GetFieldsMapFn, + getPolicies: GetPoliciesFn, + getPolicyMetadata: GetPolicyMetadataFn +) { + const commandDef = getCommandDefinition(command.name); + // get the argument position + let argIndex = command.args.length; + const prevIndex = Math.max(argIndex - 1, 0); + const lastArg = removeMarkerArgFromArgsList(command)!.args[prevIndex]; + if (isIncompleteItem(lastArg)) { + argIndex = prevIndex; + } + + // if a node is not specified use the lastArg + // mind to give priority to node as lastArg might be a function root + // => "a > b and c == d" gets translated into and( gt(a, b) , eq(c, d) ) => hence "and" is lastArg + const nodeArg = node || lastArg; + // A new expression is considered either + // * just after a command name => i.e. ... | STATS + // * or after a comma => i.e. STATS fieldA, + const isNewExpression = isRestartingExpression(innerText) || argIndex === 0; + + // Are options already declared? This is useful to suggest only new ones + const optionsAlreadyDeclared = ( + command.args.filter((arg) => isOptionItem(arg)) as ESQLCommandOption[] + ).map(({ name }) => ({ + name, + index: commandDef.options.findIndex(({ name: defName }) => defName === name), + })); + const optionsAvailable = commandDef.options.filter(({ name }, index) => { + const optArg = optionsAlreadyDeclared.find(({ name: optionName }) => optionName === name); + return (!optArg && !optionsAlreadyDeclared.length) || (optArg && index > optArg.index); + }); + // get the next definition for the given command + let argDef = commandDef.signature.params[argIndex]; + // tune it for the variadic case + if (!argDef) { + // this is the case of a comma argument + if (commandDef.signature.multipleParams) { + if (isNewExpression || (isAssignment(lastArg) && !isAssignmentComplete(lastArg))) { + // i.e. ... | a, + // i.e. ... | a = ..., b = + argDef = commandDef.signature.params[0]; + } + } + + // this is the case where there's an argument, but it's of the wrong type + // i.e. ... | WHERE numberField (WHERE wants a boolean expression!) + // i.e. ... | STATS numberfield (STATS wants a function expression!) + if (!isNewExpression && nodeArg && !Array.isArray(nodeArg)) { + const prevArg = commandDef.signature.params[prevIndex]; + // in some cases we do not want to go back as the command only accepts a literal + // i.e. LIMIT 5 -> that's it, so no argDef should be assigned + + // make an exception for STATS (STATS is the only command who accept a function type as arg) + if ( + prevArg && + (prevArg.type === 'function' || (!Array.isArray(nodeArg) && prevArg.type !== nodeArg.type)) + ) { + if (!isLiteralItem(nodeArg) || !prevArg.literalOnly) { + argDef = prevArg; + } + } + } + } + + // collect all fields + variables to suggest + const fieldsMap: Map = await (argDef ? getFieldsMap() : new Map()); + const anyVariables = collectVariables(commands, fieldsMap); + + // enrich with assignment has some special rules who are handled somewhere else + const canHaveAssignments = ['eval', 'stats', 'row'].includes(command.name); + + const references = { fields: fieldsMap, variables: anyVariables }; + + const suggestions: AutocompleteCommandDefinition[] = []; + + // in this flow there's a clear plan here from argument definitions so try to follow it + if (argDef) { + if (argDef.type === 'column' || argDef.type === 'any' || argDef.type === 'function') { + if (isNewExpression && canHaveAssignments) { + // i.e. + // ... | ROW + // ... | STATS + // ... | STATS ..., + // ... | EVAL + // ... | EVAL ..., + suggestions.push(buildNewVarDefinition(findNewVariable(anyVariables))); + } + } + // Suggest fields or variables + if (argDef.type === 'column' || argDef.type === 'any') { + // ... | + if (!nodeArg || (isNewExpression && commandDef.signature.multipleParams)) { + suggestions.push( + ...(await getFieldsOrFunctionsSuggestions( + [argDef.innerType || 'any'], + command.name, + getFieldsByType, + { + functions: canHaveAssignments, + fields: true, + variables: anyVariables, + }, + { + ignoreFields: isNewExpression + ? command.args.filter(isColumnItem).map(({ name }) => name) + : [], + } + )) + ); + } + } + if (argDef.type === 'function' || argDef.type === 'any') { + if (isColumnItem(nodeArg)) { + // ... | STATS a + // ... | EVAL a + const nodeArgType = extractFinalTypeFromArg(nodeArg, references); + if (nodeArgType) { + suggestions.push(...getBuiltinCompatibleFunctionDefinition(command.name, nodeArgType)); + } else { + suggestions.push(getAssignmentDefinitionCompletitionItem()); + } + } + if (isNewExpression || (isAssignment(nodeArg) && !isAssignmentComplete(nodeArg))) { + // ... | STATS a = + // ... | EVAL a = + // ... | STATS a = ..., + // ... | EVAL a = ..., + // ... | STATS a = ..., b = + // ... | EVAL a = ..., b = + suggestions.push( + ...(await getFieldsOrFunctionsSuggestions(['any'], command.name, getFieldsByType, { + functions: true, + fields: false, + variables: nodeArg ? undefined : anyVariables, + })) + ); + } + } + + if (argDef.type === 'any') { + // ... | EVAL var = field + // ... | EVAL var = fn(field) + // make sure we're still in the same assignment context and there's no comma (newExpression ensures that) + if (!isNewExpression) { + if (isAssignment(nodeArg) && isAssignmentComplete(nodeArg)) { + const [rightArg] = nodeArg.args[1] as [ESQLSingleAstItem]; + const nodeArgType = extractFinalTypeFromArg(rightArg, references); + suggestions.push( + ...getBuiltinCompatibleFunctionDefinition(command.name, nodeArgType || 'any') + ); + if (nodeArgType === 'number' && isLiteralItem(rightArg)) { + // ... EVAL var = 1 + suggestions.push(...getCompatibleLiterals(command.name, ['time_literal_unit'])); + } + if (isFunctionItem(rightArg)) { + if (rightArg.args.some(isTimeIntervalItem)) { + const lastFnArg = rightArg.args[rightArg.args.length - 1]; + const lastFnArgType = extractFinalTypeFromArg(lastFnArg, references); + if (lastFnArgType === 'number' && isLiteralItem(lastFnArg)) + // ... EVAL var = 1 year + 2 + suggestions.push(...getCompatibleLiterals(command.name, ['time_literal_unit'])); + } + } + } else { + if (isFunctionItem(nodeArg)) { + const nodeArgType = extractFinalTypeFromArg(nodeArg, references); + suggestions.push( + ...(await getBuiltinFunctionNextArgument( + command, + argDef, + nodeArg, + nodeArgType || 'any', + references, + getFieldsByType + )) + ); + if (nodeArg.args.some(isTimeIntervalItem)) { + const lastFnArg = nodeArg.args[nodeArg.args.length - 1]; + const lastFnArgType = extractFinalTypeFromArg(lastFnArg, references); + if (lastFnArgType === 'number' && isLiteralItem(lastFnArg)) + // ... EVAL var = 1 year + 2 + suggestions.push(...getCompatibleLiterals(command.name, ['time_literal_unit'])); + } + } + } + } + } + + // if the definition includes a list of constants, suggest them + if (argDef.values) { + // ... | ... + suggestions.push(...buildConstantsDefinitions(argDef.values)); + } + // If the type is specified try to dig deeper in the definition to suggest the best candidate + if (['string', 'number', 'boolean'].includes(argDef.type) && !argDef.values) { + // it can be just literal values (i.e. "string") + if (argDef.literalOnly) { + // ... | ... + suggestions.push(...getCompatibleLiterals(command.name, [argDef.type], [argDef.name])); + } else { + // or it can be anything else as long as it is of the right type and the end (i.e. column or function) + if (!nodeArg) { + // ... | + // In this case start suggesting something not strictly based on type + suggestions.push( + ...(await getFieldsOrFunctionsSuggestions(['any'], command.name, getFieldsByType, { + functions: true, + fields: true, + variables: anyVariables, + })) + ); + } else { + // if something is already present, leverage its type to suggest something in context + const nodeArgType = extractFinalTypeFromArg(nodeArg, references); + // These cases can happen here, so need to identify each and provide the right suggestion + // i.e. ... | field + // i.e. ... | field + + // i.e. ... | field >= + // i.e. ... | field > 0 + // i.e. ... | field + otherN + + if (nodeArgType) { + if (isFunctionItem(nodeArg)) { + suggestions.push( + ...(await getBuiltinFunctionNextArgument( + command, + argDef, + nodeArg, + nodeArgType, + references, + getFieldsByType + )) + ); + } else { + // i.e. ... | field + suggestions.push( + ...getBuiltinCompatibleFunctionDefinition(command.name, nodeArgType) + ); + } + } + } + } + } + if (argDef.type === 'source') { + if (argDef.innerType === 'policy') { + // ... | ENRICH + const policies = await getPolicies(); + suggestions.push(...(policies.length ? policies : [buildNoPoliciesAvailableDefinition()])); + } else { + // FROM + // @TODO: filter down the suggestions here based on other existing sources defined + suggestions.push(...(await getSources())); + } + } + } + + const nonOptionArgs = command.args.filter( + (arg) => !isOptionItem(arg) && !Array.isArray(arg) && !arg.incomplete + ); + // Perform some checks on mandatory arguments + const mandatoryArgsAlreadyPresent = + (commandDef.signature.multipleParams && nonOptionArgs.length > 1) || + nonOptionArgs.length >= + commandDef.signature.params.filter(({ optional }) => !optional).length || + argDef?.type === 'function'; + + // check if declared args are fully valid for the given command + const currentArgsAreValidForCommand = areCurrentArgsValid(command, nodeArg, references); + + // latest suggestions: options and final ones + if ( + (!isNewExpression && mandatoryArgsAlreadyPresent && currentArgsAreValidForCommand) || + optionsAlreadyDeclared.length + ) { + // suggest some command options + if (optionsAvailable.length) { + suggestions.push(...optionsAvailable.map(buildOptionDefinition)); + } + + if (!optionsAvailable.length || optionsAvailable.every(({ optional }) => optional)) { + // now suggest pipe or comma + suggestions.push( + ...getFinalSuggestions({ + comma: + commandDef.signature.multipleParams && + optionsAvailable.length === commandDef.options.length, + }) + ); + } + } + // Due to some logic overlapping functions can be repeated + // so dedupe here based on insertText string (it can differ from name) + return uniqBy(suggestions, (suggestion) => suggestion.insertText); +} + +async function getBuiltinFunctionNextArgument( + command: ESQLCommand, + argDef: { type: string }, + nodeArg: ESQLFunction, + nodeArgType: string, + references: Pick, + getFieldsByType: GetFieldsByTypeFn +) { + const suggestions = []; + const isFnComplete = isFunctionArgComplete(nodeArg, references); + if (isFnComplete.complete) { + // i.e. ... | field > 0 + // i.e. ... | field + otherN + suggestions.push(...getBuiltinCompatibleFunctionDefinition(command.name, nodeArgType || 'any')); + } else { + // i.e. ... | field >= + // i.e. ... | field + + // i.e. ... | field and + + // Because it's an incomplete function, need to extract the type of the current argument + // and suggest the next argument based on types + + // pick the last arg and check its type to verify whether is incomplete for the given function + const cleanedArgs = removeMarkerArgFromArgsList(nodeArg)!.args; + const nestedType = extractFinalTypeFromArg(nodeArg.args[cleanedArgs.length - 1], references); + + if (isFnComplete.reason === 'fewArgs') { + const finalType = nestedType || nodeArgType || 'any'; + suggestions.push( + ...(await getFieldsOrFunctionsSuggestions( + // this is a special case with AND/OR + // expression AND/OR + // technically another boolean value should be suggested, but it is a better experience + // to actually suggest a wider set of fields/functions + [ + finalType === 'boolean' && getFunctionDefinition(nodeArg.name)?.builtin + ? 'any' + : finalType, + ], + command.name, + getFieldsByType, + { + functions: true, + fields: true, + variables: references.variables, + } + )) + ); + } + if (isFnComplete.reason === 'wrongTypes') { + if (nestedType) { + // suggest something to complete the builtin function + if (nestedType !== argDef.type) { + suggestions.push( + ...getBuiltinCompatibleFunctionDefinition(command.name, nestedType, [argDef.type]) + ); + } + } + } + } + return suggestions; +} + +async function getFieldsOrFunctionsSuggestions( + types: string[], + commandName: string, + getFieldsByType: GetFieldsByTypeFn, + { + functions, + fields, + variables, + }: { + functions: boolean; + fields: boolean; + variables?: Map; + }, + { + ignoreFn = [], + ignoreFields = [], + }: { + ignoreFn?: string[]; + ignoreFields?: string[]; + } = {} +): Promise { + const filteredFieldsByType = (await (fields + ? getFieldsByType(types, ignoreFields) + : [])) as AutocompleteCommandDefinition[]; + + const filteredVariablesByType: string[] = []; + if (variables) { + for (const variable of variables.values()) { + if (types.includes('any') || types.includes(variable[0].type)) { + filteredVariablesByType.push(variable[0].name); + } + } + // due to a bug on the ES|QL table side, filter out fields list with underscored variable names (??) + // avg( numberField ) => avg_numberField_ + if ( + filteredVariablesByType.length && + filteredVariablesByType.some((v) => /[^a-zA-Z\d]/.test(v)) + ) { + for (const variable of filteredVariablesByType) { + const underscoredName = variable.replace(/[^a-zA-Z\d]/g, '_'); + const index = filteredFieldsByType.findIndex(({ label }) => underscoredName === label); + if (index >= 0) { + filteredFieldsByType.splice(index); + } + } + } + } + + const suggestions = filteredFieldsByType.concat( + functions ? getCompatibleFunctionDefinition(commandName, types, ignoreFn) : [], + variables ? buildVariablesDefinitions(filteredVariablesByType) : [], + getCompatibleLiterals(commandName, types) // literals are handled internally + ); + + // rewrite the sortText here to have literals first, then fields, last functions + return suggestions.map(({ sortText, kind, ...rest }) => ({ + ...rest, + kind, + sortText: String.fromCharCode(97 - kind), + command: TRIGGER_SUGGESTION_COMMAND, + })); +} + +async function getFunctionArgsSuggestions( + innerText: string, + commands: ESQLCommand[], + { + command, + node, + }: { + command: ESQLCommand; + node: ESQLFunction; + }, + getFieldsByType: GetFieldsByTypeFn, + getFieldsMap: GetFieldsMapFn, + getPolicyMetadata: GetPolicyMetadataFn +): Promise { + const fnDefinition = getFunctionDefinition(node.name); + if (fnDefinition) { + const fieldsMap: Map = await getFieldsMap(); + const variablesExcludingCurrentCommandOnes = excludeVariablesFromCurrentCommand( + commands, + command, + fieldsMap + ); + // pick the type of the next arg + const shouldGetNextArgument = node.text.includes(EDITOR_MARKER); + let argIndex = Math.max(node.args.length, 0); + if (!shouldGetNextArgument && argIndex) { + argIndex -= 1; + } + const types = fnDefinition.signatures.flatMap((signature) => { + if (signature.params.length > argIndex) { + return signature.params[argIndex].type; + } + if (signature.infiniteParams) { + return signature.params[0].type; + } + return []; + }); + + const arg = node.args[argIndex]; + + const hasMoreMandatoryArgs = + fnDefinition.signatures[0].params.filter( + ({ optional }, index) => !optional && index > argIndex + ).length > argIndex; + + const suggestions = []; + const noArgDefined = !arg; + const isUnknownColumn = + arg && + isColumnItem(arg) && + !columnExists(arg, { fields: fieldsMap, variables: variablesExcludingCurrentCommandOnes }) + .hit; + if (noArgDefined || isUnknownColumn) { + // ... | EVAL fn( ) + // ... | EVAL fn( field, ) + suggestions.push( + ...(await getFieldsOrFunctionsSuggestions( + types, + command.name, + getFieldsByType, + { + functions: command.name !== 'stats', + fields: true, + variables: variablesExcludingCurrentCommandOnes, + }, + // do not repropose the same function as arg + // i.e. avoid cases like abs(abs(abs(...))) with suggestions + { ignoreFn: [node.name] } + )) + ); + } + + // for eval and row commands try also to complete numeric literals with time intervals where possible + if (arg) { + if (command.name !== 'stats') { + if (isLiteralItem(arg) && arg.literalType === 'number') { + // ... | EVAL fn(2 ) + suggestions.push( + ...(await getFieldsOrFunctionsSuggestions( + ['time_literal_unit'], + command.name, + getFieldsByType, + { + functions: false, + fields: false, + variables: variablesExcludingCurrentCommandOnes, + } + )) + ); + } + } + if (hasMoreMandatoryArgs) { + // suggest a comma if there's another argument for the function + suggestions.push(commaCompleteItem); + } + // if there are other arguments in the function, inject automatically a comma after each suggestion + return suggestions.map((suggestion) => + suggestion !== commaCompleteItem + ? { + ...suggestion, + insertText: + hasMoreMandatoryArgs && !fnDefinition.builtin + ? `${suggestion.insertText},` + : suggestion.insertText, + } + : suggestion + ); + } + + return suggestions.map(({ insertText, ...rest }) => ({ + ...rest, + insertText: hasMoreMandatoryArgs && !fnDefinition.builtin ? `${insertText},` : insertText, + })); + } + return mathCommandDefinition; +} + +async function getOptionArgsSuggestions( + innerText: string, + commands: ESQLCommand[], + { + command, + option, + node, + }: { + command: ESQLCommand; + option: ESQLCommandOption; + node: ESQLSingleAstItem | undefined; + }, + getFieldsByType: GetFieldsByTypeFn, + getFieldsMaps: GetFieldsMapFn, + getPolicyMetadata: GetPolicyMetadataFn +) { + const optionDef = getCommandOption(option.name); + const suggestions = []; + const isNewExpression = isRestartingExpression(innerText) || option.args.length === 0; + if (command.name === 'enrich') { + if (option.name === 'on') { + // if it's a new expression, suggest fields to match on + if (isNewExpression || (option && isAssignment(option.args[0]) && !option.args[1])) { + const policyName = isSourceItem(command.args[0]) ? command.args[0].name : undefined; + if (policyName) { + const [policyMetadata, fieldsMap] = await Promise.all([ + getPolicyMetadata(policyName), + getFieldsMaps(), + ]); + if (policyMetadata) { + suggestions.push( + ...buildMatchingFieldsDefinition( + policyMetadata.matchField, + Array.from(fieldsMap.keys()) + ) + ); + } + } + } else { + // propose the with option + suggestions.push( + buildOptionDefinition(getCommandOption('with')!), + ...getFinalSuggestions({ + comma: true, + }) + ); + } + } + if (option.name === 'with') { + let argIndex = option.args.length; + let lastArg = option.args[Math.max(argIndex - 1, 0)]; + if (isIncompleteItem(lastArg)) { + argIndex = Math.max(argIndex - 1, 0); + lastArg = option.args[argIndex]; + } + const policyName = isSourceItem(command.args[0]) ? command.args[0].name : undefined; + if (policyName) { + const [policyMetadata, fieldsMap] = await Promise.all([ + getPolicyMetadata(policyName), + getFieldsMaps(), + ]); + const anyVariables = collectVariables( + commands, + appendEnrichFields(fieldsMap, policyMetadata) + ); + + if (isNewExpression) { + suggestions.push(buildNewVarDefinition(findNewVariable(anyVariables))); + } + + // make sure to remove the marker arg from the assign fn + const assignFn = isAssignment(lastArg) + ? (removeMarkerArgFromArgsList(lastArg) as ESQLFunction) + : undefined; + + if (policyMetadata) { + if (isNewExpression || (assignFn && !isAssignmentComplete(assignFn))) { + // ... | ENRICH ... WITH a = + suggestions.push(...buildFieldsDefinitions(policyMetadata.enrichFields)); + } + } + if ( + assignFn && + hasSameArgBothSides(assignFn) && + !isNewExpression && + !isIncompleteItem(assignFn) + ) { + // ... | ENRICH ... WITH a + // effectively only assign will apper + suggestions.push(...getBuiltinCompatibleFunctionDefinition(command.name, 'any')); + } + + if ( + assignFn && + (isAssignmentComplete(assignFn) || hasSameArgBothSides(assignFn)) && + !isNewExpression + ) { + suggestions.push( + ...getFinalSuggestions({ + comma: true, + }) + ); + } + } + } + } + if (command.name === 'rename') { + if (option.args.length < 2) { + const fieldsMap = await getFieldsMaps(); + const anyVariables = collectVariables(commands, fieldsMap); + suggestions.push(...buildVariablesDefinitions([findNewVariable(anyVariables)])); + } + } + + if (optionDef) { + if (!suggestions.length) { + const argIndex = Math.max(option.args.length - 1, 0); + const types = [optionDef.signature.params[argIndex].type].filter(nonNullable); + if (option.args.length && !isRestartingExpression(innerText)) { + suggestions.push( + ...getFinalSuggestions({ + comma: true, + }) + ); + } else if (!option.args.length || isRestartingExpression(innerText)) { + suggestions.push( + ...(await getFieldsOrFunctionsSuggestions( + types[0] === 'column' ? ['any'] : types, + command.name, + getFieldsByType, + { + functions: false, + fields: true, + } + )) + ); + } + } + } + return suggestions; +} diff --git a/packages/kbn-monaco/src/esql/lib/ast/autocomplete/complete_items.ts b/packages/kbn-monaco/src/esql/lib/ast/autocomplete/complete_items.ts new file mode 100644 index 0000000000000..cfa0e9fa74b3f --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/ast/autocomplete/complete_items.ts @@ -0,0 +1,80 @@ +/* + * Copyright 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 type { AutocompleteCommandDefinition } from './types'; +import { statsAggregationFunctionDefinitions } from '../definitions/aggs'; +import { builtinFunctions } from '../definitions/builtin'; +import { evalFunctionsDefinitions } from '../definitions/functions'; +import { getAllCommands } from '../shared/helpers'; +import { + getAutocompleteFunctionDefinition, + getAutocompleteBuiltinDefinition, + getAutocompleteCommandDefinition, +} from './factories'; + +export const mathCommandDefinition: AutocompleteCommandDefinition[] = evalFunctionsDefinitions.map( + getAutocompleteFunctionDefinition +); + +export const aggregationFunctionsDefinitions: AutocompleteCommandDefinition[] = + statsAggregationFunctionDefinitions.map(getAutocompleteFunctionDefinition); + +export function getAssignmentDefinitionCompletitionItem() { + const assignFn = builtinFunctions.find(({ name }) => name === '=')!; + return getAutocompleteBuiltinDefinition(assignFn); +} + +export const getBuiltinCompatibleFunctionDefinition = ( + command: string, + argType: string, + returnTypes?: string[] +): AutocompleteCommandDefinition[] => { + const compatibleFunctions = builtinFunctions.filter( + ({ name, supportedCommands, signatures, ignoreAsSuggestion }) => + !ignoreAsSuggestion && + !/not_/.test(name) && + supportedCommands.includes(command) && + signatures.some(({ params }) => params.some((pArg) => pArg.type === argType)) + ); + + if (!returnTypes) { + return compatibleFunctions.map(getAutocompleteBuiltinDefinition); + } + return compatibleFunctions + .filter((mathDefinition) => + mathDefinition.signatures.some( + (signature) => returnTypes[0] === 'any' || returnTypes.includes(signature.returnType) + ) + ) + .map(getAutocompleteBuiltinDefinition); +}; + +export const commandAutocompleteDefinitions: AutocompleteCommandDefinition[] = getAllCommands().map( + getAutocompleteCommandDefinition +); + +export const pipeCompleteItem: AutocompleteCommandDefinition = { + label: '|', + insertText: '|', + kind: 1, + detail: i18n.translate('monaco.esql.autocomplete.pipeDoc', { + defaultMessage: 'Pipe (|)', + }), + sortText: 'B', +}; + +export const commaCompleteItem: AutocompleteCommandDefinition = { + label: ',', + insertText: ',', + kind: 1, + detail: i18n.translate('monaco.esql.autocomplete.commaDoc', { + defaultMessage: 'Comma (,)', + }), + sortText: 'C', +}; diff --git a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/utils.ts b/packages/kbn-monaco/src/esql/lib/ast/autocomplete/documentation_util.ts similarity index 100% rename from packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/utils.ts rename to packages/kbn-monaco/src/esql/lib/ast/autocomplete/documentation_util.ts diff --git a/packages/kbn-monaco/src/esql/lib/ast/autocomplete/factories.ts b/packages/kbn-monaco/src/esql/lib/ast/autocomplete/factories.ts new file mode 100644 index 0000000000000..fc136a87b23da --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/ast/autocomplete/factories.ts @@ -0,0 +1,268 @@ +/* + * Copyright 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 { AutocompleteCommandDefinition } from './types'; +import { statsAggregationFunctionDefinitions } from '../definitions/aggs'; +import { evalFunctionsDefinitions } from '../definitions/functions'; +import { getFunctionSignatures, getCommandSignature } from '../definitions/helpers'; +import { chronoLiterals, timeLiterals } from '../definitions/literals'; +import { + FunctionDefinition, + CommandDefinition, + CommandOptionsDefinition, +} from '../definitions/types'; +import { getCommandDefinition } from '../shared/helpers'; +import { buildDocumentation, buildFunctionDocumentation } from './documentation_util'; + +const allFunctions = statsAggregationFunctionDefinitions.concat(evalFunctionsDefinitions); + +export const TRIGGER_SUGGESTION_COMMAND = { + title: 'Trigger Suggestion Dialog', + id: 'editor.action.triggerSuggest', +}; + +export function getAutocompleteFunctionDefinition(fn: FunctionDefinition) { + const fullSignatures = getFunctionSignatures(fn); + return { + label: fullSignatures[0].declaration, + insertText: `${fn.name}($0)`, + insertTextRules: 4, // monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + kind: 1, + detail: fn.description, + documentation: { + value: buildFunctionDocumentation(fullSignatures), + }, + sortText: 'C', + }; +} + +export function getAutocompleteBuiltinDefinition(fn: FunctionDefinition) { + return { + label: fn.name, + insertText: `${fn.name} $0`, + insertTextRules: 4, // monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + kind: 11, + detail: fn.description, + documentation: { + value: '', + }, + sortText: 'D', + command: TRIGGER_SUGGESTION_COMMAND, + }; +} + +export const isCompatibleFunctionName = (fnName: string, command: string) => { + const fnSupportedByCommand = allFunctions.filter(({ supportedCommands }) => + supportedCommands.includes(command) + ); + return fnSupportedByCommand.some(({ name }) => name === fnName); +}; + +export const getCompatibleFunctionDefinition = ( + command: string, + returnTypes?: string[], + ignored: string[] = [] +): AutocompleteCommandDefinition[] => { + const fnSupportedByCommand = allFunctions.filter( + ({ name, supportedCommands }) => supportedCommands.includes(command) && !ignored.includes(name) + ); + if (!returnTypes) { + return fnSupportedByCommand.map(getAutocompleteFunctionDefinition); + } + return fnSupportedByCommand + .filter((mathDefinition) => + mathDefinition.signatures.some( + (signature) => returnTypes[0] === 'any' || returnTypes.includes(signature.returnType) + ) + ) + .map(getAutocompleteFunctionDefinition); +}; + +export function getAutocompleteCommandDefinition( + command: CommandDefinition +): AutocompleteCommandDefinition { + const commandDefinition = getCommandDefinition(command.name); + const commandSignature = getCommandSignature(commandDefinition); + return { + label: commandDefinition.name, + insertText: commandDefinition.name, + kind: 0, + detail: commandDefinition.description, + documentation: { + value: buildDocumentation(commandSignature.declaration, commandSignature.examples), + }, + sortText: 'A', + }; +} + +export const buildFieldsDefinitions = (fields: string[]): AutocompleteCommandDefinition[] => + fields.map((label) => ({ + label, + insertText: label, + kind: 4, + detail: i18n.translate('monaco.esql.autocomplete.fieldDefinition', { + defaultMessage: `Field specified by the input table`, + }), + sortText: 'D', + })); + +export const buildVariablesDefinitions = (variables: string[]): AutocompleteCommandDefinition[] => + variables.map((label) => ({ + label, + insertText: /[^a-zA-Z\d]/.test(label) ? `\`${label}\`` : label, + kind: 4, + detail: i18n.translate('monaco.esql.autocomplete.variableDefinition', { + defaultMessage: `Variable specified by the user within the ES|QL query`, + }), + sortText: 'D', + })); + +export const buildSourcesDefinitions = (sources: string[]): AutocompleteCommandDefinition[] => + sources.map((label) => ({ + label, + insertText: label, + kind: 21, + detail: i18n.translate('monaco.esql.autocomplete.sourceDefinition', { + defaultMessage: `Input table`, + }), + sortText: 'A', + })); + +export const buildConstantsDefinitions = ( + userConstants: string[], + detail?: string +): AutocompleteCommandDefinition[] => + userConstants.map((label) => ({ + label, + insertText: label, + kind: 14, + detail: + detail ?? + i18n.translate('monaco.esql.autocomplete.constantDefinition', { + defaultMessage: `User defined variable`, + }), + sortText: 'A', + })); + +export const buildNewVarDefinition = (label: string): AutocompleteCommandDefinition => { + return { + label, + insertText: `${label} =`, + kind: 21, + detail: i18n.translate('monaco.esql.autocomplete.newVarDoc', { + defaultMessage: 'Define a new variable', + }), + sortText: '1', + }; +}; + +export const buildPoliciesDefinitions = ( + policies: Array<{ name: string; sourceIndices: string[] }> +): AutocompleteCommandDefinition[] => + policies.map(({ name: label, sourceIndices }) => ({ + label, + insertText: label, + kind: 5, + detail: i18n.translate('monaco.esql.autocomplete.policyDefinition', { + defaultMessage: `Policy defined on {count, plural, one {index} other {indices}}: {indices}`, + values: { + count: sourceIndices.length, + indices: sourceIndices.join(', '), + }, + }), + sortText: 'D', + })); + +export const buildMatchingFieldsDefinition = ( + matchingField: string, + fields: string[] +): AutocompleteCommandDefinition[] => + fields.map((label) => ({ + label, + insertText: label, + kind: 4, + detail: i18n.translate('monaco.esql.autocomplete.matchingFieldDefinition', { + defaultMessage: `Use to match on {matchingField} on the policy`, + values: { + matchingField, + }, + }), + sortText: 'D', + })); + +export const buildOptionDefinition = (option: CommandOptionsDefinition) => { + const completeItem: AutocompleteCommandDefinition = { + label: option.name, + insertText: option.name, + kind: 21, + detail: option.description, + sortText: 'D', + }; + if (option.wrapped) { + completeItem.insertText = `${option.wrapped[0]}${option.name} $0 ${option.wrapped[1]}`; + completeItem.insertTextRules = 4; // monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet; + } + return completeItem; +}; + +export const buildNoPoliciesAvailableDefinition = (): AutocompleteCommandDefinition => ({ + label: i18n.translate('monaco.esql.autocomplete.noPoliciesLabel', { + defaultMessage: 'No available policy', + }), + insertText: '', + kind: 26, + detail: i18n.translate('monaco.esql.autocomplete.noPoliciesLabelsFound', { + defaultMessage: 'Click to create', + }), + sortText: 'D', + command: { + id: 'esql.policies.create', + title: i18n.translate('monaco.esql.autocomplete.createNewPolicy', { + defaultMessage: 'Click to create', + }), + }, +}); + +function getUnitDuration(unit: number = 1) { + const filteredTimeLiteral = timeLiterals.filter(({ name }) => { + const result = /s$/.test(name); + return unit > 1 ? result : !result; + }); + return filteredTimeLiteral.map(({ name }) => `${unit} ${name}`); +} + +export function getCompatibleLiterals(commandName: string, types: string[], names?: string[]) { + const suggestions: AutocompleteCommandDefinition[] = []; + if (types.includes('number') && commandName === 'limit') { + // suggest 10/50/100 + suggestions.push(...buildConstantsDefinitions(['10', '100', '1000'], '')); + } + if (types.includes('time_literal')) { + // filter plural for now and suggest only unit + singular + suggestions.push(...buildConstantsDefinitions(getUnitDuration(1))); // i.e. 1 year + } + // this is a special type built from the suggestion system, not inherited from the AST + if (types.includes('time_literal_unit')) { + suggestions.push(...buildConstantsDefinitions(timeLiterals.map(({ name }) => name))); // i.e. year, month, ... + } + if (types.includes('chrono_literal')) { + suggestions.push(...buildConstantsDefinitions(chronoLiterals.map(({ name }) => name))); // i.e. EPOC_DAY, ... + } + if (types.includes('string')) { + if (names) { + const index = types.indexOf('string'); + if (/pattern/.test(names[index])) { + suggestions.push(...buildConstantsDefinitions(['"a-pattern"'], 'A pattern string')); + } else { + suggestions.push(...buildConstantsDefinitions(['string'], '')); + } + } + } + return suggestions; +} diff --git a/packages/kbn-monaco/src/esql/lib/ast/autocomplete/types.ts b/packages/kbn-monaco/src/esql/lib/ast/autocomplete/types.ts new file mode 100644 index 0000000000000..ad6428fcbc771 --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/ast/autocomplete/types.ts @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { monaco } from '../../../../..'; + +/** @internal **/ +export interface UserDefinedVariables { + userDefined: string[]; + policies: string[]; +} + +/** @internal **/ +export type AutocompleteCommandDefinition = Pick< + monaco.languages.CompletionItem, + | 'label' + | 'insertText' + | 'kind' + | 'detail' + | 'documentation' + | 'sortText' + | 'insertTextRules' + | 'command' +>; diff --git a/packages/kbn-monaco/src/esql/lib/ast/definitions/aggs.ts b/packages/kbn-monaco/src/esql/lib/ast/definitions/aggs.ts new file mode 100644 index 0000000000000..91011a87a26a1 --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/ast/definitions/aggs.ts @@ -0,0 +1,127 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { FunctionDefinition } from './types'; + +function createNumericAggDefinition({ + name, + description, + args = [], +}: { + name: string; + description: string; + args?: Array<{ name: string; type: string; value: string }>; +}): FunctionDefinition { + const extraParamsExample = args.length ? `, ${args.map(({ value }) => value).join(',')}` : ''; + return { + name, + description, + supportedCommands: ['stats'], + signatures: [ + { + params: [ + { name: 'column', type: 'number', noNestingFunctions: true }, + ...args.map(({ name: paramName, type }) => ({ + name: paramName, + type, + noNestingFunctions: true, + })), + ], + returnType: 'number', + examples: [ + `from index | stats result = ${name}(field${extraParamsExample})`, + `from index | stats ${name}(field${extraParamsExample})`, + ], + }, + ], + }; +} + +export const statsAggregationFunctionDefinitions: FunctionDefinition[] = [ + { + name: 'avg', + description: i18n.translate('monaco.esql.definitions.avgDoc', { + defaultMessage: 'Returns the average of the values in a field', + }), + }, + { + name: 'max', + description: i18n.translate('monaco.esql.definitions.maxDoc', { + defaultMessage: 'Returns the maximum value in a field.', + }), + }, + { + name: 'min', + description: i18n.translate('monaco.esql.definitions.minDoc', { + defaultMessage: 'Returns the minimum value in a field.', + }), + }, + { + name: 'sum', + description: i18n.translate('monaco.esql.definitions.sumDoc', { + defaultMessage: 'Returns the sum of the values in a field.', + }), + }, + { + name: 'median', + description: i18n.translate('monaco.esql.definitions.medianDoc', { + defaultMessage: 'Returns the 50% percentile.', + }), + }, + { + name: 'median_absolute_deviation', + description: i18n.translate('monaco.esql.definitions.medianDeviationDoc', { + defaultMessage: + 'Returns the median of each data point’s deviation from the median of the entire sample.', + }), + }, + { + name: 'percentile', + description: i18n.translate('monaco.esql.definitions.percentiletDoc', { + defaultMessage: 'Returns the n percentile of a field.', + }), + args: [{ name: 'percentile', type: 'number', value: '90' }], + }, +] + .map(createNumericAggDefinition) + .concat([ + { + name: 'count', + description: i18n.translate('monaco.esql.definitions.countDoc', { + defaultMessage: 'Returns the count of the values in a field.', + }), + supportedCommands: ['stats'], + signatures: [ + { + params: [ + { name: 'column', type: 'any', noNestingFunctions: true, supportsWildcard: true }, + ], + returnType: 'number', + examples: [`from index | stats result = count(field)`, `from index | stats count(field)`], + }, + ], + }, + { + name: 'count_distinct', + description: i18n.translate('monaco.esql.definitions.countDistinctDoc', { + defaultMessage: 'Returns the count of distinct values in a field.', + }), + supportedCommands: ['stats'], + signatures: [ + { + params: [{ name: 'column', type: 'any', noNestingFunctions: true }], + returnType: 'number', + examples: [ + `from index | stats result = count_distinct(field)`, + `from index | stats count_distinct(field)`, + ], + }, + ], + }, + ]); diff --git a/packages/kbn-monaco/src/esql/lib/ast/definitions/builtin.ts b/packages/kbn-monaco/src/esql/lib/ast/definitions/builtin.ts new file mode 100644 index 0000000000000..eed2fcc5d65b4 --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/ast/definitions/builtin.ts @@ -0,0 +1,348 @@ +/* + * Copyright 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 { FunctionDefinition } from './types'; + +function createMathDefinition( + name: string, + types: Array, + description: string, + warning?: FunctionDefinition['warning'] +) { + return { + builtin: true, + name, + description, + supportedCommands: ['eval', 'where', 'row'], + signatures: types.map((type) => { + if (Array.isArray(type)) { + return { + params: [ + { name: 'left', type: type[0] }, + { name: 'right', type: type[1] }, + ], + returnType: /literal/.test(type[0]) ? type[1] : type[0], + }; + } + return { + params: [ + { name: 'left', type }, + { name: 'right', type }, + ], + returnType: type, + }; + }), + warning, + }; +} + +function createComparisonDefinition( + { + name, + description, + }: { + name: string; + description: string; + }, + warning?: FunctionDefinition['warning'] +) { + return { + builtin: true, + name, + description, + supportedCommands: ['eval', 'where', 'row'], + signatures: [ + { + params: [ + { name: 'left', type: 'number' }, + { name: 'right', type: 'number' }, + ], + returnType: 'boolean', + }, + { + params: [ + { name: 'left', type: 'string' }, + { name: 'right', type: 'string' }, + ], + returnType: 'boolean', + }, + { + params: [ + { name: 'left', type: 'date' }, + { name: 'right', type: 'date' }, + ], + returnType: 'boolean', + }, + ], + }; +} + +export const builtinFunctions: FunctionDefinition[] = [ + createMathDefinition( + '+', + ['number', 'date', ['date', 'time_literal'], ['time_literal', 'date']], + i18n.translate('monaco.esql.definition.addDoc', { + defaultMessage: 'Add (+)', + }) + ), + createMathDefinition( + '-', + ['number', 'date', ['date', 'time_literal'], ['time_literal', 'date']], + i18n.translate('monaco.esql.definition.subtractDoc', { + defaultMessage: 'Subtract (-)', + }) + ), + createMathDefinition( + '*', + ['number'], + i18n.translate('monaco.esql.definition.multiplyDoc', { + defaultMessage: 'Multiply (*)', + }) + ), + createMathDefinition( + '/', + ['number'], + i18n.translate('monaco.esql.definition.divideDoc', { + defaultMessage: 'Divide (/)', + }), + (left, right) => { + if (right.type === 'literal' && right.literalType === 'number') { + return right.value === 0 + ? i18n.translate('monaco.esql.divide.warning.divideByZero', { + defaultMessage: 'Cannot divide by zero: {left}/{right}', + values: { + left: left.text, + right: right.value, + }, + }) + : undefined; + } + } + ), + createMathDefinition( + '%', + ['number'], + i18n.translate('monaco.esql.definition.moduleDoc', { + defaultMessage: 'Module (%)', + }), + (left, right) => { + if (right.type === 'literal' && right.literalType === 'number') { + return right.value === 0 + ? i18n.translate('monaco.esql.divide.warning.zeroModule', { + defaultMessage: 'Module by zero can return null value: {left}/{right}', + values: { + left: left.text, + right: right.value, + }, + }) + : undefined; + } + } + ), + ...[ + { + name: '==', + description: i18n.translate('monaco.esql.definition.equalToDoc', { + defaultMessage: 'Equal to', + }), + }, + { + name: '!=', + description: i18n.translate('monaco.esql.definition.notEqualToDoc', { + defaultMessage: 'Not equal to', + }), + }, + { + name: '<', + description: i18n.translate('monaco.esql.definition.lessThanDoc', { + defaultMessage: 'Less than', + }), + }, + { + name: '>', + description: i18n.translate('monaco.esql.definition.greaterThanDoc', { + defaultMessage: 'Greater than', + }), + }, + { + name: '<=', + description: i18n.translate('monaco.esql.definition.lessThanOrEqualToDoc', { + defaultMessage: 'Less than or equal to', + }), + }, + { + name: '>=', + description: i18n.translate('monaco.esql.definition.greaterThanOrEqualToDoc', { + defaultMessage: 'Greater than or equal to', + }), + }, + ].map((op) => createComparisonDefinition(op)), + ...[ + { + name: 'like', + description: i18n.translate('monaco.esql.definition.likeDoc', { + defaultMessage: 'Filter data based on string patterns', + }), + }, + { name: 'not_like', description: '' }, + { + name: 'rlike', + description: i18n.translate('monaco.esql.definition.rlikeDoc', { + defaultMessage: 'Filter data based on string regular expressions', + }), + }, + { name: 'not_rlike', description: '' }, + ].map(({ name, description }) => ({ + builtin: true, + ignoreAsSuggestion: /not/.test(name), + name, + description, + supportedCommands: ['eval', 'where', 'row'], + signatures: [ + { + params: [ + { name: 'left', type: 'string' }, + { name: 'right', type: 'string' }, + ], + returnType: 'boolean', + }, + ], + })), + ...[ + { + name: 'in', + description: i18n.translate('monaco.esql.definition.inDoc', { + defaultMessage: + 'Tests if the value an expression takes is contained in a list of other expressions', + }), + }, + { name: 'not_in', description: '' }, + ].map(({ name, description }) => ({ + builtin: true, + ignoreAsSuggestion: /not/.test(name), + name, + description, + supportedCommands: ['eval', 'where', 'row'], + signatures: [ + { + params: [ + { name: 'left', type: 'number' }, + { name: 'right', type: 'number[]' }, + ], + returnType: 'boolean', + }, + { + params: [ + { name: 'left', type: 'string' }, + { name: 'right', type: 'string[]' }, + ], + returnType: 'boolean', + }, + { + params: [ + { name: 'left', type: 'boolean' }, + { name: 'right', type: 'boolean[]' }, + ], + returnType: 'boolean', + }, + { + params: [ + { name: 'left', type: 'date' }, + { name: 'right', type: 'date[]' }, + ], + returnType: 'boolean', + }, + ], + })), + ...[ + { + name: 'and', + description: i18n.translate('monaco.esql.definition.andDoc', { + defaultMessage: 'and', + }), + }, + { + name: 'or', + description: i18n.translate('monaco.esql.definition.orDoc', { + defaultMessage: 'or', + }), + }, + ].map(({ name, description }) => ({ + builtin: true, + name, + description, + supportedCommands: ['eval', 'where', 'row'], + signatures: [ + { + params: [ + { name: 'left', type: 'boolean' }, + { name: 'right', type: 'boolean' }, + ], + returnType: 'boolean', + }, + ], + })), + { + builtin: true, + name: 'not', + description: i18n.translate('monaco.esql.definition.notDoc', { + defaultMessage: 'Not', + }), + supportedCommands: ['eval', 'where', 'row'], + signatures: [ + { + params: [{ name: 'expression', type: 'boolean' }], + returnType: 'boolean', + }, + ], + }, + { + builtin: true, + name: '=', + description: i18n.translate('monaco.esql.definition.assignDoc', { + defaultMessage: 'Assign (=)', + }), + supportedCommands: ['eval', 'stats', 'row', 'dissect', 'where', 'enrich'], + signatures: [ + { + params: [ + { name: 'left', type: 'any' }, + { name: 'right', type: 'any' }, + ], + returnType: 'void', + }, + ], + }, + { + name: 'functions', + description: i18n.translate('monaco.esql.definition.functionsDoc', { + defaultMessage: 'Show ES|QL avaialble functions with signatures', + }), + supportedCommands: ['show'], + signatures: [ + { + params: [], + returnType: 'void', + }, + ], + }, + { + name: 'info', + description: i18n.translate('monaco.esql.definition.infoDoc', { + defaultMessage: 'Show information about the current ES node', + }), + supportedCommands: ['show'], + signatures: [ + { + params: [], + returnType: 'void', + }, + ], + }, +]; diff --git a/packages/kbn-monaco/src/esql/lib/ast/definitions/commands.ts b/packages/kbn-monaco/src/esql/lib/ast/definitions/commands.ts new file mode 100644 index 0000000000000..4404bf00b5fad --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/ast/definitions/commands.ts @@ -0,0 +1,280 @@ +/* + * Copyright 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 { isColumnItem } from '../shared/helpers'; +import { ESQLColumn, ESQLCommand, ESQLMessage } from '../types'; +import { + appendSeparatorOption, + asOption, + byOption, + metadataOption, + onOption, + withOption, +} from './options'; +import type { CommandDefinition } from './types'; + +export const commandDefinitions: CommandDefinition[] = [ + { + name: 'row', + description: i18n.translate('monaco.esql.definitions.rowDoc', { + defaultMessage: + 'Produces a row with one or more columns with values that you specify. This can be useful for testing.', + }), + examples: ['row a=1', 'row a=1, b=2'], + signature: { + multipleParams: true, + // syntax check already validates part of this + params: [{ name: 'assignment', type: 'any' }], + }, + options: [], + }, + { + name: 'from', + description: i18n.translate('monaco.esql.definitions.fromDoc', { + defaultMessage: + 'Retrieves data from one or more data streams, indices, or aliases. In a query or subquery, you must use the from command first and it does not need a leading pipe. For example, to retrieve data from an index:', + }), + examples: ['from logs', 'from logs-*', 'from logs_*, events-*'], + options: [metadataOption], + signature: { + multipleParams: true, + params: [{ name: 'index', type: 'source', wildcards: true }], + }, + }, + { + name: 'show', + description: i18n.translate('monaco.esql.definitions.showDoc', { + defaultMessage: 'Returns information about the deployment and its capabilities', + }), + examples: ['show functions', 'show info'], + options: [], + signature: { + multipleParams: false, + params: [{ name: 'functions', type: 'string', values: ['functions', 'info'] }], + }, + }, + { + name: 'stats', + description: i18n.translate('monaco.esql.definitions.statsDoc', { + defaultMessage: + 'Calculates aggregate statistics, such as average, count, and sum, over the incoming search results set. Similar to SQL aggregation, if the stats command is used without a BY clause, only one row is returned, which is the aggregation over the entire incoming search results set. When you use a BY clause, one row is returned for each distinct value in the field specified in the BY clause. The stats command returns only the fields in the aggregation, and you can use a wide range of statistical functions with the stats command. When you perform more than one aggregation, separate each aggregation with a comma.', + }), + examples: ['… | stats avg = avg(a)', '… | stats sum(b) by b'], + signature: { + multipleParams: true, + params: [{ name: 'expression', type: 'function' }], + }, + options: [byOption], + }, + { + name: 'eval', + description: i18n.translate('monaco.esql.definitions.evalDoc', { + defaultMessage: + 'Calculates an expression and puts the resulting value into a search results field.', + }), + examples: [ + '… | eval b * c', + '… | eval a = b * c', + '… | eval then = now() + 1 year + 2 weeks', + '… | eval a = b * c, d = e * f', + ], + signature: { + multipleParams: true, + params: [{ name: 'expression', type: 'any' }], + }, + options: [], + }, + { + name: 'rename', + description: i18n.translate('monaco.esql.definitions.renameDoc', { + defaultMessage: 'Renames an old column to a new one', + }), + examples: ['… | rename old as new', '… | rename old as new, a as b'], + signature: { + multipleParams: true, + params: [{ name: 'renameClause', type: 'column' }], + }, + options: [asOption], + }, + { + name: 'limit', + description: i18n.translate('monaco.esql.definitions.limitDoc', { + defaultMessage: + 'Returns the first search results, in search order, based on the "limit" specified.', + }), + examples: ['… | limit 100', '… | limit 0'], + signature: { + multipleParams: false, + params: [{ name: 'size', type: 'number', literalOnly: true }], + }, + options: [], + }, + { + name: 'keep', + description: i18n.translate('monaco.esql.definitions.keepDoc', { + defaultMessage: 'Rearranges fields in the input table by applying the keep clauses in fields', + }), + examples: ['… | keep a', '… | keep a,b'], + options: [], + signature: { + multipleParams: true, + params: [{ name: 'column', type: 'column', wildcards: true }], + }, + validate: (command: ESQLCommand) => { + // the command name is automatically converted into KEEP by the ast_walker + // so validate the actual text + const messages: ESQLMessage[] = []; + if (/^project/.test(command.text.toLowerCase())) { + messages.push({ + location: command.location, + text: i18n.translate('monaco.esql.validation.projectCommandDeprecated', { + defaultMessage: 'PROJECT command is no longer supported, please use KEEP instead', + }), + type: 'warning', + }); + } + return messages; + }, + }, + { + name: 'drop', + description: i18n.translate('monaco.esql.definitions.dropDoc', { + defaultMessage: 'Drops columns', + }), + examples: ['… | drop a', '… | drop a,b'], + options: [], + signature: { + multipleParams: true, + params: [{ name: 'column', type: 'column', wildcards: true }], + }, + validate: (command: ESQLCommand) => { + const messages: ESQLMessage[] = []; + const wildcardItems = command.args.filter((arg) => isColumnItem(arg) && arg.name === '*'); + if (wildcardItems.length) { + messages.push( + ...wildcardItems.map((column) => ({ + location: (column as ESQLColumn).location, + text: i18n.translate('monaco.esql.validation.dropAllColumnsError', { + defaultMessage: 'Removing all fields is not allowed [*]', + }), + type: 'error' as const, + })) + ); + } + const droppingTimestamp = command.args.find( + (arg) => isColumnItem(arg) && arg.name === '@timestamp' + ); + if (droppingTimestamp) { + messages.push({ + location: (droppingTimestamp as ESQLColumn).location, + text: i18n.translate('monaco.esql.validation.dropTimestampWarning', { + defaultMessage: 'Drop [@timestamp] will remove all time filters to the search results', + }), + type: 'warning', + }); + } + return messages; + }, + }, + { + name: 'sort', + description: i18n.translate('monaco.esql.definitions.sortDoc', { + defaultMessage: + 'Sorts all results by the specified fields. By default, null values are treated as being larger than any other value. With an ascending sort order, null values are sorted last, and with a descending sort order, null values are sorted first. You can change that by providing NULLS FIRST or NULLS LAST', + }), + examples: [ + '… | sort a desc, b nulls last, c asc nulls first', + '… | sort b nulls last', + '… | sort c asc nulls first', + ], + options: [], + signature: { + multipleParams: true, + params: [ + { name: 'column', type: 'column' }, + { name: 'direction', type: 'string', optional: true, values: ['asc', 'desc'] }, + { name: 'nulls', type: 'string', optional: true, values: ['nulls first', 'nulls last'] }, + ], + }, + }, + { + name: 'where', + description: i18n.translate('monaco.esql.definitions.whereDoc', { + defaultMessage: + 'Uses "predicate-expressions" to filter search results. A predicate expression, when evaluated, returns TRUE or FALSE. The where command only returns the results that evaluate to TRUE. For example, to filter results for a specific field value', + }), + examples: ['… | where status_code == 200'], + signature: { + multipleParams: false, + params: [{ name: 'expression', type: 'boolean' }], + }, + options: [], + }, + { + name: 'dissect', + description: i18n.translate('monaco.esql.definitions.dissectDoc', { + defaultMessage: + 'Extracts multiple string values from a single string input, based on a pattern', + }), + examples: ['… | dissect a "%{b} %{c}"'], + options: [appendSeparatorOption], + signature: { + multipleParams: false, + params: [ + { name: 'column', type: 'column', innerType: 'string' }, + { name: 'pattern', type: 'string', literalOnly: true }, + ], + }, + }, + { + name: 'grok', + description: i18n.translate('monaco.esql.definitions.grokDoc', { + defaultMessage: + 'Extracts multiple string values from a single string input, based on a pattern', + }), + examples: ['… | grok a "%{IP:b} %{NUMBER:c}"'], + options: [], + signature: { + multipleParams: false, + params: [ + { name: 'column', type: 'column', innerType: 'string' }, + { name: 'pattern', type: 'string', literalOnly: true }, + ], + }, + }, + { + name: 'mv_expand', + description: i18n.translate('monaco.esql.definitions.mvExpandDoc', { + defaultMessage: 'Expands multivalued fields into one row per value, duplicating other fields', + }), + examples: ['row a=[1,2,3] | mv_expand a'], + options: [], + signature: { + multipleParams: false, + params: [{ name: 'column', type: 'column', innerType: 'list' }], + }, + }, + { + name: 'enrich', + description: i18n.translate('monaco.esql.definitions.enrichDoc', { + defaultMessage: + 'Enrich table with another table. Before you can use enrich, you need to create and execute an enrich policy.', + }), + examples: [ + '… | enrich my-policy', + '… | enrich my-policy on pivotField', + '… | enrich my-policy on pivotField with a = enrichFieldA, b = enrichFieldB', + ], + options: [onOption, withOption], + signature: { + multipleParams: false, + params: [{ name: 'policyName', type: 'source', innerType: 'policy' }], + }, + }, +]; diff --git a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/functions_commands.ts b/packages/kbn-monaco/src/esql/lib/ast/definitions/functions.ts similarity index 52% rename from packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/functions_commands.ts rename to packages/kbn-monaco/src/esql/lib/ast/definitions/functions.ts index 12056ee784695..6cabc530883aa 100644 --- a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/functions_commands.ts +++ b/packages/kbn-monaco/src/esql/lib/ast/definitions/functions.ts @@ -7,47 +7,12 @@ */ import { i18n } from '@kbn/i18n'; -import { buildDocumentation, buildFunctionDocumentation } from './utils'; +import { FunctionDefinition } from './types'; -import type { AutocompleteCommandDefinition } from '../types'; - -export const whereCommandDefinition: AutocompleteCommandDefinition[] = [ - { - label: 'cidr_match', - insertText: 'cidr_match', - kind: 1, - detail: i18n.translate('monaco.esql.autocomplete.cidrMatchDoc', { - defaultMessage: - 'The function takes a first parameter of type IP, followed by one or more parameters evaluated to a CIDR specificatione.', - }), - documentation: { - value: buildDocumentation('cidr_match(grouped[T]): aggregated[T]', [ - 'from index | eval cidr="10.0.0.0/8" | where cidr_match(ip_field, "127.0.0.1/30", cidr)', - ]), - }, - sortText: 'C', - }, -]; - -interface FunctionDefinition { - name: string; - description: string; - signatures: Array<{ - params: Array<{ - name: string; - type: string | string[]; - optional?: boolean; - }>; - infiniteParams?: boolean; - returnType: string; - examples?: string[]; - }>; -} - -const mathCommandFullDefinitions: FunctionDefinition[] = [ +export const evalFunctionsDefinitions: FunctionDefinition[] = [ { name: 'round', - description: i18n.translate('monaco.esql.autocomplete.roundDoc', { + description: i18n.translate('monaco.esql.definitions.roundDoc', { defaultMessage: 'Returns a number rounded to the decimal, specified by he closest integer value. The default is to round to an integer.', }), @@ -55,39 +20,39 @@ const mathCommandFullDefinitions: FunctionDefinition[] = [ { params: [{ name: 'field', type: 'number' }], returnType: 'number', - examples: [`from index where field="value" | eval round_value = round(field)`], + examples: [`from index | eval round_value = round(field)`], }, ], }, { name: 'abs', - description: i18n.translate('monaco.esql.autocomplete.absDoc', { + description: i18n.translate('monaco.esql.definitions.absDoc', { defaultMessage: 'Returns the absolute value.', }), signatures: [ { params: [{ name: 'field', type: 'number' }], returnType: 'number', - examples: [`from index where field="value" | eval abs_value = abs(field)`], + examples: [`from index | eval abs_value = abs(field)`], }, ], }, { name: 'log10', - description: i18n.translate('monaco.esql.autocomplete.log10Doc', { + description: i18n.translate('monaco.esql.definitions.log10Doc', { defaultMessage: 'Returns the log base 10.', }), signatures: [ { params: [{ name: 'field', type: 'number' }], returnType: 'number', - examples: [`from index where field="value" | eval log10_value = log10(field)`], + examples: [`from index | eval log10_value = log10(field)`], }, ], }, { name: 'pow', - description: i18n.translate('monaco.esql.autocomplete.powDoc', { + description: i18n.translate('monaco.esql.definitions.powDoc', { defaultMessage: 'Returns the the value of a base (first argument) raised to a power (second argument).', }), @@ -98,31 +63,48 @@ const mathCommandFullDefinitions: FunctionDefinition[] = [ { name: 'exponent', type: 'number' }, ], returnType: 'number', - examples: ['from index where field="value" | eval s = POW(field, exponent)'], + examples: ['from index | eval s = POW(field, exponent)'], }, ], }, { name: 'concat', - description: i18n.translate('monaco.esql.autocomplete.concatDoc', { + description: i18n.translate('monaco.esql.definitions.concatDoc', { defaultMessage: 'Concatenates two or more strings.', }), signatures: [ { params: [{ name: 'field', type: 'string' }], infiniteParams: true, + minParams: 1, returnType: 'string', - examples: [ - 'from index where field="value" | eval concatenated = concat(field1, "-", field2)', + examples: ['from index | eval concatenated = concat(field1, "-", field2)'], + }, + ], + }, + { + name: 'replace', + description: i18n.translate('monaco.esql.definitions.replaceDoc', { + defaultMessage: + 'The function substitutes in the string (1st argument) any match of the regular expression (2nd argument) with the replacement string (3rd argument). If any of the arguments are NULL, the result is NULL.', + }), + signatures: [ + { + params: [ + { name: 'field', type: 'string' }, + { name: 'regexp', type: 'string' }, + { name: 'replacement', type: 'string' }, ], + returnType: 'string', + examples: ['from index | eval newStr = replace(field, "Hello", "World")'], }, ], }, { name: 'substring', - description: i18n.translate('monaco.esql.autocomplete.substringDoc', { + description: i18n.translate('monaco.esql.definitions.substringDoc', { defaultMessage: - 'Returns a substring of a string, specified by a start position and an optional length. This example returns the first three characters of every last name.', + 'Returns a substring of a string, specified by a start position and an optional length.', }), signatures: [ { @@ -132,26 +114,26 @@ const mathCommandFullDefinitions: FunctionDefinition[] = [ { name: 'endIndex', type: 'number' }, ], returnType: 'string', - examples: ['from index where field="value" | eval new_string = substring(field, 1, 3)'], + examples: ['from index | eval new_string = substring(field, 1, 3)'], }, ], }, { name: 'trim', - description: i18n.translate('monaco.esql.autocomplete.trimDoc', { + description: i18n.translate('monaco.esql.definitions.trimDoc', { defaultMessage: 'Removes leading and trailing whitespaces from strings.', }), signatures: [ { params: [{ name: 'field', type: 'string' }], returnType: 'string', - examples: ['from index where field="value" | eval new_string = trim(field)'], + examples: ['from index | eval new_string = trim(field)'], }, ], }, { name: 'starts_with', - description: i18n.translate('monaco.esql.autocomplete.startsWithDoc', { + description: i18n.translate('monaco.esql.definitions.startsWithDoc', { defaultMessage: 'Returns a boolean that indicates whether a keyword string starts with another string.', }), @@ -162,13 +144,30 @@ const mathCommandFullDefinitions: FunctionDefinition[] = [ { name: 'prefix', type: 'string' }, ], returnType: 'boolean', - examples: ['from index where field="value" | eval new_string = starts_with(field, "a")'], + examples: ['from index | eval starts_with_a = starts_with(field, "a")'], + }, + ], + }, + { + name: 'ends_with', + description: i18n.translate('monaco.esql.definitions.endsWithDoc', { + defaultMessage: + 'Returns a boolean that indicates whether a keyword string ends with another string:', + }), + signatures: [ + { + params: [ + { name: 'field', type: 'string' }, + { name: 'prefix', type: 'string' }, + ], + returnType: 'boolean', + examples: ['from index | eval ends_with_a = ends_with(field, "a")'], }, ], }, { name: 'split', - description: i18n.translate('monaco.esql.autocomplete.splitDoc', { + description: i18n.translate('monaco.esql.definitions.splitDoc', { defaultMessage: 'Splits a single valued string into multiple strings.', }), signatures: [ @@ -184,173 +183,196 @@ const mathCommandFullDefinitions: FunctionDefinition[] = [ }, { name: 'to_string', - description: i18n.translate('monaco.esql.autocomplete.toStringDoc', { + alias: ['to_str'], + description: i18n.translate('monaco.esql.definitions.toStringDoc', { defaultMessage: 'Converts to string.', }), signatures: [ { params: [{ name: 'field', type: 'any' }], returnType: 'string', - examples: [`from index where field="value"" | EVAL string = to_string(field)`], + examples: [`from index" | EVAL string = to_string(field)`], }, ], }, { name: 'to_boolean', - description: i18n.translate('monaco.esql.autocomplete.toBooleanDoc', { + alias: ['to_bool'], + description: i18n.translate('monaco.esql.definitions.toBooleanDoc', { defaultMessage: 'Converts to boolean.', }), signatures: [ { params: [{ name: 'field', type: 'any' }], returnType: 'boolean', - examples: [`from index where field="value"" | EVAL bool = to_boolean(field)`], + examples: [`from index" | EVAL bool = to_boolean(field)`], }, ], }, { name: 'to_datetime', - description: i18n.translate('monaco.esql.autocomplete.toDateTimeDoc', { + alias: ['to_dt'], + description: i18n.translate('monaco.esql.definitions.toDateTimeDoc', { defaultMessage: 'Converts to date.', }), signatures: [ { params: [{ name: 'field', type: 'any' }], returnType: 'date', - examples: [`from index where field="value"" | EVAL datetime = to_datetime(field)`], + examples: [`from index" | EVAL datetime = to_datetime(field)`], }, ], }, { name: 'to_degrees', - description: i18n.translate('monaco.esql.autocomplete.toDegreesDoc', { + description: i18n.translate('monaco.esql.definitions.toDegreesDoc', { defaultMessage: 'Coverts to degrees', }), signatures: [ { params: [{ name: 'field', type: 'number' }], returnType: 'number', - examples: [`from index where field="value" | eval degrees = to_degrees(field)`], + examples: [`from index | eval degrees = to_degrees(field)`], }, ], }, { name: 'to_double', - description: i18n.translate('monaco.esql.autocomplete.toDoubleDoc', { + alias: ['to_dbl'], + description: i18n.translate('monaco.esql.definitions.toDoubleDoc', { defaultMessage: 'Converts to double.', }), signatures: [ { params: [{ name: 'field', type: 'any' }], returnType: 'number', - examples: [`from index where field="value"" | EVAL double = to_double(field)`], + examples: [`from index | EVAL double = to_double(field)`], + }, + ], + }, + { + name: 'to_geopoint', + description: i18n.translate('monaco.esql.definitions.toGeopointDoc', { + defaultMessage: 'Converts to geo_point.', + }), + signatures: [ + { + params: [{ name: 'field', type: 'any' }], + returnType: 'geo_point', + examples: [`from index | EVAL geopoint = to_geopoint(field)`], }, ], }, { name: 'to_integer', - description: i18n.translate('monaco.esql.autocomplete.toIntegerDoc', { + alias: ['to_int'], + description: i18n.translate('monaco.esql.definitions.toIntegerDoc', { defaultMessage: 'Converts to integer.', }), signatures: [ { params: [{ name: 'field', type: 'any' }], returnType: 'number', - examples: [`from index where field="value"" | EVAL integer = to_integer(field)`], + examples: [`from index | EVAL integer = to_integer(field)`], }, ], }, { name: 'to_long', - description: i18n.translate('monaco.esql.autocomplete.toLongDoc', { + description: i18n.translate('monaco.esql.definitions.toLongDoc', { defaultMessage: 'Converts to long.', }), signatures: [ { params: [{ name: 'field', type: 'any' }], returnType: 'number', - examples: [`from index where field="value"" | EVAL long = to_long(field)`], + examples: [`from index | EVAL long = to_long(field)`], }, ], }, { name: 'to_radians', - description: i18n.translate('monaco.esql.autocomplete.toRadiansDoc', { + description: i18n.translate('monaco.esql.definitions.toRadiansDoc', { defaultMessage: 'Converts to radians', }), signatures: [ { params: [{ name: 'field', type: 'number' }], returnType: 'number', - examples: [`from index where field="value" | eval radians = to_radians(field)`], + examples: [`from index | eval radians = to_radians(field)`], }, ], }, { name: 'to_unsigned_long', - description: i18n.translate('monaco.esql.autocomplete.toUnsignedLongDoc', { + alias: ['to_ul', 'to_ulong'], + description: i18n.translate('monaco.esql.definitions.toUnsignedLongDoc', { defaultMessage: 'Converts to unsigned long.', }), signatures: [ { params: [{ name: 'field', type: 'any' }], returnType: 'number', - examples: [ - `from index where field="value"" | EVAL unsigned_long = to_unsigned_long(field)`, - ], + examples: [`from index | EVAL unsigned_long = to_unsigned_long(field)`], }, ], }, { name: 'to_ip', - description: i18n.translate('monaco.esql.autocomplete.toIpDoc', { + description: i18n.translate('monaco.esql.definitions.toIpDoc', { defaultMessage: 'Converts to ip.', }), signatures: [ { params: [{ name: 'field', type: 'any' }], - returnType: 'string[]', - examples: [`from index where field="value"" | EVAL ip = to_ip(field)`], + returnType: 'ip', + examples: [`from index | EVAL ip = to_ip(field)`], }, ], }, { name: 'to_version', - description: i18n.translate('monaco.esql.autocomplete.toVersionDoc', { + alias: ['to_ver'], + description: i18n.translate('monaco.esql.definitions.toVersionDoc', { defaultMessage: 'Converts to version.', }), signatures: [ { - params: [{ name: 'field', type: ['string', 'version'] }], + params: [{ name: 'field', type: 'string' }], returnType: 'version', - examples: [`from index where field="value"" | EVAL version = to_version(field)`], + examples: [`from index | EVAL version = to_version(stringField)`], + }, + { + params: [{ name: 'field', type: 'version' }], + returnType: 'version', + examples: [`from index | EVAL version = to_version(versionField)`], }, ], }, { name: 'date_extract', - description: i18n.translate('monaco.esql.autocomplete.dateExtractDoc', { + description: i18n.translate('monaco.esql.definitions.dateExtractDoc', { defaultMessage: `Extracts parts of a date, like year, month, day, hour. The supported field types are those provided by java.time.temporal.ChronoField`, }), signatures: [ { params: [ - { name: 'field', type: 'date' }, { name: 'date_part', - type: 'string', + type: 'chrono_literal', }, + { name: 'field', type: 'date' }, ], returnType: 'number', examples: [ - `ROW date = DATE_PARSE("2022-05-06", "yyyy-MM-dd") | EVAL year = DATE_EXTRACT(date, "year")`, + `ROW date = DATE_PARSE("yyyy-MM-dd", "2022-05-06") | EVAL year = DATE_EXTRACT("year", date)`, ], }, ], }, { name: 'date_format', - description: i18n.translate('monaco.esql.autocomplete.dateFormatDoc', { + description: i18n.translate('monaco.esql.definitions.dateFormatDoc', { defaultMessage: `Returns a string representation of a date in the provided format. If no format is specified, the "yyyy-MM-dd'T'HH:mm:ss.SSSZ" format is used.`, }), signatures: [ @@ -360,15 +382,13 @@ const mathCommandFullDefinitions: FunctionDefinition[] = [ { name: 'format_string', type: 'string', optional: true }, ], returnType: 'string', - examples: [ - 'from index where field="value" | eval hired = date_format(hire_date, "YYYY-MM-dd")', - ], + examples: ['from index | eval hired = date_format("YYYY-MM-dd", hire_date)'], }, ], }, { name: 'date_trunc', - description: i18n.translate('monaco.esql.autocomplete.dateTruncDoc', { + description: i18n.translate('monaco.esql.definitions.dateTruncDoc', { defaultMessage: `Rounds down a date to the closest interval. Intervals can be expressed using the timespan literal syntax.`, }), signatures: [ @@ -378,15 +398,13 @@ const mathCommandFullDefinitions: FunctionDefinition[] = [ { name: 'field', type: 'date' }, ], returnType: 'date', - examples: [ - `from index where field="value" | eval year_hired = DATE_TRUNC(1 year, hire_date)`, - ], + examples: [`from index | eval year_hired = DATE_TRUNC(1 year, hire_date)`], }, ], }, { name: 'date_parse', - description: i18n.translate('monaco.esql.autocomplete.dateParseDoc', { + description: i18n.translate('monaco.esql.definitions.dateParseDoc', { defaultMessage: `Parse dates from strings.`, }), signatures: [ @@ -397,14 +415,14 @@ const mathCommandFullDefinitions: FunctionDefinition[] = [ ], returnType: 'date', examples: [ - `from index where field="value" | eval year_hired = date_parse(hire_date, yyyy-MM-dd'T'HH:mm:ss.SSS'Z')`, + `from index | eval year_hired = date_parse("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", hire_date)`, ], }, ], }, { name: 'auto_bucket', - description: i18n.translate('monaco.esql.autocomplete.autoBucketDoc', { + description: i18n.translate('monaco.esql.definitions.autoBucketDoc', { defaultMessage: `Automatically bucket dates based on a given range and bucket target.`, }), signatures: [ @@ -417,7 +435,7 @@ const mathCommandFullDefinitions: FunctionDefinition[] = [ ], returnType: 'date', examples: [ - 'from index where field="value" | eval hd = auto_bucket(hire_date, 20, "1985-01-01T00:00:00Z", "1986-01-01T00:00:00Z")', + 'from index | eval hd = auto_bucket(hire_date, 20, "1985-01-01T00:00:00Z", "1986-01-01T00:00:00Z")', ], }, { @@ -428,113 +446,124 @@ const mathCommandFullDefinitions: FunctionDefinition[] = [ { name: 'endValue', type: 'number' }, ], returnType: 'number', - examples: [ - 'from index where field="value" | eval bs = auto_bucket(salary, 20, 25324, 74999)', - ], + examples: ['from index | eval bs = auto_bucket(salary, 20, 25324, 74999)'], }, ], }, { name: 'is_finite', - description: i18n.translate('monaco.esql.autocomplete.isFiniteDoc', { + description: i18n.translate('monaco.esql.definitions.isFiniteDoc', { defaultMessage: 'Returns a boolean that indicates whether its input is a finite number.', }), signatures: [ { params: [{ name: 'field', type: 'number' }], returnType: 'boolean', - examples: ['from index where field="value" | eval s = is_finite(field/0)'], + examples: ['from index | eval s = is_finite(field/0)'], }, ], }, { name: 'is_infinite', - description: i18n.translate('monaco.esql.autocomplete.isInfiniteDoc', { + description: i18n.translate('monaco.esql.definitions.isInfiniteDoc', { defaultMessage: 'Returns a boolean that indicates whether its input is infinite.', }), signatures: [ { params: [{ name: 'field', type: 'number' }], returnType: 'boolean', - examples: ['from index where field="value" | eval s = is_infinite(field/0)'], + examples: ['from index | eval s = is_infinite(field/0)'], + }, + ], + }, + { + name: 'is_nan', + description: i18n.translate('monaco.esql.definitions.isNanDoc', { + defaultMessage: 'Returns a boolean that indicates whether its input is not a number.', + }), + signatures: [ + { + params: [{ name: 'field', type: 'number' }], + returnType: 'boolean', + examples: ['row a = 1 | eval is_nan(a)'], }, ], }, { name: 'case', - description: i18n.translate('monaco.esql.autocomplete.caseDoc', { + description: i18n.translate('monaco.esql.definitions.caseDoc', { defaultMessage: 'Accepts pairs of conditions and values. The function returns the value that belongs to the first condition that evaluates to `true`. If the number of arguments is odd, the last argument is the default value which is returned when no condition matches.', }), signatures: [ { params: [ - { name: 'condition', type: 'booleanExpression' }, + { name: 'condition', type: 'boolean' }, { name: 'value', type: 'any' }, ], - infiniteParams: true, + minParams: 3, returnType: 'any', examples: [ - `from index where field="value" | eval type = case(languages <= 1, "monolingual", languages <= 2, "bilingual", "polyglot")`, + `from index | eval type = case(languages <= 1, "monolingual", languages <= 2, "bilingual", "polyglot")`, ], }, ], }, { name: 'length', - description: i18n.translate('monaco.esql.autocomplete.lengthDoc', { + description: i18n.translate('monaco.esql.definitions.lengthDoc', { defaultMessage: 'Returns the character length of a string.', }), signatures: [ { params: [{ name: 'field', type: 'string' }], returnType: 'number', - examples: [`from index where field="value" | eval fn_length = length(field)`], + examples: [`from index | eval fn_length = length(field)`], }, ], }, { name: 'acos', - description: i18n.translate('monaco.esql.autocomplete.acosDoc', { + description: i18n.translate('monaco.esql.definitions.acosDoc', { defaultMessage: 'Inverse cosine trigonometric function', }), signatures: [ { params: [{ name: 'field', type: 'number' }], returnType: 'number', - examples: [`from index where field="value" | eval acos = acos(field)`], + examples: [`from index | eval acos = acos(field)`], }, ], }, { name: 'asin', - description: i18n.translate('monaco.esql.autocomplete.asinDoc', { + description: i18n.translate('monaco.esql.definitions.asinDoc', { defaultMessage: 'Inverse sine trigonometric function', }), signatures: [ { params: [{ name: 'field', type: 'number' }], returnType: 'number', - examples: [`from index where field="value" | eval asin = asin(field)`], + examples: [`from index | eval asin = asin(field)`], }, ], }, { name: 'atan', - description: i18n.translate('monaco.esql.autocomplete.atanDoc', { + description: i18n.translate('monaco.esql.definitions.atanDoc', { defaultMessage: 'Inverse tangent trigonometric function', }), signatures: [ { params: [{ name: 'field', type: 'number' }], returnType: 'number', - examples: [`from index where field="value" | eval atan = atan(field)`], + examples: [`from index | eval atan = atan(field)`], }, ], }, { name: 'atan2', - description: i18n.translate('monaco.esql.autocomplete.atan2Doc', { + description: i18n.translate('monaco.esql.definitions.atan2Doc', { defaultMessage: 'The angle between the positive x-axis and the ray from the origin to the point (x , y) in the Cartesian plane', }), @@ -545,13 +574,13 @@ const mathCommandFullDefinitions: FunctionDefinition[] = [ { name: 'y', type: 'number' }, ], returnType: 'number', - examples: [`from index where field="value" | eval atan2 = atan2(x, y)`], + examples: [`from index | eval atan2 = atan2(x, y)`], }, ], }, { name: 'coalesce', - description: i18n.translate('monaco.esql.autocomplete.coalesceDoc', { + description: i18n.translate('monaco.esql.definitions.coalesceDoc', { defaultMessage: 'Returns the first non-null value.', }), signatures: [ @@ -565,46 +594,46 @@ const mathCommandFullDefinitions: FunctionDefinition[] = [ }, { name: 'cos', - description: i18n.translate('monaco.esql.autocomplete.cosDoc', { + description: i18n.translate('monaco.esql.definitions.cosDoc', { defaultMessage: 'Cosine trigonometric function', }), signatures: [ { params: [{ name: 'field', type: 'number' }], returnType: 'number', - examples: [`from index where field="value" | eval cos = cos(field)`], + examples: [`from index | eval cos = cos(field)`], }, ], }, { name: 'cosh', - description: i18n.translate('monaco.esql.autocomplete.coshDoc', { + description: i18n.translate('monaco.esql.definitions.coshDoc', { defaultMessage: 'Cosine hyperbolic function', }), signatures: [ { params: [{ name: 'field', type: 'number' }], returnType: 'number', - examples: [`from index where field="value" | eval cosh = cosh(field)`], + examples: [`from index | eval cosh = cosh(field)`], }, ], }, { name: 'floor', - description: i18n.translate('monaco.esql.autocomplete.floorDoc', { + description: i18n.translate('monaco.esql.definitions.floorDoc', { defaultMessage: 'Round a number down to the nearest integer.', }), signatures: [ { params: [{ name: 'field', type: 'number' }], returnType: 'number', - examples: [`from index where field="value" | eval a = floor(field)`], + examples: [`from index | eval a = floor(field)`], }, ], }, { name: 'greatest', - description: i18n.translate('monaco.esql.autocomplete.greatestDoc', { + description: i18n.translate('monaco.esql.definitions.greatestDoc', { defaultMessage: 'Returns the maximum value from many columns.', }), signatures: [ @@ -618,7 +647,7 @@ const mathCommandFullDefinitions: FunctionDefinition[] = [ }, { name: 'left', - description: i18n.translate('monaco.esql.autocomplete.leftDoc', { + description: i18n.translate('monaco.esql.definitions.leftDoc', { defaultMessage: 'Return the substring that extracts length chars from the string starting from the left.', }), @@ -629,13 +658,13 @@ const mathCommandFullDefinitions: FunctionDefinition[] = [ { name: 'length', type: 'number' }, ], returnType: 'string', - examples: [`from index where field="value" | eval substr = left(field, 3)`], + examples: [`from index | eval substr = left(field, 3)`], }, ], }, { name: 'ltrim', - description: i18n.translate('monaco.esql.autocomplete.ltrimDoc', { + description: i18n.translate('monaco.esql.definitions.ltrimDoc', { defaultMessage: 'Removes leading whitespaces from strings.', }), signatures: [ @@ -648,7 +677,7 @@ const mathCommandFullDefinitions: FunctionDefinition[] = [ }, { name: 'now', - description: i18n.translate('monaco.esql.autocomplete.nowDoc', { + description: i18n.translate('monaco.esql.definitions.nowDoc', { defaultMessage: 'Returns current date and time.', }), signatures: [ @@ -661,7 +690,7 @@ const mathCommandFullDefinitions: FunctionDefinition[] = [ }, { name: 'right', - description: i18n.translate('monaco.esql.autocomplete.rightDoc', { + description: i18n.translate('monaco.esql.definitions.rightDoc', { defaultMessage: 'Return the substring that extracts length chars from the string starting from the right.', }), @@ -672,13 +701,13 @@ const mathCommandFullDefinitions: FunctionDefinition[] = [ { name: 'length', type: 'number' }, ], returnType: 'string', - examples: [`from index where field="value" | eval string = right(field, 3)`], + examples: [`from index | eval string = right(field, 3)`], }, ], }, { name: 'rtrim', - description: i18n.translate('monaco.esql.autocomplete.rtrimDoc', { + description: i18n.translate('monaco.esql.definitions.rtrimDoc', { defaultMessage: 'Removes trailing whitespaces from strings.', }), signatures: [ @@ -691,7 +720,7 @@ const mathCommandFullDefinitions: FunctionDefinition[] = [ }, { name: 'sin', - description: i18n.translate('monaco.esql.autocomplete.sinDoc', { + description: i18n.translate('monaco.esql.definitions.sinDoc', { defaultMessage: 'Sine trigonometric function.', }), signatures: [ @@ -704,7 +733,7 @@ const mathCommandFullDefinitions: FunctionDefinition[] = [ }, { name: 'sinh', - description: i18n.translate('monaco.esql.autocomplete.sinhDoc', { + description: i18n.translate('monaco.esql.definitions.sinhDoc', { defaultMessage: 'Sine hyperbolic function.', }), signatures: [ @@ -717,7 +746,7 @@ const mathCommandFullDefinitions: FunctionDefinition[] = [ }, { name: 'sqrt', - description: i18n.translate('monaco.esql.autocomplete.sqrtDoc', { + description: i18n.translate('monaco.esql.definitions.sqrtDoc', { defaultMessage: 'Returns the square root of a number. ', }), signatures: [ @@ -730,7 +759,7 @@ const mathCommandFullDefinitions: FunctionDefinition[] = [ }, { name: 'tan', - description: i18n.translate('monaco.esql.autocomplete.tanDoc', { + description: i18n.translate('monaco.esql.definitions.tanDoc', { defaultMessage: 'Tangent trigonometric function.', }), signatures: [ @@ -743,7 +772,7 @@ const mathCommandFullDefinitions: FunctionDefinition[] = [ }, { name: 'tanh', - description: i18n.translate('monaco.esql.autocomplete.tanhDoc', { + description: i18n.translate('monaco.esql.definitions.tanhDoc', { defaultMessage: 'Tangent hyperbolic function.', }), signatures: [ @@ -754,167 +783,180 @@ const mathCommandFullDefinitions: FunctionDefinition[] = [ }, ], }, -].sort(({ name: a }, { name: b }) => a.localeCompare(b)); - -function printArguments({ - name, - type, - optional, - reference, -}: { - name: string; - type: string | string[]; - optional?: boolean; - reference?: string; -}): string { - return `${name}${optional ? ':?' : ':'} ${Array.isArray(type) ? type.join(' | ') : type}`; -} - -export const mathCommandDefinition: AutocompleteCommandDefinition[] = - mathCommandFullDefinitions.map(({ name, description, signatures }) => ({ - label: name, - insertText: name, - kind: 1, - detail: description, - documentation: { - value: buildFunctionDocumentation( - signatures.map(({ params, returnType, infiniteParams, examples }) => ({ - declaration: `${name}(${params.map(printArguments).join(', ')}${ - infiniteParams ? ` ,[... ${params.map(printArguments)}]` : '' - }): ${returnType}`, - examples, - })) - ), - }, - sortText: 'C', - })); - -export const aggregationFunctionsDefinitions: AutocompleteCommandDefinition[] = [ - { - label: 'avg', - insertText: 'avg', - kind: 1, - detail: i18n.translate('monaco.esql.autocomplete.avgDoc', { - defaultMessage: 'Returns the average of the values in a field', - }), - documentation: { - value: buildDocumentation('avg(grouped[T]): aggregated[T]', [ - 'from index | stats average = avg(field)', - ]), - }, - sortText: 'C', - }, - { - label: 'max', - insertText: 'max', - kind: 1, - detail: i18n.translate('monaco.esql.autocomplete.maxDoc', { - defaultMessage: 'Returns the maximum value in a field.', - }), - documentation: { - value: buildDocumentation('max(grouped[T]): aggregated[T]', [ - 'from index | stats max = max(field)', - ]), - }, - sortText: 'C', - }, - { - label: 'min', - insertText: 'min', - kind: 1, - detail: i18n.translate('monaco.esql.autocomplete.minDoc', { - defaultMessage: 'Returns the minimum value in a field.', - }), - documentation: { - value: buildDocumentation('min(grouped[T]): aggregated[T]', [ - 'from index | stats min = min(field)', - ]), - }, - sortText: 'C', - }, - { - label: 'sum', - insertText: 'sum', - kind: 1, - detail: i18n.translate('monaco.esql.autocomplete.sumDoc', { - defaultMessage: 'Returns the sum of the values in a field.', - }), - documentation: { - value: buildDocumentation('sum(grouped[T]): aggregated[T]', [ - 'from index | stats sum = sum(field)', - ]), - }, - sortText: 'C', - }, - { - label: 'count', - insertText: 'count', - kind: 1, - detail: i18n.translate('monaco.esql.autocomplete.countDoc', { - defaultMessage: 'Returns the count of the values in a field.', - }), - documentation: { - value: buildDocumentation('count(grouped[T]): aggregated[T]', [ - 'from index | stats count = count(field)', - ]), - }, - sortText: 'C', - }, - { - label: 'count_distinct', - insertText: 'count_distinct', - kind: 1, - detail: i18n.translate('monaco.esql.autocomplete.countDistinctDoc', { - defaultMessage: 'Returns the count of distinct values in a field.', - }), - documentation: { - value: buildDocumentation('count(grouped[T]): aggregated[T]', [ - 'from index | stats count = count_distinct(field)', - ]), - }, - sortText: 'C', - }, - { - label: 'median', - insertText: 'median', - kind: 1, - detail: i18n.translate('monaco.esql.autocomplete.medianDoc', { - defaultMessage: 'Returns the 50% percentile.', - }), - documentation: { - value: buildDocumentation('count(grouped[T]): aggregated[T]', [ - 'from index | stats count = median(field)', - ]), - }, - sortText: 'C', - }, - { - label: 'median_absolute_deviation', - insertText: 'median_absolute_deviation', - kind: 1, - detail: i18n.translate('monaco.esql.autocomplete.medianDeviationDoc', { + { + name: 'cidr_match', + description: i18n.translate('monaco.esql.definitions.cidrMatchDoc', { + defaultMessage: + 'The function takes a first parameter of type IP, followed by one or more parameters evaluated to a CIDR specificatione.', + }), + signatures: [ + { + minParams: 2, + params: [ + { name: 'ip', type: 'ip' }, + { name: 'cidr_block', type: 'string' }, + ], + returnType: 'boolean', + examples: [ + 'from index | where cidr_match(ip_field, "127.0.0.1/30")', + 'from index | eval cidr="10.0.0.0/8" | where cidr_match(ip_field, "127.0.0.1/30", cidr)', + ], + }, + ], + }, + { + name: 'mv_avg', + description: i18n.translate('monaco.esql.definitions.mvAvgDoc', { defaultMessage: - 'Returns the median of each data point’s deviation from the median of the entire sample.', - }), - documentation: { - value: buildDocumentation('count(grouped[T]): aggregated[T]', [ - 'from index | stats count = median_absolute_deviation(field)', - ]), - }, - sortText: 'C', - }, - { - label: 'percentile', - insertText: 'percentile', - kind: 1, - detail: i18n.translate('monaco.esql.autocomplete.percentiletDoc', { - defaultMessage: 'Returns the n percentile of a field.', - }), - documentation: { - value: buildDocumentation('percentile(grouped[T]): aggregated[T]', [ - 'from index | stats pct = percentile(field, 90)', - ]), - }, - sortText: 'C', - }, -]; + 'Converts a multivalued field into a single valued field containing the average of all of the values.', + }), + signatures: [ + { + params: [{ name: 'multivalue', type: 'number[]' }], + returnType: 'number', + examples: ['row a = [1, 2, 3] | mv_avg(a)'], + }, + ], + }, + { + name: 'mv_concat', + description: i18n.translate('monaco.esql.definitions.mvConcatDoc', { + defaultMessage: + 'Converts a multivalued string field into a single valued field containing the concatenation of all values separated by a delimiter', + }), + signatures: [ + { + params: [ + { name: 'multivalue', type: 'string[]' }, + { name: 'delimeter', type: 'string' }, + ], + returnType: 'string', + examples: ['row a = ["1", "2", "3"] | mv_concat(a, ", ")'], + }, + ], + }, + { + name: 'mv_count', + description: i18n.translate('monaco.esql.definitions.mvCountDoc', { + defaultMessage: + 'Converts a multivalued field into a single valued field containing a count of the number of values', + }), + signatures: [ + { + params: [{ name: 'multivalue', type: 'any[]' }], + returnType: 'number', + examples: ['row a = [1, 2, 3] | eval mv_count(a)'], + }, + ], + }, + { + name: 'mv_dedupe', + description: i18n.translate('monaco.esql.definitions.mvDedupeDoc', { + defaultMessage: 'Removes duplicates from a multivalued field', + }), + signatures: [ + { + params: [{ name: 'multivalue', type: 'any[]' }], + returnType: 'any[]', + examples: ['row a = [2, 2, 3] | eval mv_dedupe(a)'], + }, + ], + }, + { + name: 'mv_max', + description: i18n.translate('monaco.esql.definitions.mvMaxDoc', { + defaultMessage: + 'Converts a multivalued field into a single valued field containing the maximum value.', + }), + signatures: [ + { + params: [{ name: 'multivalue', type: 'number[]' }], + returnType: 'number', + examples: ['row a = [1, 2, 3] | eval mv_max(a)'], + }, + ], + }, + { + name: 'mv_min', + description: i18n.translate('monaco.esql.definitions.mvMinDoc', { + defaultMessage: + 'Converts a multivalued field into a single valued field containing the minimum value.', + }), + signatures: [ + { + params: [{ name: 'multivalue', type: 'number[]' }], + returnType: 'number', + examples: ['row a = [1, 2, 3] | eval mv_min(a)'], + }, + ], + }, + { + name: 'mv_median', + description: i18n.translate('monaco.esql.definitions.mvMedianDoc', { + defaultMessage: + 'Converts a multivalued field into a single valued field containing the median value.', + }), + signatures: [ + { + params: [{ name: 'multivalue', type: 'number[]' }], + returnType: 'number', + examples: ['row a = [1, 2, 3] | eval mv_median(a)'], + }, + ], + }, + { + name: 'mv_sum', + description: i18n.translate('monaco.esql.definitions.mvSumDoc', { + defaultMessage: + 'Converts a multivalued field into a single valued field containing the sum of all of the values.', + }), + signatures: [ + { + params: [{ name: 'multivalue', type: 'number[]' }], + returnType: 'number', + examples: ['row a = [1, 2, 3] | eval mv_sum(a)'], + }, + ], + }, + { + name: 'pi', + description: i18n.translate('monaco.esql.definitions.piDoc', { + defaultMessage: 'The ratio of a circle’s circumference to its diameter.', + }), + signatures: [ + { + params: [], + returnType: 'number', + examples: ['row a = 1 | eval pi()'], + }, + ], + }, + { + name: 'e', + description: i18n.translate('monaco.esql.definitions.eDoc', { + defaultMessage: 'Euler’s number.', + }), + signatures: [ + { + params: [], + returnType: 'number', + examples: ['row a = 1 | eval e()'], + }, + ], + }, + { + name: 'tau', + description: i18n.translate('monaco.esql.definitions.tauDoc', { + defaultMessage: 'The ratio of a circle’s circumference to its radius.', + }), + signatures: [ + { + params: [], + returnType: 'number', + examples: ['row a = 1 | eval tau()'], + }, + ], + }, +] + .sort(({ name: a }, { name: b }) => a.localeCompare(b)) + .map((def) => ({ ...def, supportedCommands: ['eval', 'where', 'row'] })); diff --git a/packages/kbn-monaco/src/esql/lib/ast/definitions/helpers.ts b/packages/kbn-monaco/src/esql/lib/ast/definitions/helpers.ts new file mode 100644 index 0000000000000..409aaf762475e --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/ast/definitions/helpers.ts @@ -0,0 +1,101 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { CommandDefinition, CommandOptionsDefinition, FunctionDefinition } from './types'; + +export function getCommandOrOptionsSignature({ + name, + signature, + ...rest +}: CommandDefinition | CommandOptionsDefinition): string { + const args = signature.params + .map(({ name: argName, type }) => { + return `<${argName}>`; + }) + .join(' '); + const optionArgs = + 'options' in rest ? rest.options.map(getCommandOrOptionsSignature).join(' ') : ''; + const signatureString = `${name.toUpperCase()} ${args}${ + signature.multipleParams ? `[, ${args}]` : '' + }${optionArgs ? ' ' + optionArgs : ''}`; + if ('wrapped' in rest && rest.wrapped) { + return `${rest.wrapped[0]}${signatureString}${rest.wrapped[1]}${rest.optional ? '?' : ''}`; + } + return signatureString; +} + +export function getFunctionSignatures( + { name, signatures }: FunctionDefinition, + { withTypes }: { withTypes: boolean } = { withTypes: true } +) { + return signatures.map(({ params, returnType, infiniteParams, examples }) => ({ + declaration: `${name}(${params.map((arg) => printArguments(arg, withTypes)).join(', ')}${ + infiniteParams ? ` ,[... ${params.map((arg) => printArguments(arg, withTypes))}]` : '' + })${withTypes ? `: ${returnType}` : ''}`, + examples, + })); +} + +export function getCommandSignature( + { name, signature, options, examples }: CommandDefinition, + { withTypes }: { withTypes: boolean } = { withTypes: true } +) { + return { + declaration: `${name} ${printCommandArguments(signature, withTypes)} ${options.map( + (option) => + `${option.wrapped ? option.wrapped[0] : ''}${option.name} ${printCommandArguments( + option.signature, + withTypes + )}${option.wrapped ? option.wrapped[1] : ''}` + )}`, + examples, + }; +} + +function printCommandArguments( + { multipleParams, params }: CommandDefinition['signature'], + withTypes: boolean +): string { + return `${params.map((arg) => printCommandArgument(arg, withTypes)).join(', `')}${ + multipleParams + ? ` ,[...${params.map((arg) => printCommandArgument(arg, withTypes)).join(', `')}]` + : '' + }`; +} + +function printCommandArgument( + param: CommandDefinition['signature']['params'][number], + withTypes: boolean +): string { + if (!withTypes) { + return param.name || ''; + } + return `${param.name}${param.optional ? ':?' : ':'} ${param.type}${ + param.innerType ? `{${param.innerType}}` : '' + }`; +} + +export function printArguments( + { + name, + type, + optional, + reference, + }: { + name: string; + type: string | string[]; + optional?: boolean; + reference?: string; + }, + withTypes: boolean +): string { + if (!withTypes) { + return name; + } + return `${name}${optional ? ':?' : ':'} ${Array.isArray(type) ? type.join(' | ') : type}`; +} diff --git a/packages/kbn-monaco/src/esql/lib/ast/definitions/literals.ts b/packages/kbn-monaco/src/esql/lib/ast/definitions/literals.ts new file mode 100644 index 0000000000000..3cc61683372a7 --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/ast/definitions/literals.ts @@ -0,0 +1,142 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; +import type { Literals } from './types'; + +export const timeLiterals: Literals[] = [ + { + name: 'year', + description: i18n.translate('monaco.esql.definitions.dateDurationDefinition.year', { + defaultMessage: 'Year', + }), + }, + { + name: 'years', + description: i18n.translate('monaco.esql.definitions.dateDurationDefinition.years', { + defaultMessage: 'Years (Plural)', + }), + }, + { + name: 'month', + description: i18n.translate('monaco.esql.definitions.dateDurationDefinition.month', { + defaultMessage: 'Month', + }), + }, + { + name: 'months', + description: i18n.translate('monaco.esql.definitions.dateDurationDefinition.months', { + defaultMessage: 'Months (Plural)', + }), + }, + { + name: 'week', + description: i18n.translate('monaco.esql.definitions.dateDurationDefinition.week', { + defaultMessage: 'Week', + }), + }, + { + name: 'weeks', + description: i18n.translate('monaco.esql.definitions.dateDurationDefinition.weeks', { + defaultMessage: 'Weeks (Plural)', + }), + }, + { + name: 'day', + description: i18n.translate('monaco.esql.definitions.dateDurationDefinition.day', { + defaultMessage: 'Day', + }), + }, + { + name: 'days', + description: i18n.translate('monaco.esql.definitions.dateDurationDefinition.days', { + defaultMessage: 'Days (Plural)', + }), + }, + { + name: 'hour', + description: i18n.translate('monaco.esql.definitions.dateDurationDefinition.hour', { + defaultMessage: 'Hour', + }), + }, + { + name: 'hours', + description: i18n.translate('monaco.esql.definitions.dateDurationDefinition.hours', { + defaultMessage: 'Hours (Plural)', + }), + }, + { + name: 'minute', + description: i18n.translate('monaco.esql.definitions.dateDurationDefinition.minute', { + defaultMessage: 'Minute', + }), + }, + { + name: 'minutes', + description: i18n.translate('monaco.esql.definitions.dateDurationDefinition.minutes', { + defaultMessage: 'Minutes (Plural)', + }), + }, + { + name: 'second', + description: i18n.translate('monaco.esql.definitions.dateDurationDefinition.second', { + defaultMessage: 'Second', + }), + }, + { + name: 'seconds', + description: i18n.translate('monaco.esql.definitions.dateDurationDefinition.seconds', { + defaultMessage: 'Seconds (Plural)', + }), + }, + { + name: 'millisecond', + description: i18n.translate('monaco.esql.definitions.dateDurationDefinition.millisecond', { + defaultMessage: 'Millisecond', + }), + }, + { + name: 'milliseconds', + description: i18n.translate('monaco.esql.definitions.dateDurationDefinition.milliseconds', { + defaultMessage: 'Milliseconds (Plural)', + }), + }, +]; + +export const chronoLiterals: Literals[] = [ + 'ALIGNED_DAY_OF_WEEK_IN_MONTH', + 'ALIGNED_DAY_OF_WEEK_IN_YEAR', + 'ALIGNED_WEEK_OF_MONTH', + 'ALIGNED_WEEK_OF_YEAR', + 'AMPM_OF_DAY', + 'CLOCK_HOUR_OF_AMPM', + 'CLOCK_HOUR_OF_DAY', + 'DAY_OF_MONTH', + 'DAY_OF_WEEK', + 'DAY_OF_YEAR', + 'EPOCH_DAY', + 'ERA', + 'HOUR_OF_AMPM', + 'HOUR_OF_DAY', + 'INSTANT_SECONDS', + 'MICRO_OF_DAY', + 'MICRO_OF_SECOND', + 'MILLI_OF_DAY', + 'MILLI_OF_SECOND', + 'MINUTE_OF_DAY', + 'MINUTE_OF_HOUR', + 'MONTH_OF_YEAR', + 'NANO_OF_DAY', + 'NANO_OF_SECOND', + 'OFFSET_SECONDS', + 'PROLEPTIC_MONTH', + 'SECOND_OF_DAY', + 'SECOND_OF_MINUTE', + 'YEAR', + 'YEAR_OF_ERA', +].map((name) => ({ name: `"${name}"`, description: '' })); diff --git a/packages/kbn-monaco/src/esql/lib/ast/definitions/options.ts b/packages/kbn-monaco/src/esql/lib/ast/definitions/options.ts new file mode 100644 index 0000000000000..465d6d25dc32e --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/ast/definitions/options.ts @@ -0,0 +1,106 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; +import { isLiteralItem } from '../shared/helpers'; +import { ESQLCommandOption, ESQLMessage } from '../types'; +import { CommandOptionsDefinition } from './types'; + +export const byOption: CommandOptionsDefinition = { + name: 'by', + description: i18n.translate('monaco.esql.definitions.byDoc', { + defaultMessage: 'By', + }), + signature: { + multipleParams: true, + params: [{ name: 'column', type: 'column' }], + }, + optional: true, +}; + +export const metadataOption: CommandOptionsDefinition = { + name: 'metadata', + description: i18n.translate('monaco.esql.definitions.metadataDoc', { + defaultMessage: 'Metadata', + }), + signature: { + multipleParams: true, + params: [{ name: 'column', type: 'column' }], + }, + optional: true, + wrapped: ['[', ']'], +}; + +export const asOption: CommandOptionsDefinition = { + name: 'as', + description: i18n.translate('monaco.esql.definitions.asDoc', { defaultMessage: 'As' }), + signature: { + multipleParams: false, + params: [ + { name: 'oldName', type: 'column' }, + { name: 'newName', type: 'column' }, + ], + }, + optional: false, +}; + +export const onOption: CommandOptionsDefinition = { + name: 'on', + description: i18n.translate('monaco.esql.definitions.onDoc', { defaultMessage: 'On' }), + signature: { + multipleParams: false, + params: [{ name: 'matchingColumn', type: 'column' }], + }, + optional: true, +}; + +export const withOption: CommandOptionsDefinition = { + name: 'with', + description: i18n.translate('monaco.esql.definitions.withDoc', { defaultMessage: 'With' }), + signature: { + multipleParams: true, + params: [{ name: 'assignment', type: 'any' }], + }, + optional: true, +}; + +export const appendSeparatorOption: CommandOptionsDefinition = { + name: 'append_separator', + description: i18n.translate('monaco.esql.definitions.appendSeparatorDoc', { + defaultMessage: + 'The character(s) that separate the appended fields. Default to empty string ("").', + }), + signature: { + multipleParams: false, + params: [{ name: 'separator', type: 'string' }], + }, + optional: true, + skipCommonValidation: true, // tell the validation engine to use only the validate function here + validate: (option: ESQLCommandOption) => { + const messages: ESQLMessage[] = []; + const [firstArg] = option.args; + if ( + !Array.isArray(firstArg) && + (!isLiteralItem(firstArg) || firstArg.literalType !== 'string') + ) { + const value = 'value' in firstArg ? firstArg.value : firstArg.name; + messages.push({ + location: firstArg.location, + text: i18n.translate('monaco.esql.validation.wrongDissectOptionArgumentType', { + defaultMessage: + 'Invalid value for dissect append_separator: expected a string, but was [{value}]', + values: { + value, + }, + }), + type: 'error', + }); + } + return messages; + }, +}; diff --git a/packages/kbn-monaco/src/esql/lib/ast/definitions/types.ts b/packages/kbn-monaco/src/esql/lib/ast/definitions/types.ts new file mode 100644 index 0000000000000..c52f332f3f2eb --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/ast/definitions/types.ts @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { ESQLCommand, ESQLCommandOption, ESQLMessage, ESQLSingleAstItem } from '../types'; + +export interface FunctionDefinition { + builtin?: boolean; + ignoreAsSuggestion?: boolean; + name: string; + alias?: string[]; + description: string; + supportedCommands: string[]; + signatures: Array<{ + params: Array<{ + name: string; + type: string; + optional?: boolean; + noNestingFunctions?: boolean; + supportsWildcard?: boolean; + }>; + infiniteParams?: boolean; + minParams?: number; + returnType: string; + examples?: string[]; + }>; + warning?: (...args: ESQLSingleAstItem[]) => string | undefined; +} + +export interface CommandBaseDefinition { + name: string; + alias?: string; + description: string; + signature: { + multipleParams: boolean; + // innerType here is useful to drill down the type in case of "column" + // i.e. column of type string + params: Array<{ + name: string; + type: string; + optional?: boolean; + innerType?: string; + values?: string[]; + literalOnly?: boolean; + wildcards?: boolean; + }>; + }; +} + +export interface CommandOptionsDefinition extends CommandBaseDefinition { + wrapped?: string[]; + optional: boolean; + skipCommonValidation?: boolean; + validate?: (option: ESQLCommandOption) => ESQLMessage[]; +} + +export interface CommandDefinition extends CommandBaseDefinition { + options: CommandOptionsDefinition[]; + examples: string[]; + validate?: (option: ESQLCommand) => ESQLMessage[]; +} + +export interface Literals { + name: string; + description: string; +} + +export type SignatureType = + | FunctionDefinition['signatures'][number] + | CommandOptionsDefinition['signature']; +export type SignatureArgType = SignatureType['params'][number]; diff --git a/packages/kbn-monaco/src/esql/lib/ast/hover/index.ts b/packages/kbn-monaco/src/esql/lib/ast/hover/index.ts new file mode 100644 index 0000000000000..b118a7dec5489 --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/ast/hover/index.ts @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 type { monaco } from '../../../../monaco_imports'; +import { getFunctionSignatures } from '../definitions/helpers'; +import { getAstContext } from '../shared/context'; +import { monacoPositionToOffset, getFunctionDefinition, isSourceItem } from '../shared/helpers'; +import { getPolicyHelper } from '../shared/resources_helpers'; +import { ESQLCallbacks } from '../shared/types'; +import type { AstProviderFn } from '../types'; + +export async function getHoverItem( + model: monaco.editor.ITextModel, + position: monaco.Position, + token: monaco.CancellationToken, + astProvider: AstProviderFn, + resourceRetriever?: ESQLCallbacks +) { + const innerText = model.getValue(); + const offset = monacoPositionToOffset(innerText, position); + + const { ast } = await astProvider(innerText); + const astContext = getAstContext(innerText, ast, offset); + const { getPolicyMetadata } = getPolicyHelper(resourceRetriever); + + if (['newCommand', 'list'].includes(astContext.type)) { + return { contents: [] }; + } + + if (astContext.type === 'function') { + const fnDefinition = getFunctionDefinition(astContext.node.name); + + if (fnDefinition) { + return { + contents: [ + { value: getFunctionSignatures(fnDefinition)[0].declaration }, + { value: fnDefinition.description }, + ], + }; + } + } + + if (astContext.type === 'expression') { + if ( + astContext.node && + isSourceItem(astContext.node) && + astContext.node.sourceType === 'policy' + ) { + const policyMetadata = await getPolicyMetadata(astContext.node.name); + if (policyMetadata) { + return { + contents: [ + { + value: `${i18n.translate('monaco.esql.hover.policyIndexes', { + defaultMessage: '**Indexes**', + })}: ${policyMetadata.sourceIndices}`, + }, + { + value: `${i18n.translate('monaco.esql.hover.policyMatchingField', { + defaultMessage: '**Matching field**', + })}: ${policyMetadata.matchField}`, + }, + { + value: `${i18n.translate('monaco.esql.hover.policyEnrichedFields', { + defaultMessage: '**Fields**', + })}: ${policyMetadata.enrichFields}`, + }, + ], + }; + } + } + } + + return { contents: [] }; +} diff --git a/packages/kbn-monaco/src/esql/lib/ast/shared/constants.ts b/packages/kbn-monaco/src/esql/lib/ast/shared/constants.ts new file mode 100644 index 0000000000000..618928f36bcfb --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/ast/shared/constants.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const EDITOR_MARKER = 'marker_esql_editor'; diff --git a/packages/kbn-monaco/src/esql/lib/ast/shared/context.ts b/packages/kbn-monaco/src/esql/lib/ast/shared/context.ts new file mode 100644 index 0000000000000..3a51038ec4e92 --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/ast/shared/context.ts @@ -0,0 +1,158 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { + ESQLAstItem, + ESQLSingleAstItem, + ESQLAst, + ESQLFunction, + ESQLCommand, + ESQLCommandOption, +} from '../types'; +import { EDITOR_MARKER } from './constants'; +import { + isOptionItem, + isColumnItem, + getLastCharFromTrimmed, + getFunctionDefinition, +} from './helpers'; + +function findNode(nodes: ESQLAstItem[], offset: number): ESQLSingleAstItem | undefined { + for (const node of nodes) { + if (Array.isArray(node)) { + const ret = findNode(node, offset); + if (ret) { + return ret; + } + } else { + if (node.location.min <= offset && node.location.max >= offset) { + if ('args' in node) { + const ret = findNode(node.args, offset); + // if the found node is the marker, then return its parent + if (ret?.text === EDITOR_MARKER) { + return node; + } + if (ret) { + return ret; + } + } + return node; + } + } + } +} + +function findCommand(ast: ESQLAst, offset: number) { + const commandIndex = ast.findIndex( + ({ location }) => location.min <= offset && location.max >= offset + ); + return ast[commandIndex] || ast[ast.length - 1]; +} + +function findOption(nodes: ESQLAstItem[], offset: number): ESQLCommandOption | undefined { + // this is a similar logic to the findNode, but it check if the command is in root or option scope + for (const node of nodes) { + if (!Array.isArray(node) && isOptionItem(node)) { + if ( + (node.location.min <= offset && node.location.max >= offset) || + (nodes[nodes.length - 1] === node && node.location.max < offset) + ) { + return node; + } + } + } +} + +function isMarkerNode(node: ESQLSingleAstItem | undefined): boolean { + return Boolean(node && isColumnItem(node) && node.name === EDITOR_MARKER); +} + +function cleanMarkerNode(node: ESQLSingleAstItem | undefined): ESQLSingleAstItem | undefined { + return isMarkerNode(node) ? undefined : node; +} + +function isNotMarkerNodeOrArray(arg: ESQLAstItem) { + return Array.isArray(arg) || !isMarkerNode(arg); +} + +function mapToNonMarkerNode(arg: ESQLAstItem): ESQLAstItem { + return Array.isArray(arg) ? arg.filter(isNotMarkerNodeOrArray).map(mapToNonMarkerNode) : arg; +} + +export function removeMarkerArgFromArgsList( + node: T | undefined +) { + if (!node) { + return; + } + if (node.type === 'command' || node.type === 'option' || node.type === 'function') { + return { + ...node, + args: node.args.filter(isNotMarkerNodeOrArray).map(mapToNonMarkerNode), + }; + } + return node; +} + +function findAstPosition(ast: ESQLAst, offset: number) { + const command = findCommand(ast, offset); + if (!command) { + return { command: undefined, node: undefined, option: undefined }; + } + return { + command: removeMarkerArgFromArgsList(command)!, + option: removeMarkerArgFromArgsList(findOption(command.args, offset)), + node: removeMarkerArgFromArgsList(cleanMarkerNode(findNode(command.args, offset))), + }; +} + +function isNotEnrichClauseAssigment(node: ESQLFunction, command: ESQLCommand) { + return node.name !== '=' && command.name !== 'enrich'; +} +function isBuiltinFunction(node: ESQLFunction) { + return Boolean(getFunctionDefinition(node.name)?.builtin); +} + +export function getAstContext(innerText: string, ast: ESQLAst, offset: number) { + const { command, option, node } = findAstPosition(ast, offset); + if (node) { + if (node.type === 'function') { + if (['in', 'not_in'].includes(node.name)) { + // command ... a in ( ) + return { type: 'list' as const, command, node, option }; + } + if (isNotEnrichClauseAssigment(node, command) && !isBuiltinFunction(node)) { + // command ... fn( ) + return { type: 'function' as const, command, node, option }; + } + } + if (node.type === 'option' || option) { + // command ... by + return { type: 'option' as const, command, node, option }; + } + } + + if (!command || (innerText.length <= offset && getLastCharFromTrimmed(innerText) === '|')) { + // // ... | + return { type: 'newCommand' as const, command: undefined, node, option }; + } + + if (command && command.args.length) { + if (option) { + return { type: 'option' as const, command, node, option }; + } + } + + // command a ... OR command a = ... + return { + type: 'expression' as const, + command, + option, + node, + }; +} diff --git a/packages/kbn-monaco/src/esql/lib/ast/shared/helpers.ts b/packages/kbn-monaco/src/esql/lib/ast/shared/helpers.ts new file mode 100644 index 0000000000000..f54849b7e81cf --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/ast/shared/helpers.ts @@ -0,0 +1,451 @@ +/* + * Copyright 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 { monaco } from '../../../../monaco_imports'; +import { statsAggregationFunctionDefinitions } from '../definitions/aggs'; +import { builtinFunctions } from '../definitions/builtin'; +import { commandDefinitions } from '../definitions/commands'; +import { evalFunctionsDefinitions } from '../definitions/functions'; +import { getFunctionSignatures } from '../definitions/helpers'; +import { chronoLiterals, timeLiterals } from '../definitions/literals'; +import { byOption, metadataOption, asOption, onOption, withOption } from '../definitions/options'; +import { + CommandDefinition, + CommandOptionsDefinition, + FunctionDefinition, + SignatureArgType, +} from '../definitions/types'; +import { + ESQLAstItem, + ESQLColumn, + ESQLCommandOption, + ESQLFunction, + ESQLLiteral, + ESQLSingleAstItem, + ESQLSource, + ESQLTimeInterval, +} from '../types'; +import { ESQLRealField, ESQLVariable, ReferenceMaps } from '../validation/types'; +import { removeMarkerArgFromArgsList } from './context'; + +export function isFunctionItem(arg: ESQLAstItem): arg is ESQLFunction { + return arg && !Array.isArray(arg) && arg.type === 'function'; +} + +export function isOptionItem(arg: ESQLAstItem): arg is ESQLCommandOption { + return !Array.isArray(arg) && arg.type === 'option'; +} + +export function isSourceItem(arg: ESQLAstItem): arg is ESQLSource { + return arg && !Array.isArray(arg) && arg.type === 'source'; +} + +export function isColumnItem(arg: ESQLAstItem): arg is ESQLColumn { + return arg && !Array.isArray(arg) && arg.type === 'column'; +} + +export function isLiteralItem(arg: ESQLAstItem): arg is ESQLLiteral { + return arg && !Array.isArray(arg) && arg.type === 'literal'; +} + +export function isTimeIntervalItem(arg: ESQLAstItem): arg is ESQLTimeInterval { + return arg && !Array.isArray(arg) && arg.type === 'timeInterval'; +} + +export function isAssignment(arg: ESQLAstItem): arg is ESQLFunction { + return isFunctionItem(arg) && arg.name === '='; +} + +export function isAssignmentComplete(node: ESQLFunction | undefined) { + const assignExpression = removeMarkerArgFromArgsList(node)?.args?.[1]; + return Boolean(assignExpression && Array.isArray(assignExpression) && assignExpression.length); +} + +export function isExpression(arg: ESQLAstItem): arg is ESQLFunction { + return isFunctionItem(arg) && arg.name !== '='; +} + +export function isIncompleteItem(arg: ESQLAstItem): boolean { + return !arg || (!Array.isArray(arg) && arg.incomplete); +} + +// From Monaco position to linear offset +export function monacoPositionToOffset(expression: string, position: monaco.Position): number { + const lines = expression.split(/\n/); + return lines + .slice(0, position.lineNumber) + .reduce( + (prev, current, index) => + prev + (index === position.lineNumber - 1 ? position.column - 1 : current.length + 1), + 0 + ); +} + +let fnLookups: Map | undefined; +let commandLookups: Map | undefined; + +function buildFunctionLookup() { + if (!fnLookups) { + fnLookups = builtinFunctions + .concat(evalFunctionsDefinitions, statsAggregationFunctionDefinitions) + .reduce((memo, def) => { + memo.set(def.name, def); + if (def.alias) { + for (const alias of def.alias) { + memo.set(alias, def); + } + } + return memo; + }, new Map()); + } + return fnLookups; +} + +type ReasonTypes = 'missingCommand' | 'unsupportedFunction' | 'unknownFunction'; + +export function isSupportedFunction( + name: string, + parentCommand?: string +): { supported: boolean; reason: ReasonTypes | undefined } { + if (!parentCommand) { + return { + supported: false, + reason: 'missingCommand', + }; + } + const fn = buildFunctionLookup().get(name); + const isSupported = Boolean(fn?.supportedCommands.includes(parentCommand)); + return { + supported: isSupported, + reason: isSupported ? undefined : fn ? 'unsupportedFunction' : 'unknownFunction', + }; +} + +export function getFunctionDefinition(name: string) { + return buildFunctionLookup().get(name.toLowerCase()); +} + +function buildCommandLookup() { + if (!commandLookups) { + commandLookups = commandDefinitions.reduce((memo, def) => { + memo.set(def.name, def); + if (def.alias) { + memo.set(def.alias, def); + } + return memo; + }, new Map()); + } + return commandLookups; +} + +export function getCommandDefinition(name: string): CommandDefinition { + return buildCommandLookup().get(name.toLowerCase())!; +} + +export function getAllCommands() { + return Array.from(buildCommandLookup().values()); +} + +export function getCommandOption(name: CommandOptionsDefinition['name']) { + switch (name) { + case 'by': + return byOption; + case 'metadata': + return metadataOption; + case 'as': + return asOption; + case 'on': + return onOption; + case 'with': + return withOption; + default: + return; + } +} + +function compareLiteralType(argTypes: string, item: ESQLLiteral) { + if (item.literalType !== 'string') { + return argTypes === item.literalType; + } + if (argTypes === 'chrono_literal') { + return chronoLiterals.some(({ name }) => name === item.text); + } + return argTypes === item.literalType; +} + +export function getColumnHit( + columnName: string, + { fields, variables }: Pick, + position?: number +): ESQLRealField | ESQLVariable | undefined { + return fields.get(columnName) || variables.get(columnName)?.[0]; +} + +const ARRAY_REGEXP = /\[\]$/; + +export function isArrayType(type: string) { + return ARRAY_REGEXP.test(type); +} + +export function extractSingleType(type: string) { + return type.replace(ARRAY_REGEXP, ''); +} + +export function createMapFromList(arr: T[]): Map { + const arrMap = new Map(); + for (const item of arr) { + arrMap.set(item.name, item); + } + return arrMap; +} + +export function areFieldAndVariableTypesCompatible( + fieldType: string | string[] | undefined, + variableType: string | string[] +) { + if (fieldType == null) { + return false; + } + return fieldType === variableType; +} + +export function printFunctionSignature(arg: ESQLFunction): string { + const fnDef = getFunctionDefinition(arg.name); + if (fnDef) { + const signature = getFunctionSignatures( + { + ...fnDef, + signatures: [ + { + ...fnDef?.signatures[0], + params: arg.args.map((innerArg) => + Array.isArray(innerArg) + ? { name: `InnerArgument[]`, type: '' } + : { name: innerArg.text, type: innerArg.type } + ), + returnType: '', + }, + ], + }, + { withTypes: false } + ); + return signature[0].declaration; + } + return ''; +} + +export function getAllArrayValues(arg: ESQLAstItem) { + const values: string[] = []; + if (Array.isArray(arg)) { + for (const subArg of arg) { + if (Array.isArray(subArg)) { + break; + } + if (subArg.type === 'literal') { + values.push(String(subArg.value)); + } + if (subArg.type === 'column') { + values.push(subArg.name); + } + if (subArg.type === 'timeInterval') { + values.push(subArg.name); + } + if (subArg.type === 'function') { + const signature = printFunctionSignature(subArg); + if (signature) { + values.push(signature); + } + } + } + } + return values; +} + +export function getAllArrayTypes( + arg: ESQLAstItem, + parentCommand: string, + references: ReferenceMaps +) { + const types = []; + if (Array.isArray(arg)) { + for (const subArg of arg) { + if (Array.isArray(subArg)) { + break; + } + if (subArg.type === 'literal') { + types.push(subArg.literalType); + } + if (subArg.type === 'column') { + const hit = getColumnHit(subArg.name, references); + types.push(hit?.type || 'unsupported'); + } + if (subArg.type === 'timeInterval') { + types.push('time_literal'); + } + if (subArg.type === 'function') { + if (isSupportedFunction(subArg.name, parentCommand).supported) { + const fnDef = buildFunctionLookup().get(subArg.name)!; + types.push(fnDef.signatures[0].returnType); + } + } + } + } + return types; +} + +export function inKnownTimeInterval(item: ESQLTimeInterval): boolean { + return timeLiterals.some(({ name }) => name === item.unit.toLowerCase()); +} + +export function isEqualType( + item: ESQLSingleAstItem, + argDef: SignatureArgType, + references: ReferenceMaps, + parentCommand?: string +) { + const argType = 'innerType' in argDef && argDef.innerType ? argDef.innerType : argDef.type; + if (argType === 'any') { + return true; + } + if (item.type === 'literal') { + return compareLiteralType(argType, item); + } + if (item.type === 'list') { + const listType = `${item.values[0].literalType}[]`; + // argType = 'list' means any list value is ok + return argType === item.type || argType === listType; + } + if (item.type === 'function') { + if (isSupportedFunction(item.name, parentCommand).supported) { + const fnDef = buildFunctionLookup().get(item.name)!; + return fnDef.signatures.some((signature) => argType === signature.returnType); + } + } + if (item.type === 'timeInterval') { + return argType === 'time_literal' && inKnownTimeInterval(item); + } + if (item.type === 'column') { + if (argType === 'column') { + // anything goes, so avoid any effort here + return true; + } + const hit = getColumnHit(item.name, references); + if (!hit) { + return false; + } + const wrappedTypes = Array.isArray(hit.type) ? hit.type : [hit.type]; + return wrappedTypes.some((ct) => argType === ct); + } + if (item.type === 'source') { + return item.sourceType === argType; + } +} + +export function endsWithOpenBracket(text: string) { + return /\($/.test(text); +} + +export function isDateFunction(fnName: string) { + // TODO: improve this and rely in signature in the future + return ['to_datetime', 'date_trunc', 'date_parse'].includes(fnName.toLowerCase()); +} + +export function getDateMathOperation() { + return builtinFunctions.filter(({ name }) => ['+', '-'].includes(name)); +} + +export function getDurationItemsWithQuantifier(quantifier: number = 1) { + return timeLiterals + .filter(({ name }) => !/s$/.test(name)) + .map(({ name, ...rest }) => ({ + label: `${quantifier} ${name}`, + insertText: `${quantifier} ${name}`, + ...rest, + })); +} + +function fuzzySearch(fuzzyName: string, resources: IterableIterator) { + const wildCardPosition = getWildcardPosition(fuzzyName); + if (wildCardPosition !== 'none') { + const matcher = getMatcher(fuzzyName, wildCardPosition); + for (const resourceName of resources) { + if (matcher(resourceName)) { + return true; + } + } + } +} + +function getMatcher(name: string, position: 'start' | 'end' | 'middle') { + if (position === 'start') { + const prefix = name.substring(1); + return (resource: string) => resource.endsWith(prefix); + } + if (position === 'end') { + const prefix = name.substring(0, name.length - 1); + return (resource: string) => resource.startsWith(prefix); + } + const [prefix, postFix] = name.split('*'); + return (resource: string) => resource.startsWith(prefix) && resource.endsWith(postFix); +} + +function getWildcardPosition(name: string) { + if (!hasWildcard(name)) { + return 'none'; + } + if (name.startsWith('*')) { + return 'start'; + } + if (name.endsWith('*')) { + return 'end'; + } + return 'middle'; +} + +export function hasWildcard(name: string) { + return name.includes('*'); +} +export function hasCCSSource(name: string) { + return name.includes(':'); +} + +export function columnExists( + column: ESQLColumn, + { fields, variables }: Pick +) { + if (fields.has(column.name) || variables.has(column.name)) { + return { hit: true, nameHit: column.name }; + } + if (column.quoted) { + const trimmedName = column.name.replace(/\s/g, ''); + if (variables.has(trimmedName)) { + return { hit: true, nameHit: trimmedName }; + } + } + if ( + Boolean(fuzzySearch(column.name, fields.keys()) || fuzzySearch(column.name, variables.keys())) + ) { + return { hit: true, nameHit: column.name }; + } + return { hit: false }; +} + +export function sourceExists(index: string, sources: Set) { + if (sources.has(index)) { + return true; + } + return Boolean(fuzzySearch(index, sources.keys())); +} + +export function getLastCharFromTrimmed(text: string) { + return text[text.trimEnd().length - 1]; +} + +export function isRestartingExpression(text: string) { + return getLastCharFromTrimmed(text) === ','; +} diff --git a/packages/kbn-monaco/src/esql/lib/ast/shared/resources_helpers.ts b/packages/kbn-monaco/src/esql/lib/ast/shared/resources_helpers.ts new file mode 100644 index 0000000000000..fcd4cbb0737ff --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/ast/shared/resources_helpers.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 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 { ESQLCallbacks } from './types'; +import type { ESQLRealField } from '../validation/types'; + +export function getFieldsByTypeHelper(queryText: string, resourceRetriever?: ESQLCallbacks) { + const cacheFields = new Map(); + const getFields = async () => { + if (!cacheFields.size) { + const fieldsOfType = await resourceRetriever?.getFieldsFor?.({ query: queryText }); + for (const field of fieldsOfType || []) { + cacheFields.set(field.name, field); + } + } + }; + return { + getFieldsByType: async (expectedType: string | string[] = 'any', ignored: string[] = []) => { + const types = Array.isArray(expectedType) ? expectedType : [expectedType]; + await getFields(); + return ( + Array.from(cacheFields.values()) + ?.filter(({ name, type }) => { + const ts = Array.isArray(type) ? type : [type]; + return ( + !ignored.includes(name) && ts.some((t) => types[0] === 'any' || types.includes(t)) + ); + }) + .map(({ name }) => name) || [] + ); + }, + getFieldsMap: async () => { + await getFields(); + const cacheCopy = new Map(); + cacheFields.forEach((value, key) => cacheCopy.set(key, value)); + return cacheCopy; + }, + }; +} + +export function getPolicyHelper(resourceRetriever?: ESQLCallbacks) { + const getPolicies = async () => { + return (await resourceRetriever?.getPolicies?.()) || []; + }; + return { + getPolicies: async () => { + const policies = await getPolicies(); + return policies; + }, + getPolicyMetadata: async (policyName: string) => { + const policies = await getPolicies(); + return policies.find(({ name }) => name === policyName); + }, + }; +} + +export function getSourcesHelper(resourceRetriever?: ESQLCallbacks) { + return async () => { + return (await resourceRetriever?.getSources?.()) || []; + }; +} diff --git a/packages/kbn-monaco/src/esql/lib/ast/shared/types.ts b/packages/kbn-monaco/src/esql/lib/ast/shared/types.ts new file mode 100644 index 0000000000000..ceb507f59784a --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/ast/shared/types.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/** @internal **/ +type CallbackFn = (ctx?: Options) => Result[] | Promise; + +/** @public **/ +export interface ESQLCallbacks { + getSources?: CallbackFn<{}, { name: string; hidden: boolean }>; + getFieldsFor?: CallbackFn<{ query: string }, { name: string; type: string }>; + getPolicies?: CallbackFn< + {}, + { name: string; sourceIndices: string[]; matchField: string; enrichFields: string[] } + >; + getPolicyFields?: CallbackFn; + getPolicyMatchingField?: CallbackFn; +} diff --git a/packages/kbn-monaco/src/esql/lib/ast/shared/variables.ts b/packages/kbn-monaco/src/esql/lib/ast/shared/variables.ts new file mode 100644 index 0000000000000..3f93ce50b71c3 --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/ast/shared/variables.ts @@ -0,0 +1,172 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { ESQLColumn, ESQLAstItem, ESQLCommand, ESQLCommandOption } from '../types'; +import type { ESQLVariable, ESQLRealField } from '../validation/types'; +import { EDITOR_MARKER } from './constants'; +import { + isColumnItem, + isAssignment, + isExpression, + isOptionItem, + isFunctionItem, + getFunctionDefinition, +} from './helpers'; + +function addToVariableOccurrencies(variables: Map, instance: ESQLVariable) { + if (!variables.has(instance.name)) { + variables.set(instance.name, []); + } + const variablesOccurrencies = variables.get(instance.name)!; + variablesOccurrencies.push(instance); +} + +function replaceTrimmedVariable( + variables: Map, + newRef: ESQLColumn, + oldRef: ESQLVariable[] +) { + // now replace the existing trimmed version with this original one + addToVariableOccurrencies(variables, { + name: newRef.name, + type: oldRef[0].type, + location: newRef.location, + }); + // remove the trimmed one + variables.delete(oldRef[0].name); +} + +function addToVariables( + oldArg: ESQLAstItem, + newArg: ESQLAstItem, + fields: Map, + variables: Map +) { + if (isColumnItem(oldArg) && isColumnItem(newArg)) { + const newVariable: ESQLVariable = { + name: newArg.name, + type: 'number' /* fallback to number */, + location: newArg.location, + }; + // Now workout the exact type + // it can be a rename of another variable as well + let oldRef = fields.get(oldArg.name) || variables.get(oldArg.name); + if (oldRef) { + addToVariableOccurrencies(variables, newVariable); + newVariable.type = Array.isArray(oldRef) ? oldRef[0].type : oldRef.type; + } else if (oldArg.quoted) { + // a last attempt in case the user tried to rename an expression: + // trim every space and try a new hit + const expressionTrimmedRef = oldArg.name.replace(/\s/g, ''); + oldRef = variables.get(expressionTrimmedRef); + if (oldRef) { + addToVariableOccurrencies(variables, newVariable); + newVariable.type = oldRef[0].type; + replaceTrimmedVariable(variables, oldArg, oldRef); + } + } + } +} + +function getAssignRightHandSideType(item: ESQLAstItem, fields: Map) { + if (Array.isArray(item)) { + const firstArg = item[0]; + if (Array.isArray(firstArg) || !firstArg) { + return; + } + if (firstArg.type === 'literal') { + return firstArg.literalType; + } + if (isColumnItem(firstArg)) { + const field = fields.get(firstArg.name); + if (field) { + return field.type; + } + } + if (isFunctionItem(firstArg)) { + const fnDefinition = getFunctionDefinition(firstArg.name); + return fnDefinition?.signatures[0].returnType; + } + return firstArg.type; + } +} + +export function excludeVariablesFromCurrentCommand( + commands: ESQLCommand[], + currentCommand: ESQLCommand, + fieldsMap: Map +) { + const anyVariables = collectVariables(commands, fieldsMap); + const currentCommandVariables = collectVariables([currentCommand], fieldsMap); + const resultVariables = new Map(); + anyVariables.forEach((value, key) => { + if (!currentCommandVariables.has(key)) { + resultVariables.set(key, value); + } + }); + return resultVariables; +} + +export function collectVariables( + commands: ESQLCommand[], + fields: Map +): Map { + const variables = new Map(); + for (const command of commands) { + if (['row', 'eval', 'stats'].includes(command.name)) { + const assignOperations = command.args.filter(isAssignment); + for (const assignOperation of assignOperations) { + if (isColumnItem(assignOperation.args[0])) { + const rightHandSideArgType = getAssignRightHandSideType(assignOperation.args[1], fields); + addToVariableOccurrencies(variables, { + name: assignOperation.args[0].name, + type: rightHandSideArgType || 'number' /* fallback to number */, + location: assignOperation.args[0].location, + }); + } + } + const expressionOperations = command.args.filter(isExpression); + for (const expressionOperation of expressionOperations) { + if (!expressionOperation.text.includes(EDITOR_MARKER)) { + // just save the entire expression as variable string + const expressionType = 'number'; + addToVariableOccurrencies(variables, { + name: expressionOperation.text, + type: expressionType, + location: expressionOperation.location, + }); + } + } + } + if (command.name === 'enrich') { + const commandOptionsWithAssignment = command.args.filter( + (arg) => isOptionItem(arg) && arg.name === 'with' + ) as ESQLCommandOption[]; + for (const commandOption of commandOptionsWithAssignment) { + for (const assignFn of commandOption.args) { + if (isFunctionItem(assignFn)) { + const [newArg, oldArg] = assignFn?.args || []; + if (Array.isArray(oldArg)) { + addToVariables(oldArg[0], newArg, fields, variables); + } + } + } + } + } + if (command.name === 'rename') { + const commandOptionsWithAssignment = command.args.filter( + (arg) => isOptionItem(arg) && arg.name === 'as' + ) as ESQLCommandOption[]; + for (const commandOption of commandOptionsWithAssignment) { + const [oldArg, newArg] = commandOption.args; + addToVariables(oldArg, newArg, fields, variables); + } + } + } + return variables; +} diff --git a/packages/kbn-monaco/src/esql/lib/ast/signature/index.ts b/packages/kbn-monaco/src/esql/lib/ast/signature/index.ts new file mode 100644 index 0000000000000..a3202591a9c7a --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/ast/signature/index.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { monaco } from '../../../../monaco_imports'; +import type { AstProviderFn } from '../types'; + +export function getSignatureHelp( + model: monaco.editor.ITextModel, + position: monaco.Position, + context: monaco.languages.SignatureHelpContext, + astProvider: AstProviderFn +): monaco.languages.SignatureHelpResult { + return { + value: { signatures: [], activeParameter: 0, activeSignature: 0 }, + dispose: () => {}, + }; +} diff --git a/packages/kbn-monaco/src/esql/lib/ast/types.ts b/packages/kbn-monaco/src/esql/lib/ast/types.ts new file mode 100644 index 0000000000000..6c241270de457 --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/ast/types.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 { EditorError } from '../../../types'; + +export type ESQLAst = ESQLCommand[]; + +export type ESQLSingleAstItem = + | ESQLFunction + | ESQLCommandOption + | ESQLSource + | ESQLColumn + | ESQLTimeInterval + | ESQLList + | ESQLLiteral; + +export type ESQLAstItem = ESQLSingleAstItem | ESQLAstItem[]; + +export interface ESQLLocation { + min: number; + max: number; +} + +interface ESQLAstBaseItem { + name: string; + text: string; + location: ESQLLocation; + incomplete: boolean; +} + +export interface ESQLCommand extends ESQLAstBaseItem { + type: 'command'; + args: ESQLAstItem[]; +} + +export interface ESQLCommandOption extends ESQLAstBaseItem { + type: 'option'; + args: ESQLAstItem[]; +} + +export interface ESQLFunction extends ESQLAstBaseItem { + type: 'function'; + args: ESQLAstItem[]; +} + +export interface ESQLTimeInterval extends ESQLAstBaseItem { + type: 'timeInterval'; + unit: string; + quantity: number; +} + +export interface ESQLSource extends ESQLAstBaseItem { + type: 'source'; + sourceType: 'index' | 'policy'; +} + +export interface ESQLColumn extends ESQLAstBaseItem { + type: 'column'; + quoted: boolean; +} + +export interface ESQLList extends ESQLAstBaseItem { + type: 'list'; + values: ESQLLiteral[]; +} + +export interface ESQLLiteral extends ESQLAstBaseItem { + type: 'literal'; + literalType: 'string' | 'number' | 'boolean' | 'null'; + value: string | number; +} + +export interface ESQLMessage { + type: 'error' | 'warning'; + text: string; + location: ESQLLocation; +} + +export type AstProviderFn = ( + text: string | undefined +) => Promise<{ ast: ESQLAst; errors: EditorError[] }>; diff --git a/packages/kbn-monaco/src/esql/lib/ast/validation/errors.ts b/packages/kbn-monaco/src/esql/lib/ast/validation/errors.ts new file mode 100644 index 0000000000000..16913677c4890 --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/ast/validation/errors.ts @@ -0,0 +1,217 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 type { ESQLLocation, ESQLMessage } from '../types'; +import type { ErrorTypes, ErrorValues } from './types'; + +function getMessageAndTypeFromId({ + messageId, + values, +}: { + messageId: K; + values: ErrorValues; +}): { message: string; type?: 'error' | 'warning' } { + // Use a less strict type instead of doing a typecast on each message type + const out = values as unknown as Record; + // i18n validation wants to the values prop to be declared inline, so need to unpack and redeclare again all props + switch (messageId) { + case 'wrongArgumentType': + return { + message: i18n.translate('monaco.esql.validation.wrongArgumentType', { + defaultMessage: + 'Argument of [{name}] must be [{argType}], found value [{value}] type [{givenType}]', + values: { + name: out.name, + argType: out.argType, + value: out.value, + givenType: out.givenType, + }, + }), + }; + case 'unknownColumn': + return { + message: i18n.translate('monaco.esql.validation.unknownColumn', { + defaultMessage: 'Unknown column [{name}]', + values: { name: out.name }, + }), + }; + case 'unknownIndex': + return { + message: i18n.translate('monaco.esql.validation.unknownIndex', { + defaultMessage: 'Unknown index [{name}]', + values: { name: out.name }, + }), + }; + case 'unknownFunction': + return { + message: i18n.translate('monaco.esql.validation.missingFunction', { + defaultMessage: 'Unknown function [{name}]', + values: { name: out.name }, + }), + }; + case 'wrongArgumentNumber': + return { + message: i18n.translate('monaco.esql.validation.wrongArgumentNumber', { + defaultMessage: + 'Error building [{fn}]: expects exactly {numArgs, plural, one {one argument} other {{numArgs} arguments}}, passed {passedArgs} instead.', + values: { fn: out.fn, numArgs: out.numArgs, passedArgs: out.passedArgs }, + }), + }; + case 'noNestedArgumentSupport': + return { + message: i18n.translate('monaco.esql.validation.noNestedArgumentSupport', { + defaultMessage: + "Aggregate function's parameters must be an attribute or literal; found [{name}] of type [{argType}]", + values: { name: out.name, argType: out.argType }, + }), + }; + case 'shadowFieldType': + return { + message: i18n.translate('monaco.esql.validation.typeOverwrite', { + defaultMessage: + 'Column [{field}] of type {fieldType} has been overwritten as new type: {newType}', + values: { field: out.field, fieldType: out.fieldType, newType: out.newType }, + }), + type: 'warning', + }; + case 'unsupportedColumnTypeForCommand': + return { + message: i18n.translate('monaco.esql.validation.unsupportedColumnTypeForCommand', { + defaultMessage: + '{command} only supports {type} {typeCount, plural, one {type} other {types}} values, found [{column}] of type {givenType}', + values: { + command: out.command, + type: out.type, + typeCount: out.typeCount, + column: out.column, + givenType: out.givenType, + }, + }), + }; + case 'unknownOption': + return { + message: i18n.translate('monaco.esql.validation.unknownOption', { + defaultMessage: 'Invalid option for {command}: [{option}]', + values: { + command: out.command, + option: out.option, + }, + }), + }; + case 'unsupportedFunction': + return { + message: i18n.translate('monaco.esql.validation.unsupportedFunction', { + defaultMessage: '{command} does not support function {name}', + values: { + command: out.command, + name: out.name, + }, + }), + }; + case 'unknownInterval': + return { + message: i18n.translate('monaco.esql.validation.unknownInterval', { + defaultMessage: `Unexpected time interval qualifier: '{value}'`, + values: { + value: out.value, + }, + }), + }; + case 'unsupportedTypeForCommand': + return { + message: i18n.translate('monaco.esql.validation.unsupportedTypeForCommand', { + defaultMessage: '{command} does not support [{type}] in expression [{value}]', + values: { + command: out.command, + type: out.type, + value: out.value, + }, + }), + }; + case 'unknownPolicy': + return { + message: i18n.translate('monaco.esql.validation.unknownPolicy', { + defaultMessage: 'Unknown policy [{name}]', + values: { + name: out.name, + }, + }), + }; + case 'unknownAggregateFunction': + return { + message: i18n.translate('monaco.esql.validation.unknowAggregateFunction', { + defaultMessage: '{command} expects an aggregate function, found [{value}]', + values: { + command: out.command, + value: out.value, + }, + }), + }; + case 'wildcardNotSupportedForCommand': + return { + message: i18n.translate('monaco.esql.validation.wildcardNotSupportedForCommand', { + defaultMessage: 'Using wildcards (*) in {command} is not allowed [{value}]', + values: { + command: out.command, + value: out.value, + }, + }), + }; + case 'noWildcardSupportAsArg': + return { + message: i18n.translate('monaco.esql.validation.wildcardNotSupportedForFunction', { + defaultMessage: 'Using wildcards (*) in {name} is not allowed', + values: { + name: out.name, + }, + }), + }; + case 'ccsNotSupportedForCommand': + return { + message: i18n.translate('monaco.esql.validation.ccsNotSupportedForCommand', { + defaultMessage: 'ES|QL does not yet support querying remote indices [{value}]', + values: { + value: out.value, + }, + }), + }; + case 'unsupportedFieldType': + return { + message: i18n.translate('monaco.esql.validation.unsupportedFieldType', { + defaultMessage: + 'Field [{field}] cannot be retrieved, it is unsupported or not indexed; returning null', + values: { + field: out.field, + }, + }), + type: 'warning', + }; + } + return { message: '' }; +} + +export function getMessageFromId({ + locations, + ...payload +}: { + messageId: K; + values: ErrorValues; + locations: ESQLLocation; +}): ESQLMessage { + const { message, type = 'error' } = getMessageAndTypeFromId(payload); + return createMessage(type, message, locations); +} + +export function createMessage(type: 'error' | 'warning', message: string, location: ESQLLocation) { + return { + type, + text: message, + location, + }; +} diff --git a/packages/kbn-monaco/src/esql/lib/ast/validation/helpers.ts b/packages/kbn-monaco/src/esql/lib/ast/validation/helpers.ts new file mode 100644 index 0000000000000..51015e0d8d09d --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/ast/validation/helpers.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { ESQLAst } from '../types'; +import type { ESQLPolicy } from './types'; + +export function buildQueryForFieldsFromSource(queryString: string, ast: ESQLAst) { + const firstCommand = ast[0]; + return queryString.substring(0, firstCommand.location.max + 1); +} + +export function buildQueryForFieldsInPolicies(policies: ESQLPolicy[]) { + return `from ${policies + .flatMap(({ sourceIndices }) => sourceIndices) + .join(', ')} | keep ${policies.flatMap(({ enrichFields }) => enrichFields).join(', ')}`; +} diff --git a/packages/kbn-monaco/src/esql/lib/ast/validation/resources.ts b/packages/kbn-monaco/src/esql/lib/ast/validation/resources.ts new file mode 100644 index 0000000000000..24c6de0f7db6f --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/ast/validation/resources.ts @@ -0,0 +1,85 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { nonNullable } from '../ast_helpers'; +import { createMapFromList, isSourceItem } from '../shared/helpers'; +import { + getFieldsByTypeHelper, + getPolicyHelper, + getSourcesHelper, +} from '../shared/resources_helpers'; +import type { ESQLCallbacks } from '../shared/types'; +import type { ESQLCommand } from '../types'; +import { buildQueryForFieldsFromSource, buildQueryForFieldsInPolicies } from './helpers'; +import type { ESQLRealField, ESQLPolicy } from './types'; + +export async function retrieveFields( + queryString: string, + commands: ESQLCommand[], + callbacks?: ESQLCallbacks +): Promise> { + if (!callbacks || commands.length < 1) { + return new Map(); + } + if (commands[0].name === 'row') { + return new Map(); + } + const customQuery = buildQueryForFieldsFromSource(queryString, commands); + return await getFieldsByTypeHelper(customQuery, callbacks).getFieldsMap(); +} + +export async function retrievePolicies( + commands: ESQLCommand[], + callbacks?: ESQLCallbacks +): Promise> { + if (!callbacks || commands.every(({ name }) => name !== 'enrich')) { + return new Map(); + } + + const policies = await getPolicyHelper(callbacks).getPolicies(); + return createMapFromList(policies); +} + +export async function retrieveSources( + commands: ESQLCommand[], + callbacks?: ESQLCallbacks +): Promise> { + if (!callbacks || commands.length < 1) { + return new Set(); + } + if (['row', 'show'].includes(commands[0].name)) { + return new Set(); + } + const sources = await getSourcesHelper(callbacks)(); + return new Set(sources.map(({ name }) => name)); +} + +export async function retrievePoliciesFields( + commands: ESQLCommand[], + policies: Map, + callbacks?: ESQLCallbacks +): Promise> { + if (!callbacks) { + return new Map(); + } + const enrichCommands = commands.filter(({ name }) => name === 'enrich'); + if (!enrichCommands.length) { + return new Map(); + } + const policyNames = enrichCommands + .map(({ args }) => (isSourceItem(args[0]) ? args[0].name : undefined)) + .filter(nonNullable); + if (!policyNames.every((name) => policies.has(name))) { + return new Map(); + } + + const customQuery = buildQueryForFieldsInPolicies( + policyNames.map((name) => policies.get(name)) as ESQLPolicy[] + ); + return await getFieldsByTypeHelper(customQuery, callbacks).getFieldsMap(); +} diff --git a/packages/kbn-monaco/src/esql/lib/ast/validation/types.ts b/packages/kbn-monaco/src/esql/lib/ast/validation/types.ts new file mode 100644 index 0000000000000..07b7e504ab1be --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/ast/validation/types.ts @@ -0,0 +1,127 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { EditorError } from '../../../../types'; +import { ESQLMessage, ESQLLocation } from '../types'; + +export interface ESQLVariable { + name: string; + type: string; + location: ESQLLocation; +} + +export interface ESQLRealField { + name: string; + type: string; +} + +export interface ESQLPolicy { + name: string; + sourceIndices: string[]; + matchField: string; + enrichFields: string[]; +} + +export interface ReferenceMaps { + sources: Set; + variables: Map; + fields: Map; + policies: Map; +} + +export interface ValidationErrors { + wrongArgumentType: { + message: string; + type: { + name: string; + argType: string; + value: string | number | Date; + givenType: string; + }; + }; + wrongArgumentNumber: { + message: string; + type: { fn: string; numArgs: number; passedArgs: number }; + }; + unknownColumn: { + message: string; + type: { name: string | number }; + }; + unknownFunction: { + message: string; + type: { name: string }; + }; + unknownIndex: { + message: string; + type: { name: string }; + }; + noNestedArgumentSupport: { + message: string; + type: { name: string; argType: string }; + }; + unsupportedFunction: { + message: string; + type: { name: string; command: string }; + }; + shadowFieldType: { + message: string; + type: { field: string; fieldType: string; newType: string }; + }; + unsupportedColumnTypeForCommand: { + message: string; + type: { command: string; type: string; typeCount: number; givenType: string; column: string }; + }; + unknownOption: { + message: string; + type: { command: string; option: string }; + }; + wrongOptionArgumentType: { + message: string; + type: { command: string; option: string; type: string; givenValue: string }; + }; + unknownInterval: { + message: string; + type: { value: string }; + }; + unsupportedTypeForCommand: { + message: string; + type: { command: string; value: string; type: string }; + }; + unknownPolicy: { + message: string; + type: { name: string }; + }; + unknownAggregateFunction: { + message: string; + type: { command: string; value: string }; + }; + wildcardNotSupportedForCommand: { + message: string; + type: { command: string; value: string }; + }; + noWildcardSupportAsArg: { + message: string; + type: { name: string }; + }; + ccsNotSupportedForCommand: { + message: string; + type: { value: string }; + }; + unsupportedFieldType: { + message: string; + type: { field: string }; + }; +} + +export type ErrorTypes = keyof ValidationErrors; +export type ErrorValues = ValidationErrors[K]['type']; + +export interface ValidationResult { + errors: Array; + warnings: ESQLMessage[]; +} diff --git a/packages/kbn-monaco/src/esql/lib/ast/validation/validation.test.ts b/packages/kbn-monaco/src/esql/lib/ast/validation/validation.test.ts new file mode 100644 index 0000000000000..0df20ac7d88f4 --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/ast/validation/validation.test.ts @@ -0,0 +1,1441 @@ +/* + * Copyright 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 { CharStreams } from 'antlr4ts'; +import { getParser, ROOT_STATEMENT } from '../../antlr_facade'; +// import { mathCommandDefinition } from '../../autocomplete/autocomplete_definitions'; +// import { getDurationItemsWithQuantifier } from '../../autocomplete/helpers'; +import { AstListener } from '../ast_factory'; +import { validateAst } from './validation'; +import { ESQLAst } from '../types'; +import { ESQLErrorListener } from '../../monaco/esql_error_listener'; +import { evalFunctionsDefinitions } from '../definitions/functions'; +import { getFunctionSignatures } from '../definitions/helpers'; +import { FunctionDefinition } from '../definitions/types'; +import { chronoLiterals, timeLiterals } from '../definitions/literals'; +import { statsAggregationFunctionDefinitions } from '../definitions/aggs'; +import capitalize from 'lodash/capitalize'; +import { EditorError } from '../../../../types'; + +function getCallbackMocks() { + return { + getFieldsFor: jest.fn(async ({ query }) => + /enrich/.test(query) + ? [ + { name: 'otherField', type: 'string' }, + { name: 'yetAnotherField', type: 'number' }, + ] + : /unsupported_index/.test(query) + ? [{ name: 'unsupported_field', type: 'unsupported' }] + : [ + ...['string', 'number', 'date', 'boolean', 'ip'].map((type) => ({ + name: `${type}Field`, + type, + })), + { name: 'geoPointField', type: 'geo_point' }, + { name: 'any#Char$ field', type: 'number' }, + { name: 'kubernetes.something.something', type: 'number' }, + { + name: `listField`, + type: `list`, + }, + { name: '@timestamp', type: 'date' }, + ] + ), + getSources: jest.fn(async () => + ['a', 'index', 'otherIndex', '.secretIndex', 'my-index', 'unsupported_index'].map((name) => ({ + name, + hidden: name.startsWith('.'), + })) + ), + getPolicies: jest.fn(async () => [ + { + name: 'policy', + sourceIndices: ['enrichIndex1'], + matchField: 'otherStringField', + enrichFields: ['otherField', 'yetAnotherField'], + }, + ]), + }; +} + +const toDoubleSignature = evalFunctionsDefinitions.find(({ name }) => name === 'to_double')!; +const toStringSignature = evalFunctionsDefinitions.find(({ name }) => name === 'to_string')!; +const toDateSignature = evalFunctionsDefinitions.find(({ name }) => name === 'to_datetime')!; +const toBooleanSignature = evalFunctionsDefinitions.find(({ name }) => name === 'to_boolean')!; +const toIpSignature = evalFunctionsDefinitions.find(({ name }) => name === 'to_ip')!; + +const toAvgSignature = statsAggregationFunctionDefinitions.find(({ name }) => name === 'avg')!; + +const nestedFunctions = { + number: prepareNestedFunction(toDoubleSignature), + string: prepareNestedFunction(toStringSignature), + date: prepareNestedFunction(toDateSignature), + boolean: prepareNestedFunction(toBooleanSignature), + ip: prepareNestedFunction(toIpSignature), +}; + +const literals = { + chrono_literal: chronoLiterals[0].name, + time_literal: timeLiterals[0].name, +}; +function getLiteralType(typeString: 'chrono_literal' | 'time_literal') { + if (typeString === 'chrono_literal') { + return literals[typeString]; + } + return `1 ${literals[typeString]}`; +} +function getFieldName( + typeString: 'string' | 'number' | 'date' | 'boolean' | 'ip', + { useNestedFunction, isStats }: { useNestedFunction: boolean; isStats: boolean } +) { + if (useNestedFunction && isStats) { + return prepareNestedFunction(toAvgSignature); + } + return useNestedFunction ? nestedFunctions[typeString] : `${typeString}Field`; +} + +function getMultiValue(type: 'string[]' | 'number[]' | 'boolean[]' | 'any[]') { + if (/string|any/.test(type)) { + return `["a", "b", "c"]`; + } + if (/number/.test(type)) { + return `[1, 2, 3]`; + } + return `[true, false]`; +} + +function prepareNestedFunction(fnSignature: FunctionDefinition): string { + return getFunctionSignatures( + { + ...fnSignature, + signatures: [ + { + ...fnSignature?.signatures[0]!, + params: getFieldMapping(fnSignature?.signatures[0]!.params), + }, + ], + }, + { withTypes: false } + )[0].declaration; +} +function getFieldMapping( + params: FunctionDefinition['signatures'][number]['params'], + { useNestedFunction, useLiterals }: { useNestedFunction: boolean; useLiterals: boolean } = { + useNestedFunction: false, + useLiterals: true, + } +) { + return params.map(({ name: _name, type, ...rest }) => { + const typeString: string = type; + if (['string', 'number', 'date', 'boolean', 'ip'].includes(typeString)) { + return { + name: getFieldName(typeString as 'string' | 'number' | 'date' | 'boolean' | 'ip', { + useNestedFunction, + isStats: !useLiterals, + }), + type, + ...rest, + }; + } + if (/literal$/.test(typeString) && useLiterals) { + return { + name: getLiteralType(typeString as 'chrono_literal' | 'time_literal'), + type, + ...rest, + }; + } + if (['string[]', 'number[]', 'boolean[]', 'any[]'].includes(typeString)) { + return { + name: getMultiValue(typeString as 'string[]' | 'number[]' | 'boolean[]' | 'any[]'), + type, + ...rest, + }; + } + return { name: 'stringField', type, ...rest }; + }); +} + +describe('validation logic', () => { + const getAstAndErrors = async ( + text: string | undefined + ): Promise<{ + errors: EditorError[]; + ast: ESQLAst; + }> => { + if (text == null) { + return { ast: [], errors: [] }; + } + const errorListener = new ESQLErrorListener(); + const parseListener = new AstListener(); + const parser = getParser(CharStreams.fromString(text), errorListener, parseListener); + + parser[ROOT_STATEMENT](); + + return { ...parseListener.getAst(), errors: errorListener.getErrors() }; + }; + + function testErrorsAndWarningsFn( + statement: string, + expectedErrors: string[] = [], + expectedWarnings: string[] = [], + { only, skip }: { only?: boolean; skip?: boolean } = {} + ) { + const testFn = only ? it.only : skip ? it.skip : it; + testFn( + `${statement} => ${expectedErrors.length} errors, ${expectedWarnings.length} warnings`, + async () => { + const callbackMocks = getCallbackMocks(); + const { warnings, errors } = await validateAst(statement, getAstAndErrors, callbackMocks); + expect(errors.map((e) => ('message' in e ? e.message : e.text))).toEqual(expectedErrors); + expect(warnings.map((w) => w.text)).toEqual(expectedWarnings); + } + ); + } + + type TestArgs = [string, string[], string[]?]; + + // Make only and skip work with our custom wrapper + const testErrorsAndWarnings = Object.assign(testErrorsAndWarningsFn, { + skip: (...args: TestArgs) => { + const warningArgs = [[]].slice(args.length - 2); + return testErrorsAndWarningsFn( + ...((args.length > 1 ? [...args, ...warningArgs] : args) as TestArgs), + { + skip: true, + } + ); + }, + only: (...args: TestArgs) => { + const warningArgs = [[]].slice(args.length - 2); + return testErrorsAndWarningsFn( + ...((args.length > 1 ? [...args, ...warningArgs] : args) as TestArgs), + { + only: true, + } + ); + }, + }); + + describe('ESQL query should start with a source command', () => { + ['eval', 'stats', 'rename', 'limit', 'keep', 'drop', 'mv_expand', 'dissect', 'grok'].map( + (command) => + testErrorsAndWarnings(command, [ + `SyntaxError: expected {FROM, ROW, SHOW} but found "${command}"`, + ]) + ); + }); + + describe('from', () => { + testErrorsAndWarnings('f', ['SyntaxError: expected {FROM, ROW, SHOW} but found "f"']); + testErrorsAndWarnings(`from `, [ + "SyntaxError: missing {SRC_UNQUOTED_IDENTIFIER, SRC_QUOTED_IDENTIFIER} at ''", + ]); + testErrorsAndWarnings(`from index,`, [ + "SyntaxError: missing {SRC_UNQUOTED_IDENTIFIER, SRC_QUOTED_IDENTIFIER} at ''", + ]); + testErrorsAndWarnings(`from assignment = 1`, [ + 'SyntaxError: expected {, PIPE, COMMA, OPENING_BRACKET} but found "="', + 'Unknown index [assignment]', + ]); + testErrorsAndWarnings(`from index`, []); + testErrorsAndWarnings(`FROM index`, []); + testErrorsAndWarnings(`FrOm index`, []); + testErrorsAndWarnings('from `index`', []); + + testErrorsAndWarnings(`from index, otherIndex`, []); + testErrorsAndWarnings(`from index, missingIndex`, ['Unknown index [missingIndex]']); + testErrorsAndWarnings(`from fn()`, ['Unknown index [fn()]']); + testErrorsAndWarnings(`from average()`, ['Unknown index [average()]']); + testErrorsAndWarnings(`from index [METADATA _id]`, []); + testErrorsAndWarnings(`from index [metadata _id]`, []); + + testErrorsAndWarnings(`from index [METADATA _id, _source]`, []); + testErrorsAndWarnings(`from index [metadata _id, _source] [METADATA _id2]`, [ + 'SyntaxError: expected {, PIPE} but found "["', + ]); + testErrorsAndWarnings(`from index metadata _id`, [ + 'SyntaxError: expected {, PIPE, COMMA, OPENING_BRACKET} but found "metadata"', + ]); + testErrorsAndWarnings(`from index (metadata _id)`, [ + 'SyntaxError: expected {, PIPE, COMMA, OPENING_BRACKET} but found "(metadata"', + ]); + testErrorsAndWarnings(`from ind*, other*`, []); + testErrorsAndWarnings(`from index*`, []); + testErrorsAndWarnings(`from *ex`, []); + testErrorsAndWarnings(`from in*ex`, []); + testErrorsAndWarnings(`from ind*ex`, []); + testErrorsAndWarnings(`from indexes*`, ['Unknown index [indexes*]']); + + testErrorsAndWarnings(`from remote-*:indexes*`, [ + 'ES|QL does not yet support querying remote indices [remote-*:indexes*]', + ]); + testErrorsAndWarnings(`from remote-*:indexes`, [ + 'ES|QL does not yet support querying remote indices [remote-*:indexes]', + ]); + testErrorsAndWarnings(`from remote-ccs:indexes`, [ + 'ES|QL does not yet support querying remote indices [remote-ccs:indexes]', + ]); + testErrorsAndWarnings(`from a, remote-ccs:indexes`, [ + 'ES|QL does not yet support querying remote indices [remote-ccs:indexes]', + ]); + testErrorsAndWarnings(`from remote-ccs:indexes [METADATA _id]`, [ + 'ES|QL does not yet support querying remote indices [remote-ccs:indexes]', + ]); + testErrorsAndWarnings(`from *:indexes [METADATA _id]`, [ + 'ES|QL does not yet support querying remote indices [*:indexes]', + ]); + testErrorsAndWarnings('from .secretIndex', []); + testErrorsAndWarnings('from my-index', []); + }); + + describe('row', () => { + testErrorsAndWarnings('row', [ + 'SyntaxError: expected {STRING, INTEGER_LITERAL, DECIMAL_LITERAL, FALSE, LP, NOT, NULL, PARAM, TRUE, PLUS, MINUS, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER} but found ""', + ]); + testErrorsAndWarnings('row missing_column', ['Unknown column [missing_column]']); + testErrorsAndWarnings('row fn()', ['Unknown function [fn]']); + testErrorsAndWarnings('row missing_column, missing_column2', [ + 'Unknown column [missing_column]', + 'Unknown column [missing_column2]', + ]); + testErrorsAndWarnings('row a=1', []); + testErrorsAndWarnings('row a=1, missing_column', ['Unknown column [missing_column]']); + testErrorsAndWarnings('row a=1, b = average()', ['Unknown function [average]']); + testErrorsAndWarnings('row a = [1, 2, 3]', []); + testErrorsAndWarnings('row a = (1)', []); + testErrorsAndWarnings('row a = (1, 2, 3)', [ + 'SyntaxError: expected {STRING, INTEGER_LITERAL, DECIMAL_LITERAL, FALSE, LP, NOT, NULL, PARAM, TRUE, PLUS, MINUS, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER} but found ","', + "SyntaxError: extraneous input ')' expecting ", + ]); + + testErrorsAndWarnings('row var = 1 in (1, 2, 3)', []); + testErrorsAndWarnings('row var = 5 in (1, 2, 3)', []); + testErrorsAndWarnings('row var = 5 not in (1, 2, 3)', []); + testErrorsAndWarnings('row var = 1 in (1, 2, 3, round(5))', []); + testErrorsAndWarnings('row var = "a" in ("a", "b", "c")', []); + testErrorsAndWarnings('row var = "a" in ("a", "b", "c")', []); + testErrorsAndWarnings('row var = "a" not in ("a", "b", "c")', []); + testErrorsAndWarnings('row var = 1 in ("a", "b", "c")', [ + 'Argument of [in] must be [number[]], found value [("a", "b", "c")] type [(string, string, string)]', + ]); + testErrorsAndWarnings('row var = 5 in ("a", "b", "c")', [ + 'Argument of [in] must be [number[]], found value [("a", "b", "c")] type [(string, string, string)]', + ]); + testErrorsAndWarnings('row var = 5 not in ("a", "b", "c")', [ + 'Argument of [not_in] must be [number[]], found value [("a", "b", "c")] type [(string, string, string)]', + ]); + testErrorsAndWarnings('row var = 5 not in (1, 2, 3, "a")', [ + 'Argument of [not_in] must be [number[]], found value [(1, 2, 3, "a")] type [(number, number, number, string)]', + ]); + + function tweakSignatureForRowCommand(signature: string) { + /** + * row has no access to any field, so replace it with literal + * or functions (for dates) + */ + return signature + .replace(/numberField/g, '5') + .replace(/stringField/g, '"a"') + .replace(/dateField/g, 'now()') + .replace(/booleanField/g, 'true') + .replace(/ipField/g, 'to_ip("127.0.0.1")'); + } + + for (const { name, alias, signatures, ...defRest } of evalFunctionsDefinitions) { + for (const { params, returnType } of signatures) { + const fieldMapping = getFieldMapping(params); + const signatureStringCorrect = tweakSignatureForRowCommand( + getFunctionSignatures( + { name, ...defRest, signatures: [{ params: fieldMapping, returnType }] }, + { withTypes: false } + )[0].declaration + ); + + testErrorsAndWarnings(`row var = ${signatureStringCorrect}`, []); + testErrorsAndWarnings(`row ${signatureStringCorrect}`); + + if (alias) { + for (const otherName of alias) { + const signatureStringWithAlias = tweakSignatureForRowCommand( + getFunctionSignatures( + { name: otherName, ...defRest, signatures: [{ params: fieldMapping, returnType }] }, + { withTypes: false } + )[0].declaration + ); + + testErrorsAndWarnings(`row var = ${signatureStringWithAlias}`, []); + } + } + + // Skip functions that have only arguments of type "any", as it is not possible to pass "the wrong type". + // auto_bucket and to_version functions are a bit harder to test exactly a combination of argument and predict the + // the right error message + if ( + params.every(({ type }) => type !== 'any') && + !['auto_bucket', 'to_version'].includes(name) + ) { + // now test nested functions + const fieldMappingWithNestedFunctions = getFieldMapping(params, { + useNestedFunction: true, + useLiterals: true, + }); + const signatureString = tweakSignatureForRowCommand( + getFunctionSignatures( + { + name, + ...defRest, + signatures: [{ params: fieldMappingWithNestedFunctions, returnType }], + }, + { withTypes: false } + )[0].declaration + ); + + testErrorsAndWarnings(`row var = ${signatureString}`); + + const wrongFieldMapping = params.map(({ name: _name, type, ...rest }) => { + const typeString = type; + const canBeFieldButNotString = ['number', 'date', 'boolean', 'ip'].includes(typeString); + const isLiteralType = /literal$/.test(typeString); + // pick a field name purposely wrong + const nameValue = canBeFieldButNotString || isLiteralType ? '"a"' : '5'; + return { name: nameValue, type, ...rest }; + }); + const expectedErrors = params.map( + ({ type }, i) => + `Argument of [${name}] must be [${type}], found value [${ + wrongFieldMapping[i].name + }] type [${wrongFieldMapping[i].name === '5' ? 'number' : 'string'}]` + ); + const wrongSignatureString = tweakSignatureForRowCommand( + getFunctionSignatures( + { name, ...defRest, signatures: [{ params: wrongFieldMapping, returnType }] }, + { withTypes: false } + )[0].declaration + ); + testErrorsAndWarnings(`row var = ${wrongSignatureString}`, expectedErrors); + } + } + } + for (const op of ['>', '>=', '<', '<=', '==']) { + testErrorsAndWarnings(`row var = 5 ${op} 0`, []); + testErrorsAndWarnings(`row var = NOT 5 ${op} 0`, []); + testErrorsAndWarnings(`row var = (numberField ${op} 0)`, []); + testErrorsAndWarnings(`row var = (NOT (5 ${op} 0))`, []); + testErrorsAndWarnings(`row var = "a" ${op} 0`, [ + `Argument of [${op}] must be [number], found value ["a"] type [string]`, + ]); + } + for (const op of ['+', '-', '*', '/', '%']) { + testErrorsAndWarnings(`row var = 1 ${op} 1`, []); + testErrorsAndWarnings(`row var = (5 ${op} 1)`, []); + } + + for (const op of ['like', 'rlike']) { + testErrorsAndWarnings(`row var = "a" ${op} "?a"`, []); + testErrorsAndWarnings(`row var = "a" NOT ${op} "?a"`, []); + testErrorsAndWarnings(`row var = NOT "a" ${op} "?a"`, []); + testErrorsAndWarnings(`row var = NOT "a" NOT ${op} "?a"`, []); + testErrorsAndWarnings(`row var = 5 ${op} "?a"`, [ + `Argument of [${op}] must be [string], found value [5] type [number]`, + ]); + testErrorsAndWarnings(`row var = 5 NOT ${op} "?a"`, [ + `Argument of [not_${op}] must be [string], found value [5] type [number]`, + ]); + testErrorsAndWarnings(`row var = NOT 5 ${op} "?a"`, [ + `Argument of [${op}] must be [string], found value [5] type [number]`, + ]); + testErrorsAndWarnings(`row var = NOT 5 NOT ${op} "?a"`, [ + `Argument of [not_${op}] must be [string], found value [5] type [number]`, + ]); + } + + describe('date math', () => { + testErrorsAndWarnings('row 1 anno', [ + 'Row does not support [date_period] in expression [1 anno]', + ]); + testErrorsAndWarnings('row var = 1 anno', ["Unexpected time interval qualifier: 'anno'"]); + testErrorsAndWarnings('row now() + 1 anno', ["Unexpected time interval qualifier: 'anno'"]); + for (const timeLiteral of timeLiterals) { + testErrorsAndWarnings(`row 1 ${timeLiteral.name}`, [ + `Row does not support [date_period] in expression [1 ${timeLiteral.name}]`, + ]); + testErrorsAndWarnings(`row 1 ${timeLiteral.name}`, [ + `Row does not support [date_period] in expression [1 ${timeLiteral.name}]`, + ]); + + // this is not possible for now + // testErrorsAndWarnings(`row var = 1 ${timeLiteral.name}`, [ + // `Row does not support [date_period] in expression [1 ${timeLiteral.name}]`, + // ]); + testErrorsAndWarnings(`row var = now() - 1 ${timeLiteral.name}`, []); + testErrorsAndWarnings(`row var = now() - 1 ${timeLiteral.name.toUpperCase()}`, []); + testErrorsAndWarnings(`row var = now() - 1 ${capitalize(timeLiteral.name)}`, []); + testErrorsAndWarnings(`row var = now() + 1 ${timeLiteral.name}`, []); + testErrorsAndWarnings(`row 1 ${timeLiteral.name} + 1 year`, [ + `Argument of [+] must be [date], found value [1 ${timeLiteral.name}] type [duration]`, + ]); + for (const op of ['*', '/', '%']) { + testErrorsAndWarnings(`row var = now() ${op} 1 ${timeLiteral.name}`, [ + `Argument of [${op}] must be [number], found value [now()] type [date]`, + `Argument of [${op}] must be [number], found value [1 ${timeLiteral.name}] type [duration]`, + ]); + } + } + }); + }); + + describe('show', () => { + testErrorsAndWarnings('show', ['SyntaxError: expected {SHOW} but found ""']); + testErrorsAndWarnings('show functions', []); + testErrorsAndWarnings('show info', []); + testErrorsAndWarnings('show functions blah', [ + "SyntaxError: extraneous input 'blah' expecting ", + ]); + }); + + describe('limit', () => { + testErrorsAndWarnings('from index | limit ', [ + `SyntaxError: missing INTEGER_LITERAL at ''`, + ]); + testErrorsAndWarnings('from index | limit 4 ', []); + testErrorsAndWarnings('from index | limit 4.5', [ + 'SyntaxError: expected {INTEGER_LITERAL} but found "4.5"', + ]); + testErrorsAndWarnings('from index | limit a', [ + 'SyntaxError: expected {INTEGER_LITERAL} but found "a"', + ]); + testErrorsAndWarnings('from index | limit numberField', [ + 'SyntaxError: expected {INTEGER_LITERAL} but found "numberField"', + ]); + testErrorsAndWarnings('from index | limit stringField', [ + 'SyntaxError: expected {INTEGER_LITERAL} but found "stringField"', + ]); + testErrorsAndWarnings('from index | limit 4', []); + }); + + describe('keep', () => { + testErrorsAndWarnings('from index | keep ', [ + `SyntaxError: missing {SRC_UNQUOTED_IDENTIFIER, SRC_QUOTED_IDENTIFIER} at ''`, + ]); + testErrorsAndWarnings('from index | keep stringField, numberField, dateField', []); + testErrorsAndWarnings('from index | keep `stringField`, `numberField`, `dateField`', []); + testErrorsAndWarnings('from index | keep 4.5', ['Unknown column [4.5]']); + testErrorsAndWarnings('from index | keep missingField, numberField, dateField', [ + 'Unknown column [missingField]', + ]); + testErrorsAndWarnings('from index | keep `any#Char$ field`', []); + testErrorsAndWarnings( + 'from index | project ', + [`SyntaxError: missing {SRC_UNQUOTED_IDENTIFIER, SRC_QUOTED_IDENTIFIER} at ''`], + ['PROJECT command is no longer supported, please use KEEP instead'] + ); + testErrorsAndWarnings( + 'from index | project stringField, numberField, dateField', + [], + ['PROJECT command is no longer supported, please use KEEP instead'] + ); + testErrorsAndWarnings( + 'from index | PROJECT stringField, numberField, dateField', + [], + ['PROJECT command is no longer supported, please use KEEP instead'] + ); + testErrorsAndWarnings( + 'from index | project missingField, numberField, dateField', + ['Unknown column [missingField]'], + ['PROJECT command is no longer supported, please use KEEP instead'] + ); + testErrorsAndWarnings('from index | keep s*', []); + testErrorsAndWarnings('from index | keep *Field', []); + testErrorsAndWarnings('from index | keep s*Field', []); + testErrorsAndWarnings('from index | keep string*Field', []); + testErrorsAndWarnings('from index | keep s*, n*', []); + testErrorsAndWarnings('from index | keep m*', ['Unknown column [m*]']); + testErrorsAndWarnings('from index | keep *m', ['Unknown column [*m]']); + testErrorsAndWarnings('from index | keep d*m', ['Unknown column [d*m]']); + testErrorsAndWarnings( + 'from unsupported_index | keep unsupported_field', + [], + [ + 'Field [unsupported_field] cannot be retrieved, it is unsupported or not indexed; returning null', + ] + ); + }); + + describe('drop', () => { + testErrorsAndWarnings('from index | drop ', [ + `SyntaxError: missing {SRC_UNQUOTED_IDENTIFIER, SRC_QUOTED_IDENTIFIER} at ''`, + ]); + testErrorsAndWarnings('from index | drop stringField, numberField, dateField', []); + testErrorsAndWarnings('from index | drop 4.5', ['Unknown column [4.5]']); + testErrorsAndWarnings('from index | drop missingField, numberField, dateField', [ + 'Unknown column [missingField]', + ]); + testErrorsAndWarnings('from index | drop `any#Char$ field`', []); + testErrorsAndWarnings('from index | drop s*', []); + testErrorsAndWarnings('from index | drop *Field', []); + testErrorsAndWarnings('from index | drop s*Field', []); + testErrorsAndWarnings('from index | drop string*Field', []); + testErrorsAndWarnings('from index | drop s*, n*', []); + testErrorsAndWarnings('from index | drop m*', ['Unknown column [m*]']); + testErrorsAndWarnings('from index | drop *m', ['Unknown column [*m]']); + testErrorsAndWarnings('from index | drop d*m', ['Unknown column [d*m]']); + testErrorsAndWarnings('from index | drop *', ['Removing all fields is not allowed [*]']); + testErrorsAndWarnings('from index | drop stringField, *', [ + 'Removing all fields is not allowed [*]', + ]); + testErrorsAndWarnings( + 'from index | drop @timestamp', + [], + ['Drop [@timestamp] will remove all time filters to the search results'] + ); + testErrorsAndWarnings( + 'from index | drop stringField, @timestamp', + [], + ['Drop [@timestamp] will remove all time filters to the search results'] + ); + }); + + describe('mv_expand', () => { + testErrorsAndWarnings('from a | mv_expand ', [ + "SyntaxError: missing {SRC_UNQUOTED_IDENTIFIER, SRC_QUOTED_IDENTIFIER} at ''", + ]); + testErrorsAndWarnings('from a | mv_expand stringField', [ + 'Mv_expand only supports list type values, found [stringField] of type string', + ]); + + testErrorsAndWarnings(`from a | mv_expand listField`, []); + + testErrorsAndWarnings('from a | mv_expand listField, b', [ + 'SyntaxError: expected {, PIPE} but found ","', + ]); + + testErrorsAndWarnings('row a = "a" | mv_expand a', [ + 'Mv_expand only supports list type values, found [a] of type string', + ]); + testErrorsAndWarnings('row a = [1, 2, 3] | mv_expand a', []); + }); + + describe('rename', () => { + testErrorsAndWarnings('from a | rename', [ + "SyntaxError: missing {SRC_UNQUOTED_IDENTIFIER, SRC_QUOTED_IDENTIFIER} at ''", + ]); + testErrorsAndWarnings('from a | rename stringField', [ + 'SyntaxError: expected {AS} but found ""', + ]); + testErrorsAndWarnings('from a | rename a', [ + 'SyntaxError: expected {AS} but found ""', + 'Unknown column [a]', + ]); + testErrorsAndWarnings('from a | rename stringField as', [ + "SyntaxError: missing {SRC_UNQUOTED_IDENTIFIER, SRC_QUOTED_IDENTIFIER} at ''", + ]); + testErrorsAndWarnings('from a | rename missingField as', [ + "SyntaxError: missing {SRC_UNQUOTED_IDENTIFIER, SRC_QUOTED_IDENTIFIER} at ''", + 'Unknown column [missingField]', + ]); + testErrorsAndWarnings('from a | rename stringField as b', []); + testErrorsAndWarnings('from a | rename stringField AS b', []); + testErrorsAndWarnings('from a | rename stringField As b', []); + testErrorsAndWarnings('from a | rename stringField As b, b AS c', []); + testErrorsAndWarnings('from a | rename fn() as a', [ + 'Unknown column [fn()]', + 'Unknown column [a]', + ]); + testErrorsAndWarnings('from a | eval numberField + 1 | rename `numberField + 1` as a', []); + testErrorsAndWarnings( + 'from a | stats avg(numberField) | rename `avg(numberField)` as avg0', + [] + ); + testErrorsAndWarnings('from a | eval numberField + 1 | rename `numberField + 1` as ', [ + "SyntaxError: missing {SRC_UNQUOTED_IDENTIFIER, SRC_QUOTED_IDENTIFIER} at ''", + ]); + testErrorsAndWarnings('from a | rename s* as strings', [ + 'Using wildcards (*) in rename is not allowed [s*]', + 'Unknown column [strings]', + ]); + }); + + describe('dissect', () => { + testErrorsAndWarnings('from a | dissect', [ + 'SyntaxError: expected {STRING, INTEGER_LITERAL, DECIMAL_LITERAL, FALSE, LP, NULL, PARAM, TRUE, PLUS, MINUS, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER} but found ""', + ]); + testErrorsAndWarnings('from a | dissect stringField', [ + "SyntaxError: missing STRING at ''", + ]); + testErrorsAndWarnings('from a | dissect stringField 2', [ + 'SyntaxError: expected {STRING, DOT} but found "2"', + ]); + testErrorsAndWarnings('from a | dissect stringField .', [ + "SyntaxError: missing {UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER} at ''", + 'Unknown column [stringField.]', + ]); + testErrorsAndWarnings('from a | dissect stringField %a', [ + "SyntaxError: missing STRING at '%'", + ]); + // Do not try to validate the dissect pattern string + testErrorsAndWarnings('from a | dissect stringField "%{a}"', []); + testErrorsAndWarnings('from a | dissect numberField "%{a}"', [ + 'Dissect only supports string type values, found [numberField] of type number', + ]); + testErrorsAndWarnings('from a | dissect stringField "%{a}" option ', [ + 'SyntaxError: expected {ASSIGN} but found ""', + ]); + testErrorsAndWarnings('from a | dissect stringField "%{a}" option = ', [ + 'SyntaxError: expected {STRING, INTEGER_LITERAL, DECIMAL_LITERAL, FALSE, NULL, PARAM, TRUE, PLUS, MINUS, OPENING_BRACKET} but found ""', + 'Invalid option for dissect: [option]', + ]); + testErrorsAndWarnings('from a | dissect stringField "%{a}" option = 1', [ + 'Invalid option for dissect: [option]', + ]); + testErrorsAndWarnings('from a | dissect stringField "%{a}" append_separator = "-"', []); + testErrorsAndWarnings('from a | dissect stringField "%{a}" ignore_missing = true', [ + 'Invalid option for dissect: [ignore_missing]', + ]); + testErrorsAndWarnings('from a | dissect stringField "%{a}" append_separator = true', [ + 'Invalid value for dissect append_separator: expected a string, but was [true]', + ]); + // testErrorsAndWarnings('from a | dissect s* "%{a}"', [ + // 'Using wildcards (*) in dissect is not allowed [s*]', + // ]); + }); + + describe('grok', () => { + testErrorsAndWarnings('from a | grok', [ + 'SyntaxError: expected {STRING, INTEGER_LITERAL, DECIMAL_LITERAL, FALSE, LP, NULL, PARAM, TRUE, PLUS, MINUS, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER} but found ""', + ]); + testErrorsAndWarnings('from a | grok stringField', ["SyntaxError: missing STRING at ''"]); + testErrorsAndWarnings('from a | grok stringField 2', [ + 'SyntaxError: expected {STRING, DOT} but found "2"', + ]); + testErrorsAndWarnings('from a | grok stringField .', [ + "SyntaxError: missing {UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER} at ''", + 'Unknown column [stringField.]', + ]); + testErrorsAndWarnings('from a | grok stringField %a', ["SyntaxError: missing STRING at '%'"]); + // Do not try to validate the grok pattern string + testErrorsAndWarnings('from a | grok stringField "%{a}"', []); + testErrorsAndWarnings('from a | grok numberField "%{a}"', [ + 'Grok only supports string type values, found [numberField] of type number', + ]); + // testErrorsAndWarnings('from a | grok s* "%{a}"', [ + // 'Using wildcards (*) in grok is not allowed [s*]', + // ]); + }); + + describe('where', () => { + testErrorsAndWarnings('from a | where b', ['Unknown column [b]']); + for (const cond of ['true', 'false']) { + testErrorsAndWarnings(`from a | where ${cond}`, []); + testErrorsAndWarnings(`from a | where NOT ${cond}`, []); + } + for (const nValue of ['1', '+1', '1 * 1', '-1', '1 / 1']) { + testErrorsAndWarnings(`from a | where ${nValue} > 0`, []); + testErrorsAndWarnings(`from a | where NOT ${nValue} > 0`, []); + } + for (const op of ['>', '>=', '<', '<=', '==']) { + testErrorsAndWarnings(`from a | where numberField ${op} 0`, []); + testErrorsAndWarnings(`from a | where NOT numberField ${op} 0`, []); + testErrorsAndWarnings(`from a | where (numberField ${op} 0)`, []); + testErrorsAndWarnings(`from a | where (NOT (numberField ${op} 0))`, []); + testErrorsAndWarnings(`from a | where 1 ${op} 0`, []); + testErrorsAndWarnings(`from a | eval stringField ${op} 0`, [ + `Argument of [${op}] must be [number], found value [stringField] type [string]`, + ]); + } + for (const op of ['like', 'rlike']) { + testErrorsAndWarnings(`from a | where stringField ${op} "?a"`, []); + testErrorsAndWarnings(`from a | where stringField NOT ${op} "?a"`, []); + testErrorsAndWarnings(`from a | where NOT stringField ${op} "?a"`, []); + testErrorsAndWarnings(`from a | where NOT stringField NOT ${op} "?a"`, []); + testErrorsAndWarnings(`from a | where numberField ${op} "?a"`, [ + `Argument of [${op}] must be [string], found value [numberField] type [number]`, + ]); + testErrorsAndWarnings(`from a | where numberField NOT ${op} "?a"`, [ + `Argument of [not_${op}] must be [string], found value [numberField] type [number]`, + ]); + testErrorsAndWarnings(`from a | where NOT numberField ${op} "?a"`, [ + `Argument of [${op}] must be [string], found value [numberField] type [number]`, + ]); + testErrorsAndWarnings(`from a | where NOT numberField NOT ${op} "?a"`, [ + `Argument of [not_${op}] must be [string], found value [numberField] type [number]`, + ]); + } + + testErrorsAndWarnings(`from a | where cidr_match(ipField)`, [ + `Error building [cidr_match]: expects exactly 2 arguments, passed 1 instead.`, + ]); + testErrorsAndWarnings( + `from a | eval cidr = "172.0.0.1/30" | where cidr_match(ipField, "172.0.0.1/30", cidr)`, + [] + ); + + // Test that all functions work in where + const numericOrStringFunctions = evalFunctionsDefinitions.filter(({ name, signatures }) => { + return signatures.some( + ({ returnType, params }) => + ['number', 'string'].includes(returnType) && + params.every(({ type }) => ['number', 'string'].includes(type)) + ); + }); + for (const { name, signatures, ...rest } of numericOrStringFunctions) { + const supportedSignatures = signatures.filter(({ returnType }) => + ['number', 'string'].includes(returnType) + ); + for (const { params, returnType } of supportedSignatures) { + const correctMapping = params + .filter(({ optional }) => !optional) + .map(({ type }) => + ['number', 'string'].includes(Array.isArray(type) ? type.join(', ') : type) + ? { name: `${type}Field`, type } + : { name: `numberField`, type } + ); + testErrorsAndWarnings( + `from a | where ${returnType !== 'number' ? 'length(' : ''}${ + // hijacking a bit this function to produce a function call + getFunctionSignatures( + { name, ...rest, signatures: [{ params: correctMapping, returnType }] }, + { withTypes: false } + )[0].declaration + }${returnType !== 'number' ? ')' : ''} > 0`, + [] + ); + + // now test that validation is working also inside each function + // put a number field where a string is expected and viceversa + // then test an error is returned + const incorrectMapping = params + .filter(({ optional }) => !optional) + .map(({ type }) => + type === 'string' ? { name: `numberField`, type } : { name: 'stringField', type } + ); + + const expectedErrors = params + .filter(({ optional }) => !optional) + .map(({ name: argName, type }) => { + const actualValue = + type === 'string' + ? { name: `numberField`, type: 'number' } + : { name: 'stringField', type: 'string' }; + return `Argument of [${name}] must be [${type}], found value [${actualValue.name}] type [${actualValue.type}]`; + }); + testErrorsAndWarnings( + `from a | where ${returnType !== 'number' ? 'length(' : ''}${ + // hijacking a bit this function to produce a function call + getFunctionSignatures( + { name, ...rest, signatures: [{ params: incorrectMapping, returnType }] }, + { withTypes: false } + )[0].declaration + }${returnType !== 'number' ? ')' : ''} > 0`, + expectedErrors + ); + } + } + }); + + describe('eval', () => { + testErrorsAndWarnings('from a | eval ', [ + 'SyntaxError: expected {STRING, INTEGER_LITERAL, DECIMAL_LITERAL, FALSE, LP, NOT, NULL, PARAM, TRUE, PLUS, MINUS, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER} but found ""', + ]); + testErrorsAndWarnings('from a | eval stringField ', []); + testErrorsAndWarnings('from a | eval b = stringField', []); + testErrorsAndWarnings('from a | eval numberField + 1', []); + testErrorsAndWarnings('from a | eval numberField + ', [ + 'SyntaxError: expected {STRING, INTEGER_LITERAL, DECIMAL_LITERAL, FALSE, LP, NOT, NULL, PARAM, TRUE, PLUS, MINUS, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER} but found ""', + ]); + testErrorsAndWarnings('from a | eval stringField + 1', [ + 'Argument of [+] must be [number], found value [stringField] type [string]', + ]); + testErrorsAndWarnings('from a | eval a=b', ['Unknown column [b]']); + testErrorsAndWarnings('from a | eval a=b, ', [ + 'SyntaxError: expected {STRING, INTEGER_LITERAL, DECIMAL_LITERAL, FALSE, LP, NOT, NULL, PARAM, TRUE, PLUS, MINUS, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER} but found ""', + 'Unknown column [b]', + ]); + testErrorsAndWarnings('from a | eval a=round', ['Unknown column [round]']); + testErrorsAndWarnings('from a | eval a=round(', [ + 'SyntaxError: expected {STRING, INTEGER_LITERAL, DECIMAL_LITERAL, FALSE, LP, NOT, NULL, PARAM, TRUE, PLUS, MINUS, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER} but found ""', + ]); + testErrorsAndWarnings('from a | eval a=round(numberField) ', []); + testErrorsAndWarnings('from a | eval a=round(numberField), ', [ + 'SyntaxError: expected {STRING, INTEGER_LITERAL, DECIMAL_LITERAL, FALSE, LP, NOT, NULL, PARAM, TRUE, PLUS, MINUS, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER} but found ""', + ]); + testErrorsAndWarnings('from a | eval a=round(numberField) + round(numberField) ', []); + testErrorsAndWarnings('from a | eval a=round(numberField) + round(stringField) ', [ + 'Argument of [round] must be [number], found value [stringField] type [string]', + ]); + testErrorsAndWarnings( + 'from a | eval a=round(numberField) + round(stringField), numberField ', + ['Argument of [round] must be [number], found value [stringField] type [string]'] + ); + testErrorsAndWarnings( + 'from a | eval a=round(numberField) + round(numberField), numberField ', + [] + ); + testErrorsAndWarnings( + 'from a | eval a=round(numberField) + round(numberField), b = numberField ', + [] + ); + + for (const { name, alias, signatures, ...defRest } of evalFunctionsDefinitions) { + for (const { params, returnType } of signatures) { + const fieldMapping = getFieldMapping(params); + testErrorsAndWarnings( + `from a | eval var = ${ + getFunctionSignatures( + { name, ...defRest, signatures: [{ params: fieldMapping, returnType }] }, + { withTypes: false } + )[0].declaration + }` + ); + testErrorsAndWarnings( + `from a | eval ${ + getFunctionSignatures( + { name, ...defRest, signatures: [{ params: fieldMapping, returnType }] }, + { withTypes: false } + )[0].declaration + }` + ); + + if (alias) { + for (const otherName of alias) { + const signatureStringWithAlias = getFunctionSignatures( + { name: otherName, ...defRest, signatures: [{ params: fieldMapping, returnType }] }, + { withTypes: false } + )[0].declaration; + + testErrorsAndWarnings(`from a | eval var = ${signatureStringWithAlias}`, []); + } + } + + // Skip functions that have only arguments of type "any", as it is not possible to pass "the wrong type". + // auto_bucket and to_version functions are a bit harder to test exactly a combination of argument and predict the + // the right error message + if ( + params.every(({ type }) => type !== 'any') && + !['auto_bucket', 'to_version'].includes(name) + ) { + // now test nested functions + const fieldMappingWithNestedFunctions = getFieldMapping(params, { + useNestedFunction: true, + useLiterals: true, + }); + testErrorsAndWarnings( + `from a | eval var = ${ + getFunctionSignatures( + { + name, + ...defRest, + signatures: [{ params: fieldMappingWithNestedFunctions, returnType }], + }, + { withTypes: false } + )[0].declaration + }` + ); + + const wrongFieldMapping = params.map(({ name: _name, type, ...rest }) => { + const typeString = type; + const canBeFieldButNotString = ['number', 'date', 'boolean', 'ip'].includes(typeString); + const isLiteralType = /literal$/.test(typeString); + // pick a field name purposely wrong + const nameValue = + canBeFieldButNotString || isLiteralType ? 'stringField' : 'numberField'; + return { name: nameValue, type, ...rest }; + }); + const expectedErrors = params.map( + ({ type }, i) => + `Argument of [${name}] must be [${type}], found value [${ + wrongFieldMapping[i].name + }] type [${wrongFieldMapping[i].name.replace('Field', '')}]` + ); + testErrorsAndWarnings( + `from a | eval ${ + getFunctionSignatures( + { name, ...defRest, signatures: [{ params: wrongFieldMapping, returnType }] }, + { withTypes: false } + )[0].declaration + }`, + expectedErrors + ); + } + + // test that wildcard won't work as arg + if (fieldMapping.length === 1) { + const fieldMappingWithWildcard = [...fieldMapping]; + fieldMappingWithWildcard[0].name = '*'; + + testErrorsAndWarnings( + `from a | eval var = ${ + getFunctionSignatures( + { + name, + ...defRest, + signatures: [{ params: fieldMappingWithWildcard, returnType }], + }, + { withTypes: false } + )[0].declaration + }`, + [`Using wildcards (*) in ${name} is not allowed`] + ); + } + } + } + for (const op of ['>', '>=', '<', '<=', '==']) { + testErrorsAndWarnings(`from a | eval numberField ${op} 0`, []); + testErrorsAndWarnings(`from a | eval NOT numberField ${op} 0`, []); + testErrorsAndWarnings(`from a | eval (numberField ${op} 0)`, []); + testErrorsAndWarnings(`from a | eval (NOT (numberField ${op} 0))`, []); + testErrorsAndWarnings(`from a | eval 1 ${op} 0`, []); + testErrorsAndWarnings(`from a | eval stringField ${op} 0`, [ + `Argument of [${op}] must be [number], found value [stringField] type [string]`, + ]); + } + for (const op of ['+', '-', '*', '/', '%']) { + testErrorsAndWarnings(`from a | eval numberField ${op} 1`, []); + testErrorsAndWarnings(`from a | eval (numberField ${op} 1)`, []); + testErrorsAndWarnings(`from a | eval 1 ${op} 1`, []); + } + for (const divideByZeroExpr of ['1/0', 'var = 1/0', '1 + 1/0']) { + testErrorsAndWarnings( + `from a | eval ${divideByZeroExpr}`, + [], + ['Cannot divide by zero: 1/0'] + ); + } + for (const divideByZeroExpr of ['1%0', 'var = 1%0', '1 + 1%0']) { + testErrorsAndWarnings( + `from a | eval ${divideByZeroExpr}`, + [], + ['Module by zero can return null value: 1/0'] + ); + } + for (const op of ['like', 'rlike']) { + testErrorsAndWarnings(`from a | eval stringField ${op} "?a"`, []); + testErrorsAndWarnings(`from a | eval stringField NOT ${op} "?a"`, []); + testErrorsAndWarnings(`from a | eval NOT stringField ${op} "?a"`, []); + testErrorsAndWarnings(`from a | eval NOT stringField NOT ${op} "?a"`, []); + testErrorsAndWarnings(`from a | eval numberField ${op} "?a"`, [ + `Argument of [${op}] must be [string], found value [numberField] type [number]`, + ]); + testErrorsAndWarnings(`from a | eval numberField NOT ${op} "?a"`, [ + `Argument of [not_${op}] must be [string], found value [numberField] type [number]`, + ]); + testErrorsAndWarnings(`from a | eval NOT numberField ${op} "?a"`, [ + `Argument of [${op}] must be [string], found value [numberField] type [number]`, + ]); + testErrorsAndWarnings(`from a | eval NOT numberField NOT ${op} "?a"`, [ + `Argument of [not_${op}] must be [string], found value [numberField] type [number]`, + ]); + } + // test lists + testErrorsAndWarnings('from a | eval 1 in (1, 2, 3)', []); + testErrorsAndWarnings('from a | eval numberField in (1, 2, 3)', []); + testErrorsAndWarnings('from a | eval numberField not in (1, 2, 3)', []); + testErrorsAndWarnings('from a | eval numberField not in (1, 2, 3, numberField)', []); + testErrorsAndWarnings('from a | eval 1 in (1, 2, 3, round(numberField))', []); + testErrorsAndWarnings('from a | eval "a" in ("a", "b", "c")', []); + testErrorsAndWarnings('from a | eval stringField in ("a", "b", "c")', []); + testErrorsAndWarnings('from a | eval stringField not in ("a", "b", "c")', []); + testErrorsAndWarnings('from a | eval stringField not in ("a", "b", "c", stringField)', []); + testErrorsAndWarnings('from a | eval 1 in ("a", "b", "c")', [ + 'Argument of [in] must be [number[]], found value [("a", "b", "c")] type [(string, string, string)]', + ]); + testErrorsAndWarnings('from a | eval numberField in ("a", "b", "c")', [ + 'Argument of [in] must be [number[]], found value [("a", "b", "c")] type [(string, string, string)]', + ]); + testErrorsAndWarnings('from a | eval numberField not in ("a", "b", "c")', [ + 'Argument of [not_in] must be [number[]], found value [("a", "b", "c")] type [(string, string, string)]', + ]); + testErrorsAndWarnings('from a | eval numberField not in (1, 2, 3, stringField)', [ + 'Argument of [not_in] must be [number[]], found value [(1, 2, 3, stringField)] type [(number, number, number, string)]', + ]); + + testErrorsAndWarnings('from a | eval avg(numberField)', ['Eval does not support function avg']); + testErrorsAndWarnings('from a | stats avg(numberField) | eval `avg(numberField)` + 1', []); + + describe('date math', () => { + testErrorsAndWarnings('from a | eval 1 anno', [ + 'Eval does not support [date_period] in expression [1 anno]', + ]); + testErrorsAndWarnings('from a | eval var = 1 anno', [ + "Unexpected time interval qualifier: 'anno'", + ]); + testErrorsAndWarnings('from a | eval now() + 1 anno', [ + "Unexpected time interval qualifier: 'anno'", + ]); + for (const timeLiteral of timeLiterals) { + testErrorsAndWarnings(`from a | eval 1 ${timeLiteral.name}`, [ + `Eval does not support [date_period] in expression [1 ${timeLiteral.name}]`, + ]); + testErrorsAndWarnings(`from a | eval 1 ${timeLiteral.name}`, [ + `Eval does not support [date_period] in expression [1 ${timeLiteral.name}]`, + ]); + + // this is not possible for now + // testErrorsAndWarnings(`from a | eval var = 1 ${timeLiteral.name}`, [ + // `Eval does not support [date_period] in expression [1 ${timeLiteral.name}]`, + // ]); + testErrorsAndWarnings(`from a | eval var = now() - 1 ${timeLiteral.name}`, []); + testErrorsAndWarnings(`from a | eval var = dateField - 1 ${timeLiteral.name}`, []); + testErrorsAndWarnings( + `from a | eval var = dateField - 1 ${timeLiteral.name.toUpperCase()}`, + [] + ); + testErrorsAndWarnings( + `from a | eval var = dateField - 1 ${capitalize(timeLiteral.name)}`, + [] + ); + testErrorsAndWarnings(`from a | eval var = dateField + 1 ${timeLiteral.name}`, []); + testErrorsAndWarnings(`from a | eval 1 ${timeLiteral.name} + 1 year`, [ + `Argument of [+] must be [date], found value [1 ${timeLiteral.name}] type [duration]`, + ]); + for (const op of ['*', '/', '%']) { + testErrorsAndWarnings(`from a | eval var = now() ${op} 1 ${timeLiteral.name}`, [ + `Argument of [${op}] must be [number], found value [now()] type [date]`, + `Argument of [${op}] must be [number], found value [1 ${timeLiteral.name}] type [duration]`, + ]); + } + } + }); + }); + + describe('stats', () => { + testErrorsAndWarnings('from a | stats ', []); + testErrorsAndWarnings('from a | stats numberField ', [ + 'Stats expects an aggregate function, found [numberField]', + ]); + testErrorsAndWarnings('from a | stats numberField=', [ + 'SyntaxError: expected {STRING, INTEGER_LITERAL, DECIMAL_LITERAL, FALSE, LP, NOT, NULL, PARAM, TRUE, PLUS, MINUS, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER} but found ""', + ]); + testErrorsAndWarnings('from a | stats numberField=5 by ', [ + "SyntaxError: missing {UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER} at ''", + ]); + testErrorsAndWarnings('from a | stats numberField=5 by ', [ + "SyntaxError: missing {UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER} at ''", + ]); + + testErrorsAndWarnings('from a | stats avg(numberField) by wrongField', [ + 'Unknown column [wrongField]', + ]); + testErrorsAndWarnings('from a | stats avg(numberField) by 1', [ + 'SyntaxError: expected {UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER} but found "1"', + 'Unknown column [1]', + ]); + testErrorsAndWarnings('from a | stats avg(numberField) by percentile(numberField)', [ + 'SyntaxError: expected {, PIPE, COMMA, DOT} but found "("', + 'Unknown column [percentile]', + ]); + + testErrorsAndWarnings( + 'from a | stats avg(numberField) by stringField, percentile(numberField) by ipField', + [ + 'SyntaxError: expected {, PIPE, COMMA, DOT} but found "("', + 'Unknown column [percentile]', + ] + ); + + testErrorsAndWarnings( + 'from a | stats avg(numberField), percentile(numberField, 50) by ipField', + [] + ); + + testErrorsAndWarnings( + 'from a | stats avg(numberField), percentile(numberField, 50) BY ipField', + [] + ); + + testErrorsAndWarnings('from a | stats numberField + 1', ['Stats does not support function +']); + + testErrorsAndWarnings('from a | stats numberField + 1 by ipField', [ + 'Stats does not support function +', + ]); + + testErrorsAndWarnings( + 'from a | stats avg(numberField), percentile(numberField, 50) + 1 by ipField', + ['Stats does not support function +'] + ); + + testErrorsAndWarnings('from a | stats avg(numberField) by avg(numberField)', [ + 'SyntaxError: expected {, PIPE, COMMA, DOT} but found "("', + 'Unknown column [avg]', + ]); + + testErrorsAndWarnings('from a | stats count(*)', []); + testErrorsAndWarnings('from a | stats var0 = count(*)', []); + testErrorsAndWarnings('from a | stats var0 = avg(numberField), count(*)', []); + + for (const { name, alias, signatures, ...defRest } of statsAggregationFunctionDefinitions) { + for (const { params, returnType } of signatures) { + const fieldMapping = getFieldMapping(params); + testErrorsAndWarnings( + `from a | stats var = ${ + getFunctionSignatures( + { name, ...defRest, signatures: [{ params: fieldMapping, returnType }] }, + { withTypes: false } + )[0].declaration + }` + ); + testErrorsAndWarnings( + `from a | stats ${ + getFunctionSignatures( + { name, ...defRest, signatures: [{ params: fieldMapping, returnType }] }, + { withTypes: false } + )[0].declaration + }` + ); + + if (alias) { + for (const otherName of alias) { + const signatureStringWithAlias = getFunctionSignatures( + { name: otherName, ...defRest, signatures: [{ params: fieldMapping, returnType }] }, + { withTypes: false } + )[0].declaration; + + testErrorsAndWarnings(`from a | stats var = ${signatureStringWithAlias}`, []); + } + } + + // Skip functions that have only arguments of type "any", as it is not possible to pass "the wrong type". + // auto_bucket and to_version functions are a bit harder to test exactly a combination of argument and predict the + // the right error message + if ( + params.every(({ type }) => type !== 'any') && + !['auto_bucket', 'to_version'].includes(name) + ) { + // now test nested functions + const fieldMappingWithNestedFunctions = getFieldMapping(params, { + useNestedFunction: true, + useLiterals: false, + }); + testErrorsAndWarnings( + `from a | stats var = ${ + getFunctionSignatures( + { + name, + ...defRest, + signatures: [{ params: fieldMappingWithNestedFunctions, returnType }], + }, + { withTypes: false } + )[0].declaration + }`, + params.map( + (_) => + `Aggregate function's parameters must be an attribute or literal; found [avg(numberField)] of type [number]` + ) + ); + // and the message is case of wrong argument type is passed + const wrongFieldMapping = params.map(({ name: _name, type, ...rest }) => { + const typeString = type; + const canBeFieldButNotString = ['number', 'date', 'boolean', 'ip'].includes(typeString); + const isLiteralType = /literal$/.test(typeString); + // pick a field name purposely wrong + const nameValue = + canBeFieldButNotString || isLiteralType ? 'stringField' : 'numberField'; + return { name: nameValue, type, ...rest }; + }); + + const expectedErrors = params.map( + ({ type }, i) => + `Argument of [${name}] must be [${type}], found value [${ + wrongFieldMapping[i].name + }] type [${wrongFieldMapping[i].name.replace('Field', '')}]` + ); + testErrorsAndWarnings( + `from a | stats ${ + getFunctionSignatures( + { name, ...defRest, signatures: [{ params: wrongFieldMapping, returnType }] }, + { withTypes: false } + )[0].declaration + }`, + expectedErrors + ); + + // test that only count() accepts wildcard as arg + // just check that the function accepts only 1 arg as the parser cannot handle multiple args with * as start arg + if (fieldMapping.length === 1) { + const fieldMappingWithWildcard = [...fieldMapping]; + fieldMappingWithWildcard[0].name = '*'; + + testErrorsAndWarnings( + `from a | stats var = ${ + getFunctionSignatures( + { + name, + ...defRest, + signatures: [{ params: fieldMappingWithWildcard, returnType }], + }, + { withTypes: false } + )[0].declaration + }`, + name === 'count' ? [] : [`Using wildcards (*) in ${name} is not allowed`] + ); + } + } + } + } + }); + + describe('sort', () => { + testErrorsAndWarnings('from a | sort ', [ + 'SyntaxError: expected {STRING, INTEGER_LITERAL, DECIMAL_LITERAL, FALSE, LP, NOT, NULL, PARAM, TRUE, PLUS, MINUS, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER} but found ""', + ]); + testErrorsAndWarnings('from a | sort "field" ', []); + testErrorsAndWarnings('from a | sort wrongField ', ['Unknown column [wrongField]']); + testErrorsAndWarnings('from a | sort numberField, ', [ + 'SyntaxError: expected {STRING, INTEGER_LITERAL, DECIMAL_LITERAL, FALSE, LP, NOT, NULL, PARAM, TRUE, PLUS, MINUS, OPENING_BRACKET, UNQUOTED_IDENTIFIER, QUOTED_IDENTIFIER} but found ""', + ]); + testErrorsAndWarnings('from a | sort numberField, stringField', []); + for (const dir of ['desc', 'asc']) { + testErrorsAndWarnings(`from a | sort "field" ${dir} `, []); + testErrorsAndWarnings(`from a | sort numberField ${dir} `, []); + testErrorsAndWarnings(`from a | sort numberField ${dir} nulls `, [ + "SyntaxError: missing {FIRST, LAST} at ''", + ]); + for (const nullDir of ['first', 'last']) { + testErrorsAndWarnings(`from a | sort numberField ${dir} nulls ${nullDir}`, []); + testErrorsAndWarnings(`from a | sort numberField ${dir} ${nullDir}`, [ + `SyntaxError: extraneous input '${nullDir}' expecting `, + ]); + } + } + for (const nullDir of ['first', 'last']) { + testErrorsAndWarnings(`from a | sort numberField nulls ${nullDir}`, []); + testErrorsAndWarnings(`from a | sort numberField ${nullDir}`, [ + `SyntaxError: extraneous input '${nullDir}' expecting `, + ]); + } + }); + + describe('enrich', () => { + testErrorsAndWarnings(`from a | enrich`, [ + "SyntaxError: missing {SRC_UNQUOTED_IDENTIFIER, SRC_QUOTED_IDENTIFIER} at ''", + ]); + testErrorsAndWarnings(`from a | enrich policy `, []); + testErrorsAndWarnings(`from a | enrich missing-policy `, ['Unknown policy [missing-policy]']); + testErrorsAndWarnings(`from a | enrich policy on `, [ + "SyntaxError: missing {SRC_UNQUOTED_IDENTIFIER, SRC_QUOTED_IDENTIFIER} at ''", + ]); + testErrorsAndWarnings(`from a | enrich policy on b `, ['Unknown column [b]']); + testErrorsAndWarnings(`from a | enrich policy on numberField with `, [ + 'SyntaxError: expected {SRC_UNQUOTED_IDENTIFIER, SRC_QUOTED_IDENTIFIER} but found ""', + ]); + testErrorsAndWarnings(`from a | enrich policy on numberField with var0 `, [ + 'Unknown column [var0]', + ]); + testErrorsAndWarnings(`from a | enrich policy on numberField with var0 = `, [ + "SyntaxError: missing {SRC_UNQUOTED_IDENTIFIER, SRC_QUOTED_IDENTIFIER} at ''", + 'Unknown column [var0]', + ]); + testErrorsAndWarnings(`from a | enrich policy on numberField with var0 = c `, [ + 'Unknown column [var0]', + `Unknown column [c]`, + ]); + // need to re-enable once the fields/variables become location aware + // testErrorsAndWarnings(`from a | enrich policy on numberField with var0 = stringField `, [ + // `Unknown column [stringField]`, + // ]); + testErrorsAndWarnings(`from a | enrich policy on numberField with var0 = , `, [ + "SyntaxError: missing {SRC_UNQUOTED_IDENTIFIER, SRC_QUOTED_IDENTIFIER} at ','", + 'SyntaxError: expected {SRC_UNQUOTED_IDENTIFIER, SRC_QUOTED_IDENTIFIER} but found ""', + 'Unknown column [var0]', + ]); + testErrorsAndWarnings(`from a | enrich policy on numberField with var0 = otherField, var1 `, [ + 'Unknown column [var1]', + ]); + testErrorsAndWarnings(`from a | enrich policy on numberField with var0 = otherField `, []); + testErrorsAndWarnings( + `from a | enrich policy on numberField with var0 = otherField, yetAnotherField `, + [] + ); + testErrorsAndWarnings(`from a | enrich policy on numberField with var0 = otherField, var1 = `, [ + "SyntaxError: missing {SRC_UNQUOTED_IDENTIFIER, SRC_QUOTED_IDENTIFIER} at ''", + 'Unknown column [var1]', + ]); + + testErrorsAndWarnings( + `from a | enrich policy on numberField with var0 = otherField, var1 = yetAnotherField`, + [] + ); + testErrorsAndWarnings(`from a | enrich policy with `, [ + 'SyntaxError: expected {SRC_UNQUOTED_IDENTIFIER, SRC_QUOTED_IDENTIFIER} but found ""', + ]); + testErrorsAndWarnings(`from a | enrich policy with otherField`, []); + testErrorsAndWarnings(`from a | enrich policy | eval otherField`, []); + testErrorsAndWarnings(`from a | enrich policy with var0 = otherField | eval var0`, []); + testErrorsAndWarnings('from a | enrich my-pol*', [ + 'Using wildcards (*) in enrich is not allowed [my-pol*]', + ]); + }); + + describe('shadowing', () => { + testErrorsAndWarnings( + 'from a | eval stringField = 5', + [], + ['Column [stringField] of type string has been overwritten as new type: number'] + ); + testErrorsAndWarnings( + 'from a | eval numberField = "5"', + [], + ['Column [numberField] of type number has been overwritten as new type: string'] + ); + }); + + describe('callbacks', () => { + it(`should not fetch source and fields list when a row command is set`, async () => { + const callbackMocks = getCallbackMocks(); + await validateAst(`row a = 1 | eval a`, getAstAndErrors, callbackMocks); + expect(callbackMocks.getFieldsFor).not.toHaveBeenCalled(); + expect(callbackMocks.getSources).not.toHaveBeenCalled(); + }); + + it(`should fetch policies if no enrich command is found`, async () => { + const callbackMocks = getCallbackMocks(); + await validateAst(`row a = 1 | eval a`, getAstAndErrors, callbackMocks); + expect(callbackMocks.getPolicies).not.toHaveBeenCalled(); + }); + + it(`should not fetch source and fields for empty command`, async () => { + const callbackMocks = getCallbackMocks(); + await validateAst(` `, getAstAndErrors, callbackMocks); + expect(callbackMocks.getFieldsFor).not.toHaveBeenCalled(); + expect(callbackMocks.getSources).not.toHaveBeenCalled(); + }); + + it(`should skip initial source and fields call but still call fields for enriched policy`, async () => { + const callbackMocks = getCallbackMocks(); + await validateAst(`row a = 1 | eval b = a | enrich policy`, getAstAndErrors, callbackMocks); + expect(callbackMocks.getSources).not.toHaveBeenCalled(); + expect(callbackMocks.getPolicies).toHaveBeenCalled(); + expect(callbackMocks.getFieldsFor).toHaveBeenCalledTimes(1); + expect(callbackMocks.getFieldsFor).toHaveBeenLastCalledWith({ + query: `from enrichIndex1 | keep otherField, yetAnotherField`, + }); + }); + + it('should call fields callbacks also for show command', async () => { + const callbackMocks = getCallbackMocks(); + await validateAst(`show functions | keep name`, getAstAndErrors, callbackMocks); + expect(callbackMocks.getSources).not.toHaveBeenCalled(); + expect(callbackMocks.getPolicies).not.toHaveBeenCalled(); + expect(callbackMocks.getFieldsFor).toHaveBeenCalledTimes(1); + expect(callbackMocks.getFieldsFor).toHaveBeenLastCalledWith({ + query: 'show functions', + }); + }); + + it(`should fetch additional fields if an enrich command is found`, async () => { + const callbackMocks = getCallbackMocks(); + await validateAst(`from a | eval b = a | enrich policy`, getAstAndErrors, callbackMocks); + expect(callbackMocks.getSources).toHaveBeenCalled(); + expect(callbackMocks.getPolicies).toHaveBeenCalled(); + expect(callbackMocks.getFieldsFor).toHaveBeenCalledTimes(2); + expect(callbackMocks.getFieldsFor).toHaveBeenLastCalledWith({ + query: `from enrichIndex1 | keep otherField, yetAnotherField`, + }); + }); + }); +}); diff --git a/packages/kbn-monaco/src/esql/lib/ast/validation/validation.ts b/packages/kbn-monaco/src/esql/lib/ast/validation/validation.ts new file mode 100644 index 0000000000000..b1a2a53be7acf --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/ast/validation/validation.ts @@ -0,0 +1,780 @@ +/* + * Copyright 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 uniqBy from 'lodash/uniqBy'; +import capitalize from 'lodash/capitalize'; +import { CommandOptionsDefinition, SignatureArgType } from '../definitions/types'; +import { + areFieldAndVariableTypesCompatible, + extractSingleType, + getAllArrayTypes, + getAllArrayValues, + getColumnHit, + getCommandDefinition, + getFunctionDefinition, + isArrayType, + isAssignment, + isColumnItem, + isEqualType, + isFunctionItem, + isLiteralItem, + isOptionItem, + isSourceItem, + isSupportedFunction, + isTimeIntervalItem, + inKnownTimeInterval, + printFunctionSignature, + sourceExists, + columnExists, + hasWildcard, + hasCCSSource, +} from '../shared/helpers'; +import { collectVariables } from '../shared/variables'; +import type { + AstProviderFn, + ESQLAstItem, + ESQLColumn, + ESQLCommand, + ESQLCommandOption, + ESQLFunction, + ESQLMessage, + ESQLSingleAstItem, + ESQLSource, +} from '../types'; +import { getMessageFromId, createMessage } from './errors'; +import type { ESQLRealField, ESQLVariable, ReferenceMaps, ValidationResult } from './types'; +import type { ESQLCallbacks } from '../shared/types'; +import { + retrieveSources, + retrieveFields, + retrievePolicies, + retrievePoliciesFields, +} from './resources'; + +function validateFunctionLiteralArg( + astFunction: ESQLFunction, + actualArg: ESQLAstItem, + argDef: SignatureArgType, + references: ReferenceMaps, + parentCommand: string +) { + const messages: ESQLMessage[] = []; + if (isLiteralItem(actualArg)) { + if (!isEqualType(actualArg, argDef, references, parentCommand)) { + messages.push( + getMessageFromId({ + messageId: 'wrongArgumentType', + values: { + name: astFunction.name, + argType: argDef.type, + value: actualArg.value, + givenType: actualArg.literalType, + }, + locations: actualArg.location, + }) + ); + } + } + if (isTimeIntervalItem(actualArg)) { + // check first if it's a valid interval string + if (!inKnownTimeInterval(actualArg)) { + messages.push( + getMessageFromId({ + messageId: 'unknownInterval', + values: { + value: actualArg.unit, + }, + locations: actualArg.location, + }) + ); + } else { + if (!isEqualType(actualArg, argDef, references, parentCommand)) { + messages.push( + getMessageFromId({ + messageId: 'wrongArgumentType', + values: { + name: astFunction.name, + argType: argDef.type, + value: actualArg.name, + givenType: 'duration', + }, + locations: actualArg.location, + }) + ); + } + } + } + return messages; +} + +function validateNestedFunctionArg( + astFunction: ESQLFunction, + actualArg: ESQLAstItem, + argDef: SignatureArgType, + references: ReferenceMaps, + parentCommand: string +) { + const messages: ESQLMessage[] = []; + if ( + isFunctionItem(actualArg) && + // no need to check the reason here, it is checked already above + isSupportedFunction(actualArg.name, parentCommand).supported + ) { + const argFn = getFunctionDefinition(actualArg.name)!; + if (!isEqualType(actualArg, argDef, references, parentCommand)) { + messages.push( + getMessageFromId({ + messageId: 'wrongArgumentType', + values: { + name: astFunction.name, + argType: argDef.type, + value: printFunctionSignature(actualArg) || actualArg.name, + givenType: argFn.signatures[0].returnType, + }, + locations: actualArg.location, + }) + ); + } else { + if ('noNestingFunctions' in argDef && argDef.noNestingFunctions) { + messages.push( + getMessageFromId({ + messageId: 'noNestedArgumentSupport', + values: { name: actualArg.text, argType: argFn.signatures[0].returnType }, + locations: actualArg.location, + }) + ); + } + } + } + return messages; +} + +function validateFunctionColumnArg( + astFunction: ESQLFunction, + actualArg: ESQLAstItem, + argDef: SignatureArgType, + references: ReferenceMaps, + parentCommand: string +) { + const messages: ESQLMessage[] = []; + if (isColumnItem(actualArg) && actualArg.name) { + const { hit: columnCheck, nameHit } = columnExists(actualArg, references); + if (!columnCheck) { + messages.push( + getMessageFromId({ + messageId: 'unknownColumn', + values: { + name: actualArg.name, + }, + locations: actualArg.location, + }) + ); + } else { + if (actualArg.name === '*') { + // if function does not support wildcards return a specific error + if (!('supportsWildcard' in argDef) || !argDef.supportsWildcard) { + messages.push( + getMessageFromId({ + messageId: 'noWildcardSupportAsArg', + values: { + name: astFunction.name, + }, + locations: actualArg.location, + }) + ); + } + // do not validate any further for now, only count() accepts wildcard as args... + } else { + // guaranteed by the check above + const columnHit = getColumnHit(nameHit!, references); + // check the type of the column hit + const typeHit = columnHit!.type; + if (!isEqualType(actualArg, argDef, references, parentCommand)) { + messages.push( + getMessageFromId({ + messageId: 'wrongArgumentType', + values: { + name: astFunction.name, + argType: argDef.type, + value: actualArg.name, + givenType: typeHit, + }, + locations: actualArg.location, + }) + ); + } + } + } + } + return messages; +} + +function validateFunction( + astFunction: ESQLFunction, + parentCommand: string, + references: ReferenceMaps +): ESQLMessage[] { + const messages: ESQLMessage[] = []; + + if (astFunction.incomplete) { + return messages; + } + + const isFnSupported = isSupportedFunction(astFunction.name, parentCommand); + + if (!isFnSupported.supported) { + if (isFnSupported.reason === 'unknownFunction') { + messages.push( + getMessageFromId({ + messageId: 'unknownFunction', + values: { + name: astFunction.name, + }, + locations: astFunction.location, + }) + ); + } + if (isFnSupported.reason === 'unsupportedFunction') { + messages.push( + getMessageFromId({ + messageId: 'unsupportedFunction', + values: { name: astFunction.name, command: capitalize(parentCommand) }, + locations: astFunction.location, + }) + ); + } + return messages; + } + const fnDefinition = getFunctionDefinition(astFunction.name)!; + const matchingSignatures = fnDefinition.signatures.filter((def) => { + if (def.infiniteParams && astFunction.args.length > 0) { + return true; + } + if (def.minParams && astFunction.args.length >= def.minParams) { + return true; + } + if (astFunction.args.length === def.params.length) { + return true; + } + return astFunction.args.length >= def.params.filter(({ optional }) => !optional).length; + }); + if (!matchingSignatures.length) { + const numArgs = fnDefinition.signatures[0].params.filter(({ optional }) => !optional).length; + messages.push( + getMessageFromId({ + messageId: 'wrongArgumentNumber', + values: { + fn: astFunction.name, + numArgs, + passedArgs: astFunction.args.length, + }, + locations: astFunction.location, + }) + ); + } + // now perform the same check on all functions args + for (const arg of astFunction.args) { + const wrappedArray = Array.isArray(arg) ? arg : [arg]; + for (const subArg of wrappedArray) { + if (isFunctionItem(subArg)) { + messages.push(...validateFunction(subArg, parentCommand, references)); + } + } + } + // check if the definition has some warning to show: + if (fnDefinition.warning) { + const message = fnDefinition.warning( + ...(astFunction.args.filter((arg) => !Array.isArray(arg)) as ESQLSingleAstItem[]) + ); + if (message) { + messages.push(createMessage('warning', message, astFunction.location)); + } + } + // at this point we're sure that at least one signature is matching + const failingSignatures: ESQLMessage[][] = []; + for (const signature of matchingSignatures) { + const failingSignature: ESQLMessage[] = []; + signature.params.forEach((argDef, index) => { + const outerArg = astFunction.args[index]!; + if (!outerArg && argDef.optional) { + // that's ok, just skip it + // the else case is already catched with the argument counts check + // few lines above + return; + } + if (Array.isArray(outerArg) && isArrayType(argDef.type)) { + const extractedType = extractSingleType(argDef.type); + const everyArgInListMessages = outerArg + .map((arg) => { + return [ + validateFunctionLiteralArg, + validateNestedFunctionArg, + validateFunctionColumnArg, + ].flatMap((validateFn) => { + return validateFn( + astFunction, + arg, + { ...argDef, type: extractedType }, + references, + parentCommand + ); + }); + }) + .filter((ms) => ms.length); + if (everyArgInListMessages.length) { + failingSignature.push( + getMessageFromId({ + messageId: 'wrongArgumentType', + values: { + name: astFunction.name, + argType: argDef.type, + value: `(${getAllArrayValues(outerArg).join(', ')})`, + givenType: `(${getAllArrayTypes(outerArg, parentCommand, references).join(', ')})`, + }, + locations: { + min: (outerArg[0] as ESQLSingleAstItem).location.min, + max: (outerArg[outerArg.length - 1] as ESQLSingleAstItem).location.max, + }, + }) + ); + } + return; + } + const wrappedArg = Array.isArray(outerArg) ? outerArg : [outerArg]; + for (const actualArg of wrappedArg) { + const argValidationMessages = [ + validateFunctionLiteralArg, + validateNestedFunctionArg, + validateFunctionColumnArg, + ].flatMap((validateFn) => { + return validateFn(astFunction, actualArg, argDef, references, parentCommand); + }); + failingSignature.push(...argValidationMessages); + + if (isSourceItem(actualArg)) { + // something went wrong with the AST translation + throw new Error('Source should not allowed as function argument'); + } + } + }); + if (failingSignature.length) { + failingSignatures.push(failingSignature); + } + } + if (failingSignatures.length && failingSignatures.length === matchingSignatures.length) { + const failingSignatureOrderedByErrorCount = failingSignatures + .map((arr, index) => ({ index, count: arr.length })) + .sort((a, b) => a.count - b.count); + const indexForShortestFailingsignature = failingSignatureOrderedByErrorCount[0].index; + messages.push(...failingSignatures[indexForShortestFailingsignature]); + } + // This is due to a special case in enrich where an implicit assignment is possible + // so the AST needs to store an explicit "columnX = columnX" which duplicates the message + return uniqBy(messages, ({ location }) => `${location.min}-${location.max}`); +} + +function validateOption( + option: ESQLCommandOption, + optionDef: CommandOptionsDefinition | undefined, + command: ESQLCommand, + referenceMaps: ReferenceMaps +): ESQLMessage[] { + // check if the arguments of the option are of the correct type + const messages: ESQLMessage[] = []; + if (option.incomplete || command.incomplete) { + return messages; + } + if (!optionDef) { + messages.push( + getMessageFromId({ + messageId: 'unknownOption', + values: { command: command.name, option: option.name }, + locations: option.location, + }) + ); + return messages; + } + // use dedicate validate fn if provided + if (optionDef.validate) { + messages.push(...optionDef.validate(option)); + } + if (!optionDef.skipCommonValidation) { + option.args.forEach((arg, index) => { + if (!Array.isArray(arg)) { + if (!optionDef.signature.multipleParams) { + const argDef = optionDef.signature.params[index]; + if (!isEqualType(arg, argDef, referenceMaps, command.name)) { + const value = 'value' in arg ? arg.value : arg.name; + messages.push( + getMessageFromId({ + messageId: 'wrongArgumentType', + values: { + name: option.name, + argType: argDef.type, + value, + givenType: arg.type, + }, + locations: arg.location, + }) + ); + } + if (isColumnItem(arg)) { + messages.push(...validateColumnForCommand(arg, command.name, referenceMaps)); + } + } else { + const argDef = optionDef.signature.params[0]; + if (!isEqualType(arg, argDef, referenceMaps, command.name)) { + const value = 'value' in arg ? arg.value : arg.name; + messages.push( + getMessageFromId({ + messageId: 'wrongArgumentType', + values: { + name: argDef.name, + argType: argDef.type, + value, + givenType: arg.type, + }, + locations: arg.location, + }) + ); + } + if (isColumnItem(arg)) { + messages.push(...validateColumnForCommand(arg, command.name, referenceMaps)); + } + if (isFunctionItem(arg) && isAssignment(arg)) { + messages.push(...validateFunction(arg, command.name, referenceMaps)); + } + } + } + }); + } + + return messages; +} + +function validateSource( + source: ESQLSource, + commandName: string, + { sources, policies }: ReferenceMaps +) { + const messages: ESQLMessage[] = []; + if (source.incomplete) { + return messages; + } + const commandDef = getCommandDefinition(commandName); + if (commandDef.signature.params.every(({ type }) => type !== source.type)) { + const firstArg = commandDef.signature.params[0]; + messages.push( + getMessageFromId({ + messageId: 'wrongArgumentType', + values: { + name: firstArg.name, + argType: firstArg.type, + value: source.name, + givenType: source.type, + }, + locations: source.location, + }) + ); + } else { + const hasCCS = hasCCSSource(source.name); + if (hasCCS) { + messages.push( + getMessageFromId({ + messageId: 'ccsNotSupportedForCommand', + values: { value: source.name }, + locations: source.location, + }) + ); + } else { + const isWildcardAndNotSupported = + hasWildcard(source.name) && !commandDef.signature.params.some(({ wildcards }) => wildcards); + if (isWildcardAndNotSupported) { + messages.push( + getMessageFromId({ + messageId: 'wildcardNotSupportedForCommand', + values: { command: commandName, value: source.name }, + locations: source.location, + }) + ); + } else { + if (source.sourceType === 'index' && !sourceExists(source.name, sources)) { + messages.push( + getMessageFromId({ + messageId: 'unknownIndex', + values: { name: source.name }, + locations: source.location, + }) + ); + } else if (source.sourceType === 'policy' && !policies.has(source.name)) { + messages.push( + getMessageFromId({ + messageId: 'unknownPolicy', + values: { name: source.name }, + locations: source.location, + }) + ); + } + } + } + } + return messages; +} + +function validateColumnForCommand( + column: ESQLColumn, + commandName: string, + references: ReferenceMaps +): ESQLMessage[] { + const messages: ESQLMessage[] = []; + + if (['from', 'show', 'limit'].includes(commandName)) { + return messages; + } + if (commandName === 'row') { + if (!references.variables.has(column.name)) { + messages.push( + getMessageFromId({ + messageId: 'unknownColumn', + values: { + name: column.name, + }, + locations: column.location, + }) + ); + } + } else { + const { hit: columnCheck, nameHit } = columnExists(column, references); + if (columnCheck && nameHit) { + const commandDef = getCommandDefinition(commandName); + const columnParamsWithInnerTypes = commandDef.signature.params.filter( + ({ type, innerType }) => type === 'column' && innerType + ); + + if (columnParamsWithInnerTypes.length) { + // this should be guaranteed by the columnCheck above + const columnRef = getColumnHit(nameHit, references)!; + if ( + columnParamsWithInnerTypes.every(({ innerType }) => { + return innerType !== columnRef.type; + }) + ) { + const supportedTypes = columnParamsWithInnerTypes.map(({ innerType }) => innerType); + + messages.push( + getMessageFromId({ + messageId: 'unsupportedColumnTypeForCommand', + values: { + command: capitalize(commandName), + type: supportedTypes.join(', '), + typeCount: supportedTypes.length, + givenType: columnRef.type, + column: nameHit, + }, + locations: column.location, + }) + ); + } + } + if ( + hasWildcard(nameHit) && + !commandDef.signature.params.some(({ type, wildcards }) => type === 'column' && wildcards) + ) { + messages.push( + getMessageFromId({ + messageId: 'wildcardNotSupportedForCommand', + values: { + command: commandName, + value: nameHit, + }, + locations: column.location, + }) + ); + } + } else { + if (column.name) { + messages.push( + getMessageFromId({ + messageId: 'unknownColumn', + values: { + name: column.name, + }, + locations: column.location, + }) + ); + } + } + } + return messages; +} + +function validateCommand(command: ESQLCommand, references: ReferenceMaps): ESQLMessage[] { + const messages: ESQLMessage[] = []; + if (command.incomplete) { + return messages; + } + // do not check the command exists, the grammar is already picking that up + const commandDef = getCommandDefinition(command.name); + + if (commandDef.validate) { + messages.push(...commandDef.validate(command)); + } + + // Now validate arguments + for (const commandArg of command.args) { + const wrappedArg = Array.isArray(commandArg) ? commandArg : [commandArg]; + for (const arg of wrappedArg) { + if (isFunctionItem(arg)) { + messages.push(...validateFunction(arg, command.name, references)); + } + + if (isOptionItem(arg)) { + messages.push( + ...validateOption( + arg, + commandDef.options.find(({ name }) => name === arg.name), + command, + references + ) + ); + } + if (isColumnItem(arg)) { + if (command.name === 'stats') { + messages.push( + getMessageFromId({ + messageId: 'unknownAggregateFunction', + values: { + command: capitalize(command.name), + value: (arg as ESQLSingleAstItem).name, + }, + locations: (arg as ESQLSingleAstItem).location, + }) + ); + } else { + messages.push(...validateColumnForCommand(arg, command.name, references)); + } + } + if (isTimeIntervalItem(arg)) { + messages.push( + getMessageFromId({ + messageId: 'unsupportedTypeForCommand', + values: { + command: capitalize(command.name), + type: 'date_period', + value: arg.name, + }, + locations: arg.location, + }) + ); + } + if (isSourceItem(arg)) { + messages.push(...validateSource(arg, command.name, references)); + } + } + } + // no need to check for mandatory options passed + // as they are already validated at syntax level + return messages; +} + +function validateFieldsShadowing( + fields: Map, + variables: Map +) { + const messages: ESQLMessage[] = []; + for (const variable of variables.keys()) { + if (fields.has(variable)) { + const variableHits = variables.get(variable)!; + if (!areFieldAndVariableTypesCompatible(fields.get(variable)?.type, variableHits[0].type)) { + const fieldType = fields.get(variable)!.type; + const variableType = variableHits[0].type; + const flatFieldType = fieldType; + const flatVariableType = variableType; + messages.push( + getMessageFromId({ + messageId: 'shadowFieldType', + values: { + field: variable, + fieldType: flatFieldType, + newType: flatVariableType, + }, + locations: variableHits[0].location, + }) + ); + } + } + } + return messages; +} + +function validateUnsupportedTypeFields(fields: Map) { + const messages: ESQLMessage[] = []; + for (const field of fields.values()) { + if (field.type === 'unsupported') { + messages.push( + getMessageFromId({ + messageId: 'unsupportedFieldType', + values: { + field: field.name, + }, + locations: { min: 1, max: 1 }, + }) + ); + } + } + return messages; +} + +/** + * This function will perform an high level validation of the + * query AST. An initial syntax validation is already performed by the parser + * while here it can detect things like function names, types correctness and potential warnings + * @param ast A valid AST data structure + */ +export async function validateAst( + queryString: string, + astProvider: AstProviderFn, + callbacks?: ESQLCallbacks +): Promise { + const messages: ESQLMessage[] = []; + + const { ast, errors } = await astProvider(queryString); + + const [sources, availableFields, availablePolicies] = await Promise.all([ + // retrieve the list of available sources + retrieveSources(ast, callbacks), + // retrieve available fields (if a source command has been defined) + retrieveFields(queryString, ast, callbacks), + // retrieve available policies (if an enrich command has been defined) + retrievePolicies(ast, callbacks), + ]); + + if (availablePolicies.size && ast.filter(({ name }) => name === 'enrich')) { + const fieldsFromPoliciesMap = await retrievePoliciesFields(ast, availablePolicies, callbacks); + fieldsFromPoliciesMap.forEach((value, key) => availableFields.set(key, value)); + } + + const variables = collectVariables(ast, availableFields); + // notify if the user is rewriting a column as variable with another type + messages.push(...validateFieldsShadowing(availableFields, variables)); + messages.push(...validateUnsupportedTypeFields(availableFields)); + + for (const command of ast) { + const commandMessages = validateCommand(command, { + sources, + fields: availableFields, + policies: availablePolicies, + variables, + }); + messages.push(...commandMessages); + } + return { + errors: [...errors, ...messages.filter(({ type }) => type === 'error')], + warnings: messages.filter(({ type }) => type === 'warning'), + }; +} diff --git a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/comparison_commands.ts b/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/comparison_commands.ts deleted file mode 100644 index 92b2e8f7c31d1..0000000000000 --- a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/comparison_commands.ts +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 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 type { AutocompleteCommandDefinition } from '../types'; - -export const comparisonOperatorsCommandsDefinitions: AutocompleteCommandDefinition[] = [ - { - label: 'or', - insertText: 'or', - kind: 11, - detail: i18n.translate('monaco.esql.autocomplete.orDoc', { - defaultMessage: 'or', - }), - sortText: 'D', - }, - { - label: 'and', - insertText: 'and', - kind: 11, - detail: i18n.translate('monaco.esql.autocomplete.andDoc', { - defaultMessage: 'and', - }), - sortText: 'D', - }, -]; - -export const comparisonCommandsDefinitions: AutocompleteCommandDefinition[] = [ - { - label: '==', - insertText: '==', - kind: 11, - detail: i18n.translate('monaco.esql.autocomplete.equalToDoc', { - defaultMessage: 'Equal to', - }), - sortText: 'D', - }, - { - label: '!=', - insertText: '!=', - kind: 11, - detail: i18n.translate('monaco.esql.autocomplete.notEqualToDoc', { - defaultMessage: 'Not equal to', - }), - sortText: 'D', - }, - { - label: '<', - insertText: '<', - kind: 11, - detail: i18n.translate('monaco.esql.autocomplete.lessThanDoc', { - defaultMessage: 'Less than', - }), - sortText: 'D', - }, - { - label: '>', - insertText: '>', - kind: 11, - detail: i18n.translate('monaco.esql.autocomplete.greaterThanDoc', { - defaultMessage: 'Greater than', - }), - sortText: 'D', - }, - { - label: '<=', - insertText: '<=', - kind: 11, - detail: i18n.translate('monaco.esql.autocomplete.lessThanOrEqualToDoc', { - defaultMessage: 'Less than or equal to', - }), - sortText: 'D', - }, - { - label: '>=', - insertText: '>=', - kind: 11, - detail: i18n.translate('monaco.esql.autocomplete.greaterThanOrEqualToDoc', { - defaultMessage: 'Greater than or equal to', - }), - sortText: 'D', - }, - { - label: 'like', - insertText: 'like', - kind: 11, - detail: i18n.translate('monaco.esql.autocomplete.likeDoc', { - defaultMessage: 'Filter data based on string patterns', - }), - sortText: 'D', - }, - { - label: 'rlike', - insertText: 'rlike', - kind: 11, - detail: i18n.translate('monaco.esql.autocomplete.rlikeDoc', { - defaultMessage: 'Filter data based on string regular expressions', - }), - sortText: 'D', - }, - { - label: 'in', - insertText: 'in', - kind: 11, - detail: i18n.translate('monaco.esql.autocomplete.inDoc', { - defaultMessage: - 'Tests if the value an expression takes is contained in a list of other expressions', - }), - sortText: 'D', - }, -]; diff --git a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/date_math_expressions.ts b/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/date_math_expressions.ts deleted file mode 100644 index 2eb0226914ee9..0000000000000 --- a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/date_math_expressions.ts +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { i18n } from '@kbn/i18n'; -import type { AutocompleteCommandDefinition } from '../types'; - -export const dateExpressionDefinitions: AutocompleteCommandDefinition[] = [ - { - label: 'year', - insertText: 'year', - kind: 12, - detail: i18n.translate('monaco.esql.autocomplete.dateDurationDefinition.year', { - defaultMessage: 'Year', - }), - sortText: 'D', - }, - { - label: 'years', - insertText: 'years', - kind: 12, - detail: i18n.translate('monaco.esql.autocomplete.dateDurationDefinition.years', { - defaultMessage: 'Years (Plural)', - }), - sortText: 'D', - }, - { - label: 'month', - insertText: 'month', - kind: 12, - detail: i18n.translate('monaco.esql.autocomplete.dateDurationDefinition.month', { - defaultMessage: 'Month', - }), - sortText: 'D', - }, - { - label: 'months', - insertText: 'months', - kind: 12, - detail: i18n.translate('monaco.esql.autocomplete.dateDurationDefinition.months', { - defaultMessage: 'Months (Plural)', - }), - sortText: 'D', - }, - { - label: 'week', - insertText: 'week', - kind: 12, - detail: i18n.translate('monaco.esql.autocomplete.dateDurationDefinition.week', { - defaultMessage: 'Week', - }), - sortText: 'D', - }, - { - label: 'weeks', - insertText: 'weeks', - kind: 12, - detail: i18n.translate('monaco.esql.autocomplete.dateDurationDefinition.weeks', { - defaultMessage: 'Weeks (Plural)', - }), - sortText: 'D', - }, - { - label: 'day', - insertText: 'day', - kind: 12, - detail: i18n.translate('monaco.esql.autocomplete.dateDurationDefinition.day', { - defaultMessage: 'Day', - }), - sortText: 'D', - }, - { - label: 'days', - insertText: 'days', - kind: 12, - detail: i18n.translate('monaco.esql.autocomplete.dateDurationDefinition.days', { - defaultMessage: 'Days (Plural)', - }), - sortText: 'D', - }, - { - label: 'hour', - insertText: 'hour', - kind: 12, - detail: i18n.translate('monaco.esql.autocomplete.dateDurationDefinition.hour', { - defaultMessage: 'Hour', - }), - sortText: 'D', - }, - { - label: 'hours', - insertText: 'hours', - kind: 12, - detail: i18n.translate('monaco.esql.autocomplete.dateDurationDefinition.hours', { - defaultMessage: 'Hours (Plural)', - }), - sortText: 'D', - }, - { - label: 'minute', - insertText: 'minute', - kind: 12, - detail: i18n.translate('monaco.esql.autocomplete.dateDurationDefinition.minute', { - defaultMessage: 'Minute', - }), - sortText: 'D', - }, - { - label: 'minutes', - insertText: 'minutes', - kind: 12, - detail: i18n.translate('monaco.esql.autocomplete.dateDurationDefinition.minutes', { - defaultMessage: 'Minutes (Plural)', - }), - sortText: 'D', - }, - { - label: 'second', - insertText: 'second', - kind: 12, - detail: i18n.translate('monaco.esql.autocomplete.dateDurationDefinition.second', { - defaultMessage: 'Second', - }), - sortText: 'D', - }, - { - label: 'seconds', - insertText: 'seconds', - kind: 12, - detail: i18n.translate('monaco.esql.autocomplete.dateDurationDefinition.seconds', { - defaultMessage: 'Seconds (Plural)', - }), - sortText: 'D', - }, - { - label: 'millisecond', - insertText: 'millisecond', - kind: 12, - detail: i18n.translate('monaco.esql.autocomplete.dateDurationDefinition.millisecond', { - defaultMessage: 'Millisecond', - }), - sortText: 'D', - }, - { - label: 'milliseconds', - insertText: 'milliseconds', - kind: 12, - detail: i18n.translate('monaco.esql.autocomplete.dateDurationDefinition.milliseconds', { - defaultMessage: 'Milliseconds (Plural)', - }), - sortText: 'D', - }, -]; diff --git a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/dynamic_commands.ts b/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/dynamic_commands.ts deleted file mode 100644 index f6348fe2c11e2..0000000000000 --- a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/dynamic_commands.ts +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { i18n } from '@kbn/i18n'; -import type { AutocompleteCommandDefinition } from '../types'; - -export const buildPoliciesDefinitions = ( - policies: Array<{ name: string; indices: string[] }> -): AutocompleteCommandDefinition[] => - policies.map(({ name: label, indices }) => ({ - label, - insertText: label, - kind: 5, - detail: i18n.translate('monaco.esql.autocomplete.policyDefinition', { - defaultMessage: `Policy defined on {count, plural, one {index} other {indices}}: {indices}`, - values: { - count: indices.length, - indices: indices.join(', '), - }, - }), - sortText: 'D', - })); - -export const buildFieldsDefinitions = (fields: string[]): AutocompleteCommandDefinition[] => - fields.map((label) => ({ - label, - insertText: label, - kind: 4, - detail: i18n.translate('monaco.esql.autocomplete.fieldDefinition', { - defaultMessage: `Field specified by the input table`, - }), - sortText: 'D', - })); - -export const buildNoPoliciesAvailableDefinition = (): AutocompleteCommandDefinition[] => [ - { - label: i18n.translate('monaco.esql.autocomplete.noPoliciesLabel', { - defaultMessage: 'No available policy', - }), - insertText: '', - kind: 26, - detail: i18n.translate('monaco.esql.autocomplete.noPoliciesLabelsFound', { - defaultMessage: 'Click to create', - }), - sortText: 'D', - command: { - id: 'esql.policies.create', - title: i18n.translate('monaco.esql.autocomplete.createNewPolicy', { - defaultMessage: 'Click to create', - }), - }, - }, -]; - -export const buildMatchingFieldsDefinition = ( - matchingField: string, - fields: string[] -): AutocompleteCommandDefinition[] => - fields.map((label) => ({ - label, - insertText: label, - kind: 4, - detail: i18n.translate('monaco.esql.autocomplete.matchingFieldDefinition', { - defaultMessage: `Use to match on {matchingField} on the policy`, - values: { - matchingField, - }, - }), - sortText: 'D', - })); - -export const buildNewVarDefinition = (label: string): AutocompleteCommandDefinition => { - return { - label, - insertText: label, - kind: 21, - detail: i18n.translate('monaco.esql.autocomplete.newVarDoc', { - defaultMessage: 'Define a new variable', - }), - sortText: 'D', - }; -}; - -export const buildSourcesDefinitions = (sources: string[]): AutocompleteCommandDefinition[] => - sources.map((label) => ({ - label, - insertText: label, - kind: 21, - detail: i18n.translate('monaco.esql.autocomplete.sourceDefinition', { - defaultMessage: `Input table`, - }), - sortText: 'A', - })); - -export const buildConstantsDefinitions = ( - userConstants: string[], - detail?: string -): AutocompleteCommandDefinition[] => - userConstants.map((label) => ({ - label, - insertText: label, - kind: 14, - detail: - detail ?? - i18n.translate('monaco.esql.autocomplete.constantDefinition', { - defaultMessage: `User defined variable`, - }), - sortText: 'A', - })); diff --git a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/index.ts b/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/index.ts deleted file mode 100644 index e1fb514cfa4de..0000000000000 --- a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/index.ts +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 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 { - aggregationFunctionsDefinitions, - mathCommandDefinition, - whereCommandDefinition, -} from './functions_commands'; -export { sourceCommandsDefinitions } from './source_commands'; -export { processingCommandsDefinitions, pipeDefinition } from './processing_commands'; - -export { - comparisonCommandsDefinitions, - comparisonOperatorsCommandsDefinitions, -} from './comparison_commands'; -export { - mathOperatorsCommandsDefinitions, - assignOperatorDefinition, - asOperatorDefinition, - byOperatorDefinition, - openBracketDefinition, - closeBracketDefinition, -} from './operators_commands'; - -export { - orderingCommandsDefinitions, - nullsCommandsDefinition, - nullsOrderingCommandsDefinitions, -} from './ordering_commands'; - -export { - buildNewVarDefinition, - buildSourcesDefinitions, - buildFieldsDefinitions, - buildConstantsDefinitions, -} from './dynamic_commands'; diff --git a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/operators_commands.ts b/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/operators_commands.ts deleted file mode 100644 index 91ccb74cb9501..0000000000000 --- a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/operators_commands.ts +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { i18n } from '@kbn/i18n'; -import type { AutocompleteCommandDefinition } from '../types'; - -export const byOperatorDefinition: AutocompleteCommandDefinition = { - label: 'by', - insertText: 'by', - kind: 21, - detail: i18n.translate('monaco.esql.autocomplete.byDoc', { - defaultMessage: 'By', - }), - sortText: 'D', -}; - -export const onOperatorDefinition: AutocompleteCommandDefinition = { - label: 'on', - insertText: 'on', - kind: 21, - detail: i18n.translate('monaco.esql.autocomplete.onDoc', { - defaultMessage: 'On', - }), - sortText: 'D', -}; - -export const withOperatorDefinition: AutocompleteCommandDefinition = { - label: 'with', - insertText: 'with', - kind: 21, - detail: i18n.translate('monaco.esql.autocomplete.withDoc', { - defaultMessage: 'With', - }), - sortText: 'D', -}; - -export const asOperatorDefinition: AutocompleteCommandDefinition = { - label: 'as', - insertText: 'as', - kind: 11, - detail: i18n.translate('monaco.esql.autocomplete.asDoc', { - defaultMessage: 'As', - }), - sortText: 'D', -}; - -export const assignOperatorDefinition: AutocompleteCommandDefinition = { - label: '=', - insertText: '=', - kind: 11, - detail: i18n.translate('monaco.esql.autocomplete.assignDoc', { - defaultMessage: 'Assign (=)', - }), - sortText: 'D', -}; - -export const openBracketDefinition: AutocompleteCommandDefinition = { - label: '(', - insertText: '(', - kind: 11, - detail: i18n.translate('monaco.esql.autocomplete.openBracketDoc', { - defaultMessage: 'Open Bracket (', - }), - sortText: 'A', -}; - -export const closeBracketDefinition: AutocompleteCommandDefinition = { - label: ')', - insertText: ')', - kind: 11, - detail: i18n.translate('monaco.esql.autocomplete.closeBracketDoc', { - defaultMessage: 'Close Bracket )', - }), - sortText: 'A', -}; - -export const mathOperatorsCommandsDefinitions: AutocompleteCommandDefinition[] = [ - { - label: '+', - insertText: '+', - kind: 11, - detail: i18n.translate('monaco.esql.autocomplete.addDoc', { - defaultMessage: 'Add (+)', - }), - sortText: 'D', - }, - { - label: '-', - insertText: '-', - kind: 11, - detail: i18n.translate('monaco.esql.autocomplete.subtractDoc', { - defaultMessage: 'Subtract (-)', - }), - sortText: 'D', - }, - { - label: '/', - insertText: '/', - kind: 11, - detail: i18n.translate('monaco.esql.autocomplete.divideDoc', { - defaultMessage: 'Divide (/)', - }), - sortText: 'D', - }, - { - label: '*', - insertText: '*', - kind: 11, - detail: i18n.translate('monaco.esql.autocomplete.multiplyDoc', { - defaultMessage: 'Multiply (*)', - }), - sortText: 'D', - }, -]; diff --git a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/ordering_commands.ts b/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/ordering_commands.ts deleted file mode 100644 index 6e932e742a69b..0000000000000 --- a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/ordering_commands.ts +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 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 type { AutocompleteCommandDefinition } from '../types'; - -export const orderingCommandsDefinitions: AutocompleteCommandDefinition[] = [ - { - label: 'asc', - insertText: 'asc', - kind: 17, - detail: i18n.translate('monaco.esql.autocomplete.ascDoc', { - defaultMessage: 'Ascending Order', - }), - sortText: 'D', - }, - { - label: 'desc', - insertText: 'desc', - kind: 17, - detail: i18n.translate('monaco.esql.autocomplete.descDoc', { - defaultMessage: 'Descending Order', - }), - sortText: 'D', - }, -]; - -export const nullsCommandsDefinition: AutocompleteCommandDefinition = { - label: 'nulls', - insertText: 'nulls', - kind: 13, - sortText: 'D', -}; - -export const nullsOrderingCommandsDefinitions: AutocompleteCommandDefinition[] = [ - { - label: 'first', - insertText: 'first', - kind: 13, - sortText: 'D', - }, - { - label: 'last', - insertText: 'last', - kind: 13, - sortText: 'D', - }, -]; diff --git a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/processing_commands.ts b/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/processing_commands.ts deleted file mode 100644 index 5fe6969f3eddb..0000000000000 --- a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/processing_commands.ts +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { i18n } from '@kbn/i18n'; -import { buildDocumentation } from './utils'; - -import type { AutocompleteCommandDefinition } from '../types'; - -export const pipeDefinition: AutocompleteCommandDefinition = { - label: '|', - insertText: '|', - kind: 1, - detail: i18n.translate('monaco.esql.autocomplete.pipeDoc', { - defaultMessage: 'Pipe (|)', - }), - sortText: 'B', -}; - -export const processingCommandsDefinitions: AutocompleteCommandDefinition[] = [ - { - label: 'stats', - insertText: 'stats', - kind: 1, - detail: i18n.translate('monaco.esql.autocomplete.statsDoc', { - defaultMessage: - 'Calculates aggregate statistics, such as average, count, and sum, over the incoming search results set. Similar to SQL aggregation, if the stats command is used without a BY clause, only one row is returned, which is the aggregation over the entire incoming search results set. When you use a BY clause, one row is returned for each distinct value in the field specified in the BY clause. The stats command returns only the fields in the aggregation, and you can use a wide range of statistical functions with the stats command. When you perform more than one aggregation, separate each aggregation with a comma.', - }), - documentation: { - value: buildDocumentation( - 'stats aggs = fieldSpecification ( `,` fieldSpecification )* ( `by` groups = identifier ( `,` identifier )* )?', - ['… | stats sum(b) by b)', '… | stats avg = avg(a)'] - ), - }, - sortText: 'B', - }, - { - label: 'limit', - insertText: 'limit', - kind: 1, - detail: i18n.translate('monaco.esql.autocomplete.limitDoc', { - defaultMessage: - 'Returns the first search results, in search order, based on the "limit" specified.', - }), - documentation: { - value: buildDocumentation('limit size = integerLiteral', ['… | limit 100', '… | limit 0']), - }, - sortText: 'B', - }, - { - label: 'eval', - insertText: 'eval', - kind: 1, - detail: i18n.translate('monaco.esql.autocomplete.evalDoc', { - defaultMessage: - 'Calculates an expression and puts the resulting value into a search results field.', - }), - documentation: { - value: buildDocumentation('eval columns = fieldSpecification ( `,` fieldSpecification )*', [ - '… | eval a = b * c', - '… | eval then = 1 year + 2 weeks', - ]), - }, - sortText: 'B', - }, - { - label: 'keep', - insertText: 'keep', - kind: 1, - detail: i18n.translate('monaco.esql.autocomplete.keepDoc', { - defaultMessage: 'Rearranges fields in the input table by applying the keep clauses in fields', - }), - documentation: { - value: buildDocumentation('keep fieldSpecification `,` fieldSpecification *', [ - '… | keep a,b', - ]), - }, - sortText: 'B', - }, - { - label: 'rename', - insertText: 'rename', - kind: 1, - detail: i18n.translate('monaco.esql.autocomplete.renameDoc', { - defaultMessage: 'Renames an old column to a new one', - }), - documentation: { - value: buildDocumentation('rename new as old', ['… | rename a as b']), - }, - sortText: 'B', - }, - { - label: 'drop', - insertText: 'drop', - kind: 1, - detail: i18n.translate('monaco.esql.autocomplete.dropDoc', { - defaultMessage: 'Drops columns', - }), - documentation: { - value: buildDocumentation('drop fieldSpecification `,` fieldSpecification *', [ - '… | drop a,b', - ]), - }, - sortText: 'B', - }, - { - label: 'sort', - insertText: 'sort', - kind: 1, - detail: i18n.translate('monaco.esql.autocomplete.sortDoc', { - defaultMessage: - 'Sorts all results by the specified fields. When in descending order, the results missing a field are considered the smallest possible value of the field, or the largest possible value of the field when in ascending order.', - }), - documentation: { - value: buildDocumentation('sort orders = orderExpression ( `,` orderExpression )*', [ - '… | sort a desc, b nulls last, c asc nulls first', - '… | sort b nulls last`', - '… | sort c asc nulls first`', - ]), - }, - sortText: 'B', - }, - { - label: 'where', - insertText: 'where', - kind: 1, - detail: i18n.translate('monaco.esql.autocomplete.whereDoc', { - defaultMessage: - 'Uses "predicate-expressions" to filter search results. A predicate expression, when evaluated, returns TRUE or FALSE. The where command only returns the results that evaluate to TRUE. For example, to filter results for a specific field value', - }), - documentation: { - value: buildDocumentation('where condition = expression', ['… | where status_code == 200']), - }, - sortText: 'B', - }, - { - label: 'dissect', - insertText: 'dissect', - kind: 1, - detail: i18n.translate('monaco.esql.autocomplete.dissectDoc', { - defaultMessage: - 'Extracts multiple string values from a single string input, based on a pattern', - }), - documentation: { - value: buildDocumentation( - 'dissect (append_separator=)?', - ['… | dissect a "%{b} %{c}";'] - ), - }, - sortText: 'B', - }, - { - label: 'grok', - insertText: 'grok', - kind: 1, - detail: i18n.translate('monaco.esql.autocomplete.grokDoc', { - defaultMessage: - 'Extracts multiple string values from a single string input, based on a pattern', - }), - documentation: { - value: buildDocumentation('grok ', [ - '… | grok a "%{b} %{c}";', - ]), - }, - sortText: 'B', - }, - { - label: 'mv_expand', - insertText: 'mv_expand', - kind: 1, - detail: i18n.translate('monaco.esql.autocomplete.mvExpandDoc', { - defaultMessage: 'Expands multivalued fields into one row per value, duplicating other fields', - }), - documentation: { - value: buildDocumentation('mv_expand field', [ - 'ROW a=[1,2,3], b="b", j=["a","b"] | MV_EXPAND a', - ]), - }, - sortText: 'B', - }, - { - label: 'enrich', - insertText: 'enrich', - kind: 1, - detail: i18n.translate('monaco.esql.autocomplete.enrichDoc', { - defaultMessage: 'Enrich table with another table', - }), - documentation: { - value: buildDocumentation('enrich policy', ['... | ENRICH a']), - }, - sortText: 'B', - }, -]; diff --git a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/source_commands.ts b/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/source_commands.ts deleted file mode 100644 index a14f776de1bbf..0000000000000 --- a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_definitions/source_commands.ts +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -import { i18n } from '@kbn/i18n'; -import { buildDocumentation } from './utils'; - -import type { AutocompleteCommandDefinition } from '../types'; - -export const sourceCommandsDefinitions: AutocompleteCommandDefinition[] = [ - { - label: 'from', - insertText: 'from', - kind: 0, - detail: i18n.translate('monaco.esql.autocomplete.fromDoc', { - defaultMessage: - 'Retrieves data from one or more datasets. A dataset is a collection of data that you want to search. The only supported dataset is an index. In a query or subquery, you must use the from command first and it does not need a leading pipe. For example, to retrieve data from an index:', - }), - documentation: { - value: buildDocumentation( - 'from` indexPatterns = wildcardIdentifier (`,` wildcardIdentifier)*', - ['from logs', 'from logs-*', 'from logs_*, events-*', 'from from remote*:logs*'] - ), - }, - sortText: 'A', - }, -]; diff --git a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_listener.test.ts b/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_listener.test.ts deleted file mode 100644 index a33c9f99f6f9e..0000000000000 --- a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_listener.test.ts +++ /dev/null @@ -1,226 +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 { CharStreams } from 'antlr4ts'; -import { AutocompleteListener } from './autocomplete_listener'; -import { ANTLREErrorListener } from '../../../common/error_listener'; - -import { getParser, ROOT_STATEMENT } from '../antlr_facade'; - -import { isDynamicAutocompleteItem } from './dymanic_item'; -import { getDurationItemsWithQuantifier } from './helpers'; -import { mathCommandDefinition } from './autocomplete_definitions/functions_commands'; - -describe('autocomplete_listener', () => { - const getAutocompleteSuggestions = (text: string) => { - const errorListener = new ANTLREErrorListener(); - const parseListener = new AutocompleteListener(); - const parser = getParser(CharStreams.fromString(text), errorListener, parseListener); - - parser[ROOT_STATEMENT](); - - return parseListener.getAutocompleteSuggestions(); - }; - - const testSuggestions = (text: string, expected: string[]) => { - test(`${text} => [${expected.join(',')}]`, () => { - const { suggestions } = getAutocompleteSuggestions(text); - expect(suggestions.map((i) => (isDynamicAutocompleteItem(i) ? i : i.label))).toEqual( - expected - ); - }); - }; - - describe('from', () => { - testSuggestions('f', ['from']); - testSuggestions('from ', ['SourceIdentifier']); - testSuggestions('from a,', ['SourceIdentifier']); - testSuggestions('from a, b ', ['SourceIdentifier']); - }); - - describe('where', () => { - testSuggestions('from a | where ', ['cidr_match', 'FieldIdentifier']); - testSuggestions('from a | where "field" ', [ - '==', - '!=', - '<', - '>', - '<=', - '>=', - 'like', - 'rlike', - 'in', - ]); - testSuggestions('from a | where "field" >= ', ['FieldIdentifier']); - testSuggestions('from a | where "field" >= "field1" ', ['or', 'and', '|']); - testSuggestions('from a | where "field" >= "field1" and ', ['FieldIdentifier']); - testSuggestions('from a | where "field" >= "field1" and "field2" ', [ - '==', - '!=', - '<', - '>', - '<=', - '>=', - 'like', - 'rlike', - 'in', - ]); - testSuggestions('from a | stats a=avg("field") | where a ', [ - '==', - '!=', - '<', - '>', - '<=', - '>=', - 'like', - 'rlike', - 'in', - ]); - testSuggestions('from a | stats a=avg("b") | where "c" ', [ - '==', - '!=', - '<', - '>', - '<=', - '>=', - 'like', - 'rlike', - 'in', - ]); - testSuggestions('from a | where "field" >= "field1" and "field2 == ', ['FieldIdentifier']); - }); - - describe('sort', () => { - testSuggestions('from a | sort ', ['FieldIdentifier']); - testSuggestions('from a | sort "field" ', ['asc', 'desc']); - testSuggestions('from a | sort "field" desc ', ['nulls']); - testSuggestions('from a | sort "field" desc nulls ', ['first', 'last']); - }); - - describe('limit', () => { - testSuggestions('from a | limit ', ['1000']); - testSuggestions('from a | limit 4 ', ['|']); - }); - - describe('mv_expand', () => { - testSuggestions('from a | mv_expand ', ['FieldIdentifier']); - testSuggestions('from a | mv_expand a ', ['|']); - }); - - describe('stats', () => { - testSuggestions('from a | stats ', ['var0']); - testSuggestions('from a | stats a ', ['=']); - testSuggestions('from a | stats a=', [ - 'avg', - 'max', - 'min', - 'sum', - 'count', - 'count_distinct', - 'median', - 'median_absolute_deviation', - 'percentile', - ]); - testSuggestions('from a | stats a=b by ', ['FieldIdentifier']); - testSuggestions('from a | stats a=c by d', ['|']); - testSuggestions('from a | stats a=b, ', ['var0']); - testSuggestions('from a | stats a=max', ['(']); - testSuggestions('from a | stats a=min(', ['FieldIdentifier']); - testSuggestions('from a | stats a=min(b', [')', 'FieldIdentifier']); - testSuggestions('from a | stats a=min(b) ', ['|', 'by']); - testSuggestions('from a | stats a=min(b) by ', ['FieldIdentifier']); - testSuggestions('from a | stats a=min(b),', ['var0']); - testSuggestions('from a | stats var0=min(b),var1=c,', ['var2']); - testSuggestions('from a | stats a=min(b), b=max(', ['FieldIdentifier']); - }); - - describe('enrich', () => { - for (const prevCommand of [ - '', - '| enrich other-policy ', - '| enrich other-policy on b ', - '| enrich other-policy with c ', - ]) { - testSuggestions(`from a ${prevCommand}| enrich`, ['PolicyIdentifier']); - testSuggestions(`from a ${prevCommand}| enrich policy `, ['|', 'on', 'with']); - testSuggestions(`from a ${prevCommand}| enrich policy on `, [ - 'PolicyMatchingFieldIdentifier', - ]); - testSuggestions(`from a ${prevCommand}| enrich policy on b `, ['|', 'with']); - testSuggestions(`from a ${prevCommand}| enrich policy on b with `, [ - 'var0', - 'PolicyFieldIdentifier', - ]); - testSuggestions(`from a ${prevCommand}| enrich policy on b with var0 `, ['=', '|']); - testSuggestions(`from a ${prevCommand}| enrich policy on b with var0 = `, [ - 'PolicyFieldIdentifier', - ]); - testSuggestions(`from a ${prevCommand}| enrich policy on b with var0 = c `, ['|']); - testSuggestions(`from a ${prevCommand}| enrich policy on b with var0 = c, `, [ - 'var1', - 'PolicyFieldIdentifier', - ]); - testSuggestions(`from a ${prevCommand}| enrich policy on b with var0 = c, var1 `, ['=', '|']); - testSuggestions(`from a ${prevCommand}| enrich policy on b with var0 = c, var1 = `, [ - 'PolicyFieldIdentifier', - ]); - testSuggestions(`from a ${prevCommand}| enrich policy with `, [ - 'var0', - 'PolicyFieldIdentifier', - ]); - testSuggestions(`from a ${prevCommand}| enrich policy with c`, ['=', '|']); - } - }); - - describe('eval', () => { - const functionSuggestions = mathCommandDefinition.map(({ label }) => String(label)); - - testSuggestions('from a | eval ', ['var0']); - testSuggestions('from a | eval a ', ['=']); - testSuggestions('from a | eval a=', functionSuggestions); - testSuggestions('from a | eval a=b, ', ['var0']); - testSuggestions('from a | eval a=round', ['(']); - testSuggestions('from a | eval a=round(', ['FieldIdentifier']); - testSuggestions('from a | eval a=round(b) ', ['|', '+', '-', '/', '*']); - testSuggestions('from a | eval a=round(b),', ['var0']); - testSuggestions('from a | eval a=round(b) + ', ['FieldIdentifier', ...functionSuggestions]); - // NOTE: this is handled also partially in the suggestion wrapper with auto-injection of closing brackets - testSuggestions('from a | eval a=round(b', [')', 'FieldIdentifier']); - testSuggestions('from a | eval a=round(b), b=round(', ['FieldIdentifier']); - testSuggestions('from a | stats a=round(b), b=round(', ['FieldIdentifier']); - testSuggestions('from a | eval var0=round(b), var1=round(c) | stats ', ['var2']); - - describe('date math', () => { - const dateSuggestions = [ - 'year', - 'month', - 'week', - 'day', - 'hour', - 'minute', - 'second', - 'millisecond', - ].flatMap((v) => [v, `${v}s`]); - const dateMathSymbols = ['+', '-']; - testSuggestions('from a | eval a = 1 ', dateMathSymbols.concat(dateSuggestions, ['|'])); - testSuggestions('from a | eval a = 1 year ', dateMathSymbols.concat(dateSuggestions, ['|'])); - testSuggestions( - 'from a | eval a = 1 day + 2 ', - dateMathSymbols.concat(dateSuggestions, ['|']) - ); - testSuggestions( - 'from a | eval var0=date_trunc(', - ['FieldIdentifier'].concat(...getDurationItemsWithQuantifier().map(({ label }) => label)) - ); - testSuggestions( - 'from a | eval var0=date_trunc(2 ', - [')', 'FieldIdentifier'].concat(dateSuggestions) - ); - }); - }); -}); diff --git a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_listener.ts b/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_listener.ts deleted file mode 100644 index 35c75e6ce6021..0000000000000 --- a/packages/kbn-monaco/src/esql/lib/autocomplete/autocomplete_listener.ts +++ /dev/null @@ -1,641 +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 { TerminalNode } from 'antlr4ts/tree/TerminalNode'; -import type { AutocompleteCommandDefinition, UserDefinedVariables } from './types'; -import { DynamicAutocompleteItem } from './dymanic_item'; - -import { esql_parserListener as ESQLParserListener } from '../../antlr/esql_parser_listener'; -import { - esql_parser, - esql_parser as ESQLParser, - EnrichCommandContext, - EnrichWithClauseContext, - OperatorExpressionContext, -} from '../../antlr/esql_parser'; - -import { - processingCommandsDefinitions, - sourceCommandsDefinitions, - orderingCommandsDefinitions, - nullsCommandsDefinition, - nullsOrderingCommandsDefinitions, - comparisonCommandsDefinitions, - comparisonOperatorsCommandsDefinitions, - byOperatorDefinition, - pipeDefinition, - openBracketDefinition, - closeBracketDefinition, - mathOperatorsCommandsDefinitions, - aggregationFunctionsDefinitions, - mathCommandDefinition, - whereCommandDefinition, - assignOperatorDefinition, - buildConstantsDefinitions, - buildNewVarDefinition, - asOperatorDefinition, -} from './autocomplete_definitions'; - -import { - EvalCommandContext, - StatsCommandContext, - ComparisonContext, - WhereCommandContext, - SourceCommandContext, - OrderExpressionContext, - FieldContext, - QualifiedNameContext, - ProcessingCommandContext, - SourceIdentifierContext, - UserVariableContext, - BooleanExpressionContext, - RegexBooleanExpressionContext, - WhereBooleanExpressionContext, - LimitCommandContext, - ValueExpressionContext, - KeepCommandContext, - DropCommandContext, - RenameCommandContext, - DissectCommandContext, - GrokCommandContext, - MvExpandCommandContext, -} from '../../antlr/esql_parser'; -import { - onOperatorDefinition, - withOperatorDefinition, -} from './autocomplete_definitions/operators_commands'; -import { dateExpressionDefinitions } from './autocomplete_definitions/date_math_expressions'; -import { - endsWithOpenBracket, - getDateMathOperation, - getDurationItemsWithQuantifier, - isDateFunction, -} from './helpers'; - -export function nonNullable(v: T): v is NonNullable { - return v != null; -} - -export class AutocompleteListener implements ESQLParserListener { - private suggestions: Array = []; - private readonly userDefinedVariables: UserDefinedVariables = { - sourceIdentifiers: [], - policyIdentifiers: [], - }; - private readonly tables: string[][] = []; - private parentContext: number | undefined; - - private get fields(): [DynamicAutocompleteItem] { - return [DynamicAutocompleteItem.FieldIdentifier]; - } - - private get policies(): [DynamicAutocompleteItem] { - return [DynamicAutocompleteItem.PolicyIdentifier]; - } - - private get policyFields(): [DynamicAutocompleteItem] { - return [DynamicAutocompleteItem.PolicyFieldIdentifier]; - } - - private get policyMatchingField(): [DynamicAutocompleteItem] { - return [DynamicAutocompleteItem.PolicyMatchingFieldIdentifier]; - } - - private get hasSuggestions() { - return Boolean(this.suggestions.length); - } - - private isTerminalNodeExists(node: TerminalNode | undefined) { - return node && node.payload?.startIndex >= 0; - } - - private inspectOperatorExpressionContext( - context: OperatorExpressionContext | OperatorExpressionContext[] | undefined, - innerScope: 'constant' | 'dateExpression' | 'booleanExpression' - ): boolean { - if (!context) { - return false; - } - if (Array.isArray(context)) { - return context.some((c) => this.inspectOperatorExpressionContext(c, innerScope)); - } - if (context.operatorExpression()?.length) { - return this.inspectOperatorExpressionContext(context.operatorExpression(), innerScope); - } - if (context.primaryExpression()) { - return Boolean(context.primaryExpression()?.[innerScope]()); - } - return false; - } - - private hasDateExpressionTerminalNode( - context: OperatorExpressionContext | OperatorExpressionContext[] | undefined - ): boolean { - return this.inspectOperatorExpressionContext(context, 'dateExpression'); - } - - private hasOnlyConstantDefined( - context: OperatorExpressionContext | OperatorExpressionContext[] | undefined - ): boolean { - return this.inspectOperatorExpressionContext(context, 'constant'); - } - - private applyConditionalSuggestion( - skipDefinitions: AutocompleteCommandDefinition[], - targetDefinition: AutocompleteCommandDefinition, - context: number - ) { - if (!skipDefinitions.find((i) => i === targetDefinition) && this.parentContext === context) { - return targetDefinition; - } - } - - private getEndCommandSuggestions(skipDefinitions: AutocompleteCommandDefinition[] = []) { - return [ - pipeDefinition, - this.applyConditionalSuggestion(skipDefinitions, byOperatorDefinition, ESQLParser.STATS), - this.applyConditionalSuggestion(skipDefinitions, onOperatorDefinition, ESQLParser.ENRICH), - this.applyConditionalSuggestion(skipDefinitions, withOperatorDefinition, ESQLParser.ENRICH), - ].filter(nonNullable); - } - - private getNewVarName() { - const vars = this.tables.flat(); - let index = 0; - - while (true) { - const value = `var${index}`; - if (!vars.includes(value)) { - return value; - } - index++; - } - } - - getAutocompleteSuggestions() { - return { - suggestions: this.suggestions, - userDefinedVariables: this.userDefinedVariables, - }; - } - - /** ESQLParserListener fields **/ - - enterSourceCommand(ctx: SourceCommandContext) { - this.suggestions = []; - } - - exitSourceCommand(ctx: SourceCommandContext) { - if (ctx.exception) { - this.suggestions = sourceCommandsDefinitions; - } - } - - enterSourceIdentifier(ctx: SourceIdentifierContext) { - this.suggestions = [DynamicAutocompleteItem.SourceIdentifier]; - } - - exitSourceIdentifier(ctx: SourceIdentifierContext) { - if (!ctx.childCount) { - this.suggestions = [DynamicAutocompleteItem.SourceIdentifier]; - } else if (!ctx.exception && ctx.text) { - this.userDefinedVariables.sourceIdentifiers.push(ctx.text); - } - } - - enterProcessingCommand(ctx: ProcessingCommandContext) { - this.tables.push([]); - this.suggestions = []; - this.parentContext = undefined; - } - - exitProcessingCommand(ctx: ProcessingCommandContext) { - if (ctx.exception) { - this.suggestions = processingCommandsDefinitions; - } - this.parentContext = undefined; - } - - enterStatsCommand(ctx: StatsCommandContext) { - this.suggestions = []; - this.parentContext = ESQLParser.STATS; - const fn = ctx.fields(); - if (!fn) { - this.suggestions = [buildNewVarDefinition(this.getNewVarName())]; - return; - } - } - - enterEvalCommand(ctx: EvalCommandContext) { - this.suggestions = []; - this.parentContext = ESQLParser.EVAL; - } - - exitStatsCommand(ctx: StatsCommandContext) { - const qn = ctx.qualifiedNames(); - if (qn && qn.text) { - this.suggestions = this.getEndCommandSuggestions([byOperatorDefinition]); - } - } - - exitKeepCommand?(ctx: KeepCommandContext) { - const qn = ctx.qualifiedNames(); - if (qn && qn.text) { - if (qn.text.slice(-1) !== ',') { - this.suggestions = this.getEndCommandSuggestions(); - } - } - } - - exitDropCommand?(ctx: DropCommandContext) { - const qn = ctx.qualifiedNames(); - if (qn && qn.text) { - if (qn.text.slice(-1) !== ',') { - this.suggestions = this.getEndCommandSuggestions(); - } - } - } - - enterRenameCommand(ctx: RenameCommandContext) { - this.parentContext = ESQLParser.RENAME; - } - - exitRenameCommand?(ctx: RenameCommandContext) { - const rc = ctx.renameClause(); - const commaExists = ctx.COMMA(); - if (!rc[0].exception) { - const qn = rc[0].renameVariable(); - const asExists = this.isTerminalNodeExists(rc[0].AS()); - if (asExists && qn && !qn.text) { - this.suggestions = []; - } - if (qn && qn.text) { - if (!commaExists.length) { - this.suggestions = this.getEndCommandSuggestions(); - } - } - } - } - - exitDissectCommand?(ctx: DissectCommandContext) { - const qn = ctx.qualifiedNames(); - const pattern = ctx.string(); - if (qn && qn.text && pattern && pattern.text && pattern.text !== '') { - this.suggestions = this.getEndCommandSuggestions(); - } - } - - exitGrokCommand?(ctx: GrokCommandContext) { - const qn = ctx.qualifiedNames(); - const pattern = ctx.string(); - if (qn && qn.text && pattern && pattern.text && pattern.text !== '') { - this.suggestions = this.getEndCommandSuggestions(); - } - } - - exitMvExpandCommand?(ctx: MvExpandCommandContext) { - const qn = ctx.qualifiedNames(); - if (qn && qn.text) { - this.suggestions = this.getEndCommandSuggestions(); - } - } - - exitQualifiedName(ctx: QualifiedNameContext) { - const isInEval = this.parentContext === ESQLParser.EVAL; - const isInStats = this.parentContext === ESQLParser.STATS; - const isInRename = this.parentContext === ESQLParser.RENAME; - if (this.parentContext && isInRename) { - if (!ctx.exception && ctx.text) { - this.suggestions = [asOperatorDefinition]; - } - } - if (this.parentContext && (isInStats || isInEval)) { - this.suggestions = [ - ...this.getEndCommandSuggestions(), - ...(isInEval ? mathOperatorsCommandsDefinitions : []), - ]; - } - - if ( - ctx - .identifier() - .some( - (i) => - !( - this.isTerminalNodeExists(i.QUOTED_IDENTIFIER()) || - this.isTerminalNodeExists(i.UNQUOTED_IDENTIFIER()) - ) - ) - ) { - this.suggestions = this.fields; - } - } - - enterField(ctx: FieldContext) { - this.suggestions = []; - } - - exitField(ctx: FieldContext) { - const hasAssign = this.isTerminalNodeExists(ctx.ASSIGN()); - - if (ctx.exception) { - if (!hasAssign) { - this.suggestions = [buildNewVarDefinition(this.getNewVarName())]; - return; - } - } else { - if (!hasAssign) { - this.suggestions = [assignOperatorDefinition]; - } - } - } - - exitUserVariable(ctx: UserVariableContext) { - if (!ctx.exception && ctx.text) { - this.tables.at(-1)?.push(ctx.text); - } - } - - enterBooleanExpression(ctx: BooleanExpressionContext) { - this.suggestions = []; - } - - exitBooleanExpression(ctx: BooleanExpressionContext) { - if (ctx.exception) { - const ve = ctx.valueExpression(); - if (!ve) { - if (this.parentContext === ESQLParser.STATS) { - this.suggestions = [...aggregationFunctionsDefinitions]; - return; - } - - if (this.parentContext === ESQLParser.EVAL) { - this.suggestions = [...mathCommandDefinition]; - return; - } - } - } - } - - exitValueExpression(ctx: ValueExpressionContext) { - const isInStats = this.parentContext === ESQLParser.STATS; - const isInEval = this.parentContext === ESQLParser.EVAL; - - if (isInStats || isInEval) { - const hasFN = - ctx.tryGetToken(esql_parser.UNARY_FUNCTION, 0) || - ctx.tryGetToken(esql_parser.MATH_FUNCTION, 0); - const hasLP = ctx.tryGetToken(esql_parser.LP, 0); - const hasRP = ctx.tryGetToken(esql_parser.RP, 0); - // TODO: handle also other math signs later on - const hasPlusOrMinus = - ctx.tryGetToken(esql_parser.PLUS, 0) || ctx.tryGetToken(esql_parser.MINUS, 0); - - const hasDateLiteral = ctx.tryGetToken(esql_parser.DATE_LITERAL, 0); - - const isInDurationMode = hasDateLiteral || (hasFN && isDateFunction(hasFN.text)); - if (hasPlusOrMinus && this.isTerminalNodeExists(hasPlusOrMinus)) { - if (isInEval) { - this.suggestions = isInDurationMode - ? // eval a = 1 year + || eval a = date_trunc(1 year, date) - - [ - ...mathCommandDefinition.filter(({ label }) => isDateFunction(String(label))), - ...getDurationItemsWithQuantifier(), - ] - : // eval a = 1 + || eval a = abs(b) - - [...this.fields, ...mathCommandDefinition]; - } else { - this.suggestions = [...this.fields, ...aggregationFunctionsDefinitions]; - } - return; - } - - // Monaco will auto close the brackets but the language listener will not pick up yet this auto-change. - // We try to inject it outside but it won't cover all scenarios - if (hasFN) { - if (!hasLP) { - this.suggestions = [openBracketDefinition]; - return; - } - - this.suggestions = []; - - if (!hasRP) { - if (ctx.childCount === 3) { - // TODO: improve here to suggest comma if signature has multiple args - this.suggestions.push(closeBracketDefinition); - } - } - this.suggestions.push(...this.fields); - // Need to get the function name from the previous node (current is "(" ) - const fnName = hasFN.text; - const fnsToCheck = isInEval ? mathCommandDefinition : aggregationFunctionsDefinitions; - if (fnName && fnsToCheck.some(({ label }) => label === fnName)) { - // push date suggestions only for date functions - // TODO: improve this checks - if (isInEval && isDateFunction(fnName)) { - if (!ctx.tryGetToken(esql_parser.DATE_LITERAL, 0)) { - this.suggestions.push( - // if it's just after the open bracket, suggest also a number together with a date period, - // otherwise just the date period unit - ...(endsWithOpenBracket(ctx.text) - ? getDurationItemsWithQuantifier() - : dateExpressionDefinitions) - ); - } - } - } - - return; - } else { - if (ctx.childCount === 1) { - if (ctx.text && ctx.text.indexOf('(') === -1) { - this.suggestions = [...mathOperatorsCommandsDefinitions]; - if (isInEval) { - // eval a = 1 || eval a = 1 year + 1 - if ( - this.hasDateExpressionTerminalNode(ctx.operatorExpression()) || - this.hasOnlyConstantDefined(ctx.operatorExpression()) - ) { - this.suggestions = [...getDateMathOperation(), ...dateExpressionDefinitions]; - } - } - - if (isInStats) { - this.suggestions.push(...aggregationFunctionsDefinitions); - } - - this.suggestions.push(...this.getEndCommandSuggestions()); - } - return; - } - } - this.suggestions = [...this.fields]; - if (ctx.exception && isInEval) { - // case: eval a = x * or / - this.suggestions.push(...mathCommandDefinition); - } - this.suggestions.push(...this.getEndCommandSuggestions()); - } - } - - enterWhereBooleanExpression(ctx: WhereBooleanExpressionContext) { - this.suggestions = []; - } - - enterWhereCommand(ctx: WhereCommandContext) { - this.suggestions = []; - this.parentContext = ESQLParser.WHERE; - } - - enterEnrichCommand(ctx: EnrichCommandContext) { - this.suggestions = []; - this.parentContext = ESQLParser.ENRICH; - } - - exitEnrichCommand(ctx: EnrichCommandContext) { - const policyName = ctx.enrichIdentifier().text; - if (policyName && !this.userDefinedVariables.policyIdentifiers.includes(policyName)) { - this.userDefinedVariables.policyIdentifiers.push(policyName); - } - - if (this.parentContext === ESQLParser.WITH) { - return; - } - if (!policyName) { - this.suggestions = this.policies; - } - - if (policyName) - if (this.parentContext === ESQLParser.ENRICH) { - const hasOn = this.isTerminalNodeExists(ctx.ON()); - if (hasOn && !ctx._matchField.text) { - this.suggestions = this.policyMatchingField; - } else { - this.suggestions = this.getEndCommandSuggestions( - hasOn ? [onOperatorDefinition] : undefined - ); - } - } - } - - enterEnrichWithClause(ctx: EnrichWithClauseContext) { - this.suggestions = []; - this.parentContext = ESQLParser.WITH; - } - - exitEnrichWithClause(ctx: EnrichWithClauseContext) { - const hasAssign = this.isTerminalNodeExists(ctx.ASSIGN()); - // Note: this gets filled only after the assign operation :( - if (ctx._newName?.text) { - this.tables.at(-1)?.push(ctx._newName.text); - } - - if (!ctx.exception && ctx.enrichFieldIdentifier().length === 1) { - // if it's after the assign operator, then suggest the fields from the policy - // TODO: need to check if the enrichFieldIdentifier given is a policyField or not and decide whether append the assignOperator - this.suggestions = !hasAssign - ? [assignOperatorDefinition, ...this.getEndCommandSuggestions()] - : this.policyFields; - } else { - this.suggestions = []; - if (!hasAssign) { - this.suggestions.push(buildNewVarDefinition(this.getNewVarName())); - } - if (!ctx._enrichField?.text) { - this.suggestions.push(...this.policyFields); - } - if (this.suggestions.length === 0) { - this.suggestions = this.getEndCommandSuggestions([ - onOperatorDefinition, - withOperatorDefinition, - ]); - } - } - } - - exitWhereCommand(ctx: WhereCommandContext) { - const booleanExpression = ctx.whereBooleanExpression(); - - if (booleanExpression.exception) { - if (!booleanExpression.text) { - this.suggestions = [...whereCommandDefinition, ...this.fields]; - return; - } - this.suggestions = this.fields; - return; - } else { - const innerBooleanExpressions = booleanExpression.getRuleContexts( - WhereBooleanExpressionContext - ); - const regexBooleanExpression = booleanExpression.getRuleContexts( - RegexBooleanExpressionContext - ); - - if (booleanExpression.WHERE_FUNCTIONS()) { - if (booleanExpression.COMMA().length) { - this.suggestions = []; - return; - } - } - - if (regexBooleanExpression.length) { - this.suggestions = []; - return; - } - - if (innerBooleanExpressions.some((be) => be.exception)) { - this.suggestions = this.fields; - return; - } - } - if (!this.hasSuggestions && !booleanExpression.WHERE_FUNCTIONS()) { - this.suggestions = comparisonCommandsDefinitions; - } - } - - exitComparison(ctx: ComparisonContext) { - const operatorExpression = ctx.operatorExpression(); - if (operatorExpression.some((o) => o.exception)) { - this.suggestions = this.fields; - return; - } - this.suggestions = [ - ...comparisonOperatorsCommandsDefinitions, - ...this.getEndCommandSuggestions(), - ]; - } - - exitOrderExpression(ctx: OrderExpressionContext) { - if (ctx.booleanExpression().exception) { - this.suggestions = this.fields; - return; - } - if (!this.isTerminalNodeExists(ctx.ORDERING())) { - this.suggestions = orderingCommandsDefinitions; - return; - } - if (!this.isTerminalNodeExists(ctx.NULLS_ORDERING())) { - this.suggestions = [nullsCommandsDefinition]; - return; - } - if (!this.isTerminalNodeExists(ctx.NULLS_ORDERING_DIRECTION())) { - this.suggestions = nullsOrderingCommandsDefinitions; - return; - } - } - - exitLimitCommand(ctx: LimitCommandContext) { - const DEFAULT_LIMIT_SIZE = 1000; - - if (!this.isTerminalNodeExists(ctx.INTEGER_LITERAL())) { - this.suggestions = buildConstantsDefinitions([DEFAULT_LIMIT_SIZE.toString()], ''); - } else { - this.suggestions = this.getEndCommandSuggestions(); - } - } -} diff --git a/packages/kbn-monaco/src/esql/lib/autocomplete/dymanic_item.ts b/packages/kbn-monaco/src/esql/lib/autocomplete/dymanic_item.ts deleted file mode 100644 index 621c8900447a0..0000000000000 --- a/packages/kbn-monaco/src/esql/lib/autocomplete/dymanic_item.ts +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 and the Server Side Public License, v 1; you may not use this file except - * in compliance with, at your election, the Elastic License 2.0 or the Server - * Side Public License, v 1. - */ - -export enum DynamicAutocompleteItem { - SourceIdentifier = 'SourceIdentifier', - FieldIdentifier = 'FieldIdentifier', - PolicyIdentifier = 'PolicyIdentifier', - PolicyFieldIdentifier = 'PolicyFieldIdentifier', - PolicyMatchingFieldIdentifier = 'PolicyMatchingFieldIdentifier', -} - -const DynamicAutocompleteItems = Object.values(DynamicAutocompleteItem); - -export function isDynamicAutocompleteItem(v: unknown): v is DynamicAutocompleteItem { - return DynamicAutocompleteItems.some((dai) => dai === v); -} diff --git a/packages/kbn-monaco/src/esql/lib/autocomplete/helpers.ts b/packages/kbn-monaco/src/esql/lib/autocomplete/helpers.ts deleted file mode 100644 index be1392cb11e72..0000000000000 --- a/packages/kbn-monaco/src/esql/lib/autocomplete/helpers.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 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 { mathOperatorsCommandsDefinitions } from './autocomplete_definitions'; -import { dateExpressionDefinitions } from './autocomplete_definitions/date_math_expressions'; - -export function endsWithOpenBracket(text: string) { - return /\($/.test(text); -} - -export function isDateFunction(fnName: string) { - // TODO: improve this and rely in signature in the future - return ['to_datetime', 'date_trunc', 'date_parse'].includes(fnName.toLowerCase()); -} - -export function getDateMathOperation() { - return mathOperatorsCommandsDefinitions.filter(({ label }) => ['+', '-'].includes(String(label))); -} - -export function getDurationItemsWithQuantifier(quantifier: number = 1) { - return dateExpressionDefinitions - .filter(({ label }) => !/s$/.test(label.toString())) - .map(({ label, insertText, ...rest }) => ({ - label: `${quantifier} ${label}`, - insertText: `${quantifier} ${insertText}`, - ...rest, - })); -} diff --git a/packages/kbn-monaco/src/esql/lib/autocomplete/types.ts b/packages/kbn-monaco/src/esql/lib/autocomplete/types.ts deleted file mode 100644 index fc22bae7bbdb9..0000000000000 --- a/packages/kbn-monaco/src/esql/lib/autocomplete/types.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 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 { monaco } from '../../../..'; - -/** @public **/ -export interface ESQLCustomAutocompleteCallbacks { - getSourceIdentifiers?: CallbackFn; - getFieldsIdentifiers?: CallbackFn; - getPoliciesIdentifiers?: CallbackFn<{ name: string; indices: string[] }>; - getPolicyFieldsIdentifiers?: CallbackFn; - getPolicyMatchingFieldIdentifiers?: CallbackFn; -} - -/** @internal **/ -type CallbackFn = (ctx: { - word: string; - userDefinedVariables: UserDefinedVariables; -}) => T[] | Promise; - -/** @internal **/ -export interface UserDefinedVariables { - sourceIdentifiers: string[]; - policyIdentifiers: string[]; -} - -/** @internal **/ -export type AutocompleteCommandDefinition = Pick< - monaco.languages.CompletionItem, - 'label' | 'insertText' | 'kind' | 'detail' | 'documentation' | 'sortText' | 'command' ->; diff --git a/packages/kbn-monaco/src/esql/lib/monaco/esql_ast_provider.ts b/packages/kbn-monaco/src/esql/lib/monaco/esql_ast_provider.ts new file mode 100644 index 0000000000000..39bd193d5611c --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/monaco/esql_ast_provider.ts @@ -0,0 +1,121 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { EditorError } from '../../../types'; +import type { ESQLCallbacks } from '../ast/shared/types'; +import { monaco } from '../../../monaco_imports'; +import type { ESQLWorker } from '../../worker/esql_worker'; +import { suggest } from '../ast/autocomplete/autocomplete'; +import { getHoverItem } from '../ast/hover'; +import { getSignatureHelp } from '../ast/signature'; +import type { ESQLMessage } from '../ast/types'; +import { validateAst } from '../ast/validation/validation'; + +// from linear offset to Monaco position +export function offsetToRowColumn(expression: string, offset: number): monaco.Position { + const lines = expression.split(/\n/); + let remainingChars = offset; + let lineNumber = 1; + for (const line of lines) { + if (line.length >= remainingChars) { + return new monaco.Position(lineNumber, remainingChars + 1); + } + remainingChars -= line.length + 1; + lineNumber++; + } + + throw new Error('Algorithm failure'); +} + +function wrapAsMonacoMessage( + type: 'error' | 'warning', + code: string, + messages: Array +): EditorError[] { + const fallbackPosition = { column: 0, lineNumber: 0 }; + return messages.map((e) => { + if ('severity' in e) { + return e; + } + const startPosition = e.location ? offsetToRowColumn(code, e.location.min) : fallbackPosition; + const endPosition = e.location + ? offsetToRowColumn(code, e.location.max || 0) + : fallbackPosition; + return { + message: e.text, + startColumn: startPosition.column, + startLineNumber: startPosition.lineNumber, + endColumn: endPosition.column + 1, + endLineNumber: endPosition.lineNumber, + severity: type === 'error' ? monaco.MarkerSeverity.Error : monaco.MarkerSeverity.Warning, + _source: 'client' as const, + }; + }); +} + +export class ESQLAstAdapter { + constructor( + private worker: (...uris: monaco.Uri[]) => Promise, + private callbacks?: ESQLCallbacks + ) {} + + private async getAstWorker(model: monaco.editor.ITextModel) { + const worker = await this.worker(model.uri); + return worker.getAst; + } + + async getAst(model: monaco.editor.ITextModel, code?: string) { + const getAstFn = await this.getAstWorker(model); + return getAstFn(code ?? model.getValue()); + } + + async validate(model: monaco.editor.ITextModel, code: string) { + const getAstFn = await this.getAstWorker(model); + const { errors, warnings } = await validateAst( + code ?? model.getValue(), + getAstFn, + this.callbacks + ); + const monacoErrors = wrapAsMonacoMessage('error', code, errors); + const monacoWarnings = wrapAsMonacoMessage('warning', code, warnings); + return { errors: monacoErrors, warnings: monacoWarnings }; + } + + async suggestSignature( + model: monaco.editor.ITextModel, + position: monaco.Position, + context: monaco.languages.SignatureHelpContext + ) { + const getAstFn = await this.getAstWorker(model); + return getSignatureHelp(model, position, context, getAstFn); + } + + async getHover( + model: monaco.editor.ITextModel, + position: monaco.Position, + token: monaco.CancellationToken + ) { + const getAstFn = await this.getAstWorker(model); + return getHoverItem(model, position, token, getAstFn, this.callbacks); + } + + async autocomplete( + model: monaco.editor.ITextModel, + position: monaco.Position, + context: monaco.languages.CompletionContext + ) { + const getAstFn = await this.getAstWorker(model); + const suggestionEntries = await suggest(model, position, context, getAstFn, this.callbacks); + return { + suggestions: suggestionEntries.map((suggestion) => ({ + ...suggestion, + range: undefined as unknown as monaco.IRange, + })), + }; + } +} diff --git a/packages/kbn-monaco/src/esql/lib/monaco/esql_completion_provider.ts b/packages/kbn-monaco/src/esql/lib/monaco/esql_completion_provider.ts deleted file mode 100644 index 4a407c3519769..0000000000000 --- a/packages/kbn-monaco/src/esql/lib/monaco/esql_completion_provider.ts +++ /dev/null @@ -1,136 +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 { monaco } from '../../../monaco_imports'; -import { DynamicAutocompleteItem, isDynamicAutocompleteItem } from '../autocomplete/dymanic_item'; -import { - buildFieldsDefinitions, - buildSourcesDefinitions, - buildPoliciesDefinitions, - buildNoPoliciesAvailableDefinition, - buildMatchingFieldsDefinition, -} from '../autocomplete/autocomplete_definitions/dynamic_commands'; -import { pipeDefinition } from '../autocomplete/autocomplete_definitions'; - -import type { - AutocompleteCommandDefinition, - ESQLCustomAutocompleteCallbacks, - UserDefinedVariables, -} from '../autocomplete/types'; -import type { ESQLWorker } from '../../worker/esql_worker'; - -export class ESQLCompletionAdapter implements monaco.languages.CompletionItemProvider { - constructor( - private worker: (...uris: monaco.Uri[]) => Promise, - private callbacks?: ESQLCustomAutocompleteCallbacks - ) {} - - public triggerCharacters = ['(', ' ', '']; - - private async injectDynamicAutocompleteItems( - suggestions: Array, - ctx: { - word: string; - userDefinedVariables: UserDefinedVariables; - } - ): Promise { - const allSuggestions: AutocompleteCommandDefinition[][] = await Promise.all( - suggestions.map(async (suggestion) => { - if (!isDynamicAutocompleteItem(suggestion)) { - return [suggestion]; - } - let dynamicItems: AutocompleteCommandDefinition[] = []; - - if (suggestion === DynamicAutocompleteItem.SourceIdentifier) { - dynamicItems = buildSourcesDefinitions( - (await this.callbacks?.getSourceIdentifiers?.(ctx)) ?? [] - ); - if (!ctx.word && ctx.userDefinedVariables.sourceIdentifiers.length) { - dynamicItems = [pipeDefinition]; - } - } - - if (suggestion === DynamicAutocompleteItem.FieldIdentifier) { - dynamicItems = buildFieldsDefinitions( - (await this.callbacks?.getFieldsIdentifiers?.(ctx)) ?? [] - ); - } - - if (suggestion === DynamicAutocompleteItem.PolicyIdentifier) { - const results = await this.callbacks?.getPoliciesIdentifiers?.(ctx); - dynamicItems = results?.length - ? buildPoliciesDefinitions(results) - : buildNoPoliciesAvailableDefinition(); - } - - if (suggestion === DynamicAutocompleteItem.PolicyFieldIdentifier) { - dynamicItems = buildFieldsDefinitions( - (await this.callbacks?.getPolicyFieldsIdentifiers?.(ctx)) || [] - ); - } - - if (suggestion === DynamicAutocompleteItem.PolicyMatchingFieldIdentifier) { - const [fields = [], matchingField] = await Promise.all([ - this.callbacks?.getFieldsIdentifiers?.(ctx), - this.callbacks?.getPolicyMatchingFieldIdentifiers?.(ctx), - ]); - dynamicItems = matchingField?.length - ? buildMatchingFieldsDefinition(matchingField[0], fields) - : buildFieldsDefinitions(fields); - } - return dynamicItems; - }) - ); - - return allSuggestions.flat(); - } - - async provideCompletionItems( - model: monaco.editor.IReadOnlyModel, - position: monaco.Position - ): Promise { - const lines = model.getLineCount(); - - const currentLineChars = model.getValueInRange({ - startLineNumber: 0, - startColumn: 0, - endLineNumber: position.lineNumber, - endColumn: position.column, - }); - const wordInfo = model.getWordUntilPosition(position); - const worker = await this.worker(model.uri); - const providedSuggestions = - lines !== position.lineNumber || - model.getLineContent(position.lineNumber).trimEnd().length >= position.column - ? await worker.provideAutocompleteSuggestionsFromString(currentLineChars) - : await worker.provideAutocompleteSuggestions(model.uri.toString(), { - word: wordInfo.word, - line: position.lineNumber, - index: position.column, - }); - - const withDynamicItems = providedSuggestions - ? await this.injectDynamicAutocompleteItems(providedSuggestions.suggestions, { - word: wordInfo.word, - userDefinedVariables: providedSuggestions.userDefinedVariables, - }) - : []; - - return { - suggestions: withDynamicItems.map((i) => ({ - ...i, - range: { - startLineNumber: position.lineNumber, - endLineNumber: position.lineNumber, - startColumn: wordInfo.startColumn, - endColumn: wordInfo.endColumn, - }, - })), - }; - } -} diff --git a/packages/kbn-monaco/src/esql/lib/monaco/esql_error_listener.ts b/packages/kbn-monaco/src/esql/lib/monaco/esql_error_listener.ts new file mode 100644 index 0000000000000..8881c9b458cc0 --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/monaco/esql_error_listener.ts @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { ANTLRErrorListener, Recognizer, RecognitionException } from 'antlr4ts'; +import type { EditorError } from '../../../types'; +import { createError } from '../ast/ast_errors'; + +export class ESQLErrorListener implements ANTLRErrorListener { + private errors: EditorError[] = []; + + syntaxError( + recognizer: Recognizer, + offendingSymbol: any, + line: number, + column: number, + message: string, + error: RecognitionException | undefined + ): void { + const higherLevelError = error ? createError(error) : undefined; + const textMessage = + higherLevelError?.text && higherLevelError.text !== error?.message + ? higherLevelError.text + : `SyntaxError: ${message}`; + + let endColumn = column + 1; + let startColumn = column; + + if (higherLevelError) { + startColumn = higherLevelError.location.min + 1; + endColumn = higherLevelError.location.max + 1; + } else if (offendingSymbol?._text) { + endColumn = column + offendingSymbol._text.length; + } + + this.errors.push({ + startLineNumber: line, + endLineNumber: line, + startColumn, + endColumn, + message: textMessage, + severity: 8, // monaco.MarkerSeverity.Error, + }); + } + + getErrors(): EditorError[] { + return this.errors; + } +} diff --git a/packages/kbn-monaco/src/esql/lib/monaco/esql_line_tokens.ts b/packages/kbn-monaco/src/esql/lib/monaco/esql_line_tokens.ts index c4817aac586d4..0148884f9bf8b 100644 --- a/packages/kbn-monaco/src/esql/lib/monaco/esql_line_tokens.ts +++ b/packages/kbn-monaco/src/esql/lib/monaco/esql_line_tokens.ts @@ -11,11 +11,12 @@ import { ESQLState } from './esql_state'; /** @internal **/ export class ESQLLineTokens implements monaco.languages.ILineTokens { - endState: monaco.languages.IState; + endState: ESQLState; tokens: monaco.languages.IToken[]; - constructor(tokens: monaco.languages.IToken[]) { + constructor(tokens: monaco.languages.IToken[], line: number) { this.endState = new ESQLState(); + this.endState.setLineNumber(line); this.tokens = tokens; } } diff --git a/packages/kbn-monaco/src/esql/lib/monaco/esql_state.ts b/packages/kbn-monaco/src/esql/lib/monaco/esql_state.ts index a7cfd10f79276..d69702ff926f4 100644 --- a/packages/kbn-monaco/src/esql/lib/monaco/esql_state.ts +++ b/packages/kbn-monaco/src/esql/lib/monaco/esql_state.ts @@ -10,8 +10,20 @@ import { monaco } from '../../../monaco_imports'; /** @internal **/ export class ESQLState implements monaco.languages.IState { + private lastLine: number = 0; + + setLineNumber(n: number) { + this.lastLine = n; + } + + getLineNumber() { + return this.lastLine; + } + clone(): monaco.languages.IState { - return new ESQLState(); + const newState = new ESQLState(); + newState.setLineNumber(this.lastLine); + return newState; } equals(other: monaco.languages.IState): boolean { diff --git a/packages/kbn-monaco/src/esql/lib/monaco/esql_theme.ts b/packages/kbn-monaco/src/esql/lib/monaco/esql_theme.ts index 6fc6caee2886f..1647b954ada7d 100644 --- a/packages/kbn-monaco/src/esql/lib/monaco/esql_theme.ts +++ b/packages/kbn-monaco/src/esql/lib/monaco/esql_theme.ts @@ -77,12 +77,9 @@ export const buildESQlTheme = (): monaco.editor.IStandaloneThemeData => ({ 'row', 'show', 'limit', - 'cidr_match', 'nulls_ordering_direction', 'nulls_ordering', 'null', - 'boolean_value', - 'comparison_operator', 'enrich', 'on', 'with', @@ -90,12 +87,8 @@ export const buildESQlTheme = (): monaco.editor.IStandaloneThemeData => ({ euiThemeVars.euiColorPrimaryText ), - // aggregation functions - ...buildRuleGroup(['unary_function'], euiThemeVars.euiColorPrimaryText), - // is null functions - ...buildRuleGroup(['where_functions'], euiThemeVars.euiColorPrimaryText), - // math functions - ...buildRuleGroup(['math_function'], euiThemeVars.euiColorPrimaryText), + // functions + ...buildRuleGroup(['functions'], euiThemeVars.euiColorPrimaryText), // operators ...buildRuleGroup( diff --git a/packages/kbn-monaco/src/esql/lib/monaco/esql_token_helpers.ts b/packages/kbn-monaco/src/esql/lib/monaco/esql_token_helpers.ts new file mode 100644 index 0000000000000..497344dd606c4 --- /dev/null +++ b/packages/kbn-monaco/src/esql/lib/monaco/esql_token_helpers.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { monaco } from '../../../monaco_imports'; +import { nonNullable } from '../ast/ast_helpers'; +import { ESQL_TOKEN_POSTFIX } from '../constants'; + +export function enrichTokensWithFunctionsMetadata( + tokens: monaco.languages.IToken[] +): monaco.languages.IToken[] { + // need to trim spaces as "abs (arg)" is still valid as function + const myTokensWithoutSpaces = tokens.filter( + ({ scopes }) => scopes !== 'expr_ws' + ESQL_TOKEN_POSTFIX + ); + // find out all unquoted_identifiers index + const possiblyFunctions = myTokensWithoutSpaces + .map((t, i) => (t.scopes === 'unquoted_identifier' + ESQL_TOKEN_POSTFIX ? i : undefined)) + .filter(nonNullable); + + // then check if the token next is an opening bracket + for (const index of possiblyFunctions) { + if (myTokensWithoutSpaces[index + 1]?.scopes === 'lp' + ESQL_TOKEN_POSTFIX) { + // set the custom "functions" token (only used in theming) + myTokensWithoutSpaces[index].scopes = 'functions' + ESQL_TOKEN_POSTFIX; + } + } + return [...tokens]; +} diff --git a/packages/kbn-monaco/src/esql/lib/monaco/esql_tokens_provider.ts b/packages/kbn-monaco/src/esql/lib/monaco/esql_tokens_provider.ts index 2166a5e6f68ea..aa0234c155c33 100644 --- a/packages/kbn-monaco/src/esql/lib/monaco/esql_tokens_provider.ts +++ b/packages/kbn-monaco/src/esql/lib/monaco/esql_tokens_provider.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { CharStreams, Token } from 'antlr4ts'; +import { CharStreams, type Token } from 'antlr4ts'; import { monaco } from '../../../monaco_imports'; import { ANTLREErrorListener } from '../../../common/error_listener'; @@ -16,6 +16,7 @@ import { ESQLState } from './esql_state'; import { getLexer } from '../antlr_facade'; import { ESQL_TOKEN_POSTFIX } from '../constants'; +import { enrichTokensWithFunctionsMetadata } from './esql_token_helpers'; const EOF = -1; @@ -24,10 +25,16 @@ export class ESQLTokensProvider implements monaco.languages.TokensProvider { return new ESQLState(); } - tokenize(line: string, state: monaco.languages.IState): monaco.languages.ILineTokens { + tokenize(line: string, prevState: ESQLState): monaco.languages.ILineTokens { const errorStartingPoints: number[] = []; const errorListener = new ANTLREErrorListener(); - const inputStream = CharStreams.fromString(line); + // This has the drawback of not styling any ESQL wrong query as + // | from ... + const cleanedLine = + prevState.getLineNumber() && line.trimStart()[0] === '|' + ? line.trimStart().substring(1) + : line; + const inputStream = CharStreams.fromString(cleanedLine); const lexer = getLexer(inputStream, errorListener); let done = false; @@ -63,6 +70,11 @@ export class ESQLTokensProvider implements monaco.languages.TokensProvider { myTokens.sort((a, b) => a.startIndex - b.startIndex); - return new ESQLLineTokens(myTokens); + // special tratement for functions + // the previous custom Kibana grammar baked functions directly as tokens, so highlight was easier + // The ES grammar doesn't have the token concept of "function" + const tokensWithFunctions = enrichTokensWithFunctionsMetadata(myTokens); + + return new ESQLLineTokens(tokensWithFunctions, prevState.getLineNumber() + 1); } } diff --git a/packages/kbn-monaco/src/esql/worker/esql_worker.ts b/packages/kbn-monaco/src/esql/worker/esql_worker.ts index 4656ac9e9db7c..db3e01ae220e5 100644 --- a/packages/kbn-monaco/src/esql/worker/esql_worker.ts +++ b/packages/kbn-monaco/src/esql/worker/esql_worker.ts @@ -6,12 +6,12 @@ * Side Public License, v 1. */ -import { CharStreams, type CodePointCharStream } from 'antlr4ts'; -import { monaco } from '../../monaco_imports'; -import { AutocompleteListener } from '../lib/autocomplete/autocomplete_listener'; +import { CharStreams } from 'antlr4ts'; +import type { monaco } from '../../monaco_imports'; import type { BaseWorkerDefinition } from '../../types'; import { getParser, ROOT_STATEMENT } from '../lib/antlr_facade'; -import { ANTLREErrorListener } from '../../common/error_listener'; +import { AstListener } from '../lib/ast/ast_factory'; +import { ESQLErrorListener } from '../lib/monaco/esql_error_listener'; export class ESQLWorker implements BaseWorkerDefinition { private readonly _ctx: monaco.worker.IWorkerContext; @@ -33,7 +33,7 @@ export class ESQLWorker implements BaseWorkerDefinition { const inputStream = this.getModelCharStream(modelUri); if (inputStream) { - const errorListener = new ANTLREErrorListener(); + const errorListener = new ESQLErrorListener(); const parser = getParser(inputStream, errorListener); parser[ROOT_STATEMENT](); @@ -43,32 +43,21 @@ export class ESQLWorker implements BaseWorkerDefinition { return []; } - private async provideAutocompleteSuggestionFromRawString( - inputStream: CodePointCharStream | undefined - ) { - if (inputStream) { - const errorListener = new ANTLREErrorListener(); - const parseListener = new AutocompleteListener(); - const parser = getParser(inputStream, errorListener, parseListener); - - parser[ROOT_STATEMENT](); - - return parseListener.getAutocompleteSuggestions(); + async getAst(text: string | undefined) { + if (!text) { + return { ast: [], errors: [] }; } - } - - public async provideAutocompleteSuggestions( - modelUri: string, - meta: { - word: string; - line: number; - index: number; - } - ) { - return this.provideAutocompleteSuggestionFromRawString(this.getModelCharStream(modelUri)); - } - - public async provideAutocompleteSuggestionsFromString(text: string) { - return this.provideAutocompleteSuggestionFromRawString(CharStreams.fromString(text)); + const inputStream = CharStreams.fromString(text); + const errorListener = new ESQLErrorListener(); + const parserListener = new AstListener(); + const parser = getParser(inputStream, errorListener, parserListener); + + parser[ROOT_STATEMENT](); + + const { ast } = parserListener.getAst(); + return { + ast, + errors: errorListener.getErrors(), + }; } } diff --git a/packages/kbn-monaco/src/types.ts b/packages/kbn-monaco/src/types.ts index 0e5952db8344c..b2559fd919d16 100644 --- a/packages/kbn-monaco/src/types.ts +++ b/packages/kbn-monaco/src/types.ts @@ -23,11 +23,25 @@ export interface CompleteLangModuleType extends LangModuleType { validation$: () => Observable; } -export interface CustomLangModuleType extends LangModuleType { +export interface LanguageProvidersModule { + validate: ( + model: monaco.editor.ITextModel, + code: string, + callbacks?: Deps + ) => Promise<{ errors: monaco.editor.IMarkerData[]; warnings: monaco.editor.IMarkerData[] }>; + getSuggestionProvider: (callbacks?: Deps) => monaco.languages.CompletionItemProvider; + getSignatureProvider?: (callbacks?: Deps) => monaco.languages.SignatureHelpProvider; + getHoverProvider?: (callbacks?: Deps) => monaco.languages.HoverProvider; +} + +export interface CustomLangModuleType + extends Omit, + LanguageProvidersModule { onLanguage: () => void; } export interface EditorError { + severity: monaco.MarkerSeverity; startLineNumber: number; startColumn: number; endLineNumber: number; diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index ef99652ee767f..d38e1399ed3aa 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -23,7 +23,7 @@ pageLoadAssetSize: cloudSecurityPosture: 19109 console: 46091 contentManagement: 16254 - controls: 40000 + controls: 55082 core: 435325 crossClusterReplication: 65408 customIntegrations: 22034 @@ -86,7 +86,7 @@ pageLoadAssetSize: kibanaUsageCollection: 16463 kibanaUtils: 79713 kubernetesSecurity: 77234 - lens: 39000 + lens: 41000 licenseManagement: 41817 licensing: 29004 links: 44490 @@ -131,7 +131,7 @@ pageLoadAssetSize: securitySolutionServerless: 62488 serverless: 16573 serverlessObservability: 68747 - serverlessSearch: 71995 + serverlessSearch: 72995 sessionView: 77750 share: 71239 snapshotRestore: 79032 diff --git a/packages/kbn-profiling-utils/common/__fixtures__/base_flamegraph.ts b/packages/kbn-profiling-utils/common/__fixtures__/base_flamegraph.ts index ce4aa885d3543..625a32959494c 100644 --- a/packages/kbn-profiling-utils/common/__fixtures__/base_flamegraph.ts +++ b/packages/kbn-profiling-utils/common/__fixtures__/base_flamegraph.ts @@ -288,10 +288,6 @@ export const baseFlamegraph: BaseFlameGraph = { Size: 35, SamplingRate: 1, SelfCPU: 7, - SelfAnnualCO2Tons: 0.0013627551116480942, - TotalAnnualCO2Tons: 0.04769642890768329, - SelfAnnualCostsUSD: 61.30240940376492, - TotalAnnualCostsUSD: 2145.5843291317715, TotalCPU: 245, TotalSamples: 7, TotalSeconds: 4.980000019073486, diff --git a/packages/kbn-profiling-utils/common/flamegraph.ts b/packages/kbn-profiling-utils/common/flamegraph.ts index 407a83b4d3504..fc19de530d58d 100644 --- a/packages/kbn-profiling-utils/common/flamegraph.ts +++ b/packages/kbn-profiling-utils/common/flamegraph.ts @@ -52,10 +52,6 @@ export interface BaseFlameGraph { AnnualCO2TonsInclusive: number[]; AnnualCostsUSDInclusive: number[]; AnnualCostsUSDExclusive: number[]; - SelfAnnualCO2Tons: number; - TotalAnnualCO2Tons: number; - SelfAnnualCostsUSD: number; - TotalAnnualCostsUSD: number; } /** Elasticsearch flamegraph */ @@ -77,8 +73,6 @@ export interface ElasticFlameGraph TotalAnnualCO2KgsItems: number[]; SelfAnnualCostsUSDItems: number[]; TotalAnnualCostsUSDItems: number[]; - SelfAnnualCO2Kgs: number; - TotalAnnualCO2Kgs: number; } /** @@ -119,10 +113,6 @@ export function createFlameGraph(base: BaseFlameGraph): ElasticFlameGraph { TotalAnnualCO2KgsItems: base.AnnualCO2TonsInclusive.map(convertTonsToKgs), SelfAnnualCostsUSDItems: base.AnnualCostsUSDExclusive, TotalAnnualCostsUSDItems: base.AnnualCostsUSDInclusive, - SelfAnnualCO2Kgs: convertTonsToKgs(base.SelfAnnualCO2Tons), - TotalAnnualCO2Kgs: convertTonsToKgs(base.TotalAnnualCO2Tons), - SelfAnnualCostsUSD: base.SelfAnnualCostsUSD, - TotalAnnualCostsUSD: base.TotalAnnualCostsUSD, }; const rootFrameGroupID = createFrameGroupID( diff --git a/packages/kbn-profiling-utils/common/functions.ts b/packages/kbn-profiling-utils/common/functions.ts index 5d1a225693caf..72dd41899f861 100644 --- a/packages/kbn-profiling-utils/common/functions.ts +++ b/packages/kbn-profiling-utils/common/functions.ts @@ -205,8 +205,8 @@ export function createTopNFunctions({ const sumSelfCPU = sumBy(framesAndCountsAndIds, 'CountExclusive'); const sumTotalCPU = sumBy(framesAndCountsAndIds, 'CountInclusive'); - const totalAnnualCO2Kgs = sumBy(framesAndCountsAndIds, 'totalAnnualCO2kgs'); - const totalAnnualCostUSD = sumBy(framesAndCountsAndIds, 'totalAnnualCostUSD'); + const totalAnnualCO2Kgs = sumBy(framesAndCountsAndIds, 'selfAnnualCO2kgs'); + const totalAnnualCostUSD = sumBy(framesAndCountsAndIds, 'selfAnnualCostUSD'); return { TotalCount: totalCount, diff --git a/packages/kbn-profiling-utils/package.json b/packages/kbn-profiling-utils/package.json index 984883e078975..c8ba64aa958cd 100644 --- a/packages/kbn-profiling-utils/package.json +++ b/packages/kbn-profiling-utils/package.json @@ -2,5 +2,6 @@ "name": "@kbn/profiling-utils", "private": true, "version": "1.0.0", - "license": "SSPL-1.0 OR Elastic License 2.0" + "license": "SSPL-1.0 OR Elastic License 2.0", + "sideEffects": false } \ No newline at end of file diff --git a/packages/kbn-react-field/src/field_button/__snapshots__/field_button.test.tsx.snap b/packages/kbn-react-field/src/field_button/__snapshots__/field_button.test.tsx.snap index 0b4ceaf06e863..e172afa0e7ff4 100644 --- a/packages/kbn-react-field/src/field_button/__snapshots__/field_button.test.tsx.snap +++ b/packages/kbn-react-field/src/field_button/__snapshots__/field_button.test.tsx.snap @@ -9,7 +9,7 @@ exports[`fieldAction is rendered 1`] = ` onClick={[Function]} > {fieldIcon && {fieldIcon}} {fieldName && ( - + {fieldName} )} diff --git a/packages/kbn-reporting/export_types/csv_common/index.ts b/packages/kbn-reporting/export_types/csv_common/index.ts index d15efb06e3682..ec46c66ea3c25 100644 --- a/packages/kbn-reporting/export_types/csv_common/index.ts +++ b/packages/kbn-reporting/export_types/csv_common/index.ts @@ -51,3 +51,5 @@ export const CSV_SEARCHSOURCE_IMMEDIATE_TYPE = 'csv_searchsource_immediate'; // but the extension points are still needed for pre-existing scripted automation, until 8.0 export const CSV_REPORT_TYPE_DEPRECATED = 'CSV'; export const CSV_JOB_TYPE_DEPRECATED = 'csv'; + +export { getQueryFromCsvJob, type QueryInspection } from './lib/get_query_from_job'; diff --git a/packages/kbn-reporting/export_types/csv_common/jest.config.js b/packages/kbn-reporting/export_types/csv_common/jest.config.js new file mode 100644 index 0000000000000..91308e62ea8d6 --- /dev/null +++ b/packages/kbn-reporting/export_types/csv_common/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-reporting/export_types/csv_common'], +}; diff --git a/packages/kbn-reporting/export_types/csv_common/lib/get_query_from_job.test.ts b/packages/kbn-reporting/export_types/csv_common/lib/get_query_from_job.test.ts new file mode 100644 index 0000000000000..faf3c13029cce --- /dev/null +++ b/packages/kbn-reporting/export_types/csv_common/lib/get_query_from_job.test.ts @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { EsQuerySortValue, SearchSourceFields } from '@kbn/data-plugin/common'; +import { searchSourceCommonMock } from '@kbn/data-plugin/common/search/search_source/mocks'; +import { createStubDataView } from '@kbn/data-views-plugin/common/stubs'; +import { getQueryFromCsvJob } from './get_query_from_job'; + +describe('getQueryFromCsvJob', () => { + it('returns QueryInspection data', async () => { + const searchSourceStart = { ...searchSourceCommonMock }; + const originalCreate = searchSourceStart.create; + searchSourceStart.create = jest.fn().mockImplementation(async () => { + const original = await originalCreate(); + const originalGetField = original.getField; + const getField = (fieldName: keyof SearchSourceFields) => { + if (fieldName === 'index') { + return createStubDataView({ + spec: { + id: 'test-*', + title: 'test-*', + }, + }); + } + return originalGetField(fieldName); + }; + return { + ...original, + getField, + }; + }); + + const config = { scroll: { duration: '2m', size: 500 } }; + + const fromTime = '2019-06-20T00:00:00.000Z'; + const toTime = '2019-06-24T00:00:00.000Z'; + const serializedSearchSource = { + version: true, + query: { query: '', language: 'kuery' }, + index: '5193f870-d861-11e9-a311-0fa548c5f953', + sort: [{ order_date: 'desc' }] as EsQuerySortValue[], + fields: ['*'], + filter: [], + parent: { + query: { language: 'kuery', query: '' }, + filter: [], + parent: { + filter: [ + { + meta: { index: '5193f870-d861-11e9-a311-0fa548c5f953', params: {} }, + range: { + order_date: { + gte: fromTime, + lte: toTime, + format: 'strict_date_optional_time', + }, + }, + }, + ], + }, + }, + }; + + const searchSource = await searchSourceStart.create(serializedSearchSource); + const query = getQueryFromCsvJob(searchSource, config); + + // NOTE the mocked search source service is not returning assertable info + expect(query).toMatchInlineSnapshot(` + Object { + "requestBody": undefined, + } + `); + }); +}); diff --git a/packages/kbn-reporting/export_types/csv_common/lib/get_query_from_job.ts b/packages/kbn-reporting/export_types/csv_common/lib/get_query_from_job.ts new file mode 100644 index 0000000000000..73d9e6280056d --- /dev/null +++ b/packages/kbn-reporting/export_types/csv_common/lib/get_query_from_job.ts @@ -0,0 +1,60 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { estypes } from '@elastic/elasticsearch'; +import type { ISearchSource } from '@kbn/data-plugin/common'; +import { durationToNumber } from '@kbn/reporting-common'; + +/** + * Type to wrap the untyped object returned when + * getting the query from SearchSource service + */ +export interface QueryInspection { + requestBody: estypes.SearchRequest; +} + +/** + * @internal + */ +interface CsvConfigType { + scroll: { + size: number; + duration: string; + }; +} + +/** + * A utility to get the query from a CSV reporting job to inspect or analyze + * @public + */ +export const getQueryFromCsvJob = ( + searchSource: ISearchSource, + { scroll: config }: CsvConfigType, + pitId?: string +): QueryInspection => { + // Max number of documents in each returned page + searchSource.setField('size', durationToNumber(config.size)); + + // Max time to wait for result + searchSource.setField('timeout', config.duration); + + // Request high accuracy for calculating total hits + searchSource.setField('trackTotalHits', true); + + if (pitId) { + // Always use most recently provided PIT + searchSource.setField('pit', { + id: pitId, + keep_alive: config.duration, + }); + } + + return { + requestBody: searchSource.getSearchRequestBody(), + }; +}; diff --git a/packages/kbn-reporting/export_types/csv_common/tsconfig.json b/packages/kbn-reporting/export_types/csv_common/tsconfig.json index 27592ed3e537f..32ffe1f902b7e 100644 --- a/packages/kbn-reporting/export_types/csv_common/tsconfig.json +++ b/packages/kbn-reporting/export_types/csv_common/tsconfig.json @@ -16,5 +16,6 @@ "kbn_references": [ "@kbn/data-plugin", "@kbn/reporting-common", + "@kbn/data-views-plugin", ] } diff --git a/packages/kbn-reporting/public/types.ts b/packages/kbn-reporting/public/types.ts index 34aa52544ca76..15baa0ee5605f 100644 --- a/packages/kbn-reporting/public/types.ts +++ b/packages/kbn-reporting/public/types.ts @@ -7,6 +7,7 @@ */ export interface ClientConfigType { + csv: { scroll: { duration: string; size: number } }; poll: { jobsRefresh: { interval: number; intervalErrorMultiplier: number } }; roles: { enabled: boolean }; export_types: { pdf: { enabled: boolean }; png: { enabled: boolean }; csv: { enabled: boolean } }; diff --git a/packages/kbn-resizable-layout/package.json b/packages/kbn-resizable-layout/package.json index 4f925688a84b4..3963d28c52cc1 100644 --- a/packages/kbn-resizable-layout/package.json +++ b/packages/kbn-resizable-layout/package.json @@ -2,5 +2,6 @@ "name": "@kbn/resizable-layout", "private": true, "version": "1.0.0", - "license": "SSPL-1.0 OR Elastic License 2.0" + "license": "SSPL-1.0 OR Elastic License 2.0", + "sideEffects": false } \ No newline at end of file diff --git a/packages/kbn-router-utils/README.md b/packages/kbn-router-utils/README.md new file mode 100644 index 0000000000000..e5dcb4cdb4fe4 --- /dev/null +++ b/packages/kbn-router-utils/README.md @@ -0,0 +1,34 @@ +# @kbn/router-utils + +This package provides util functions when working with the router. + +## getRouterLinkProps + +Useful to generate link component properties for HTML elements, this link properties will allow them to behave as native links and handle events such as open in a new tab, or client-side navigation without refreshing the whole page. + +### Example + +We want a button to both navigate to Discover client-side or open on a new window. + +```ts +const DiscoverLink = (discoverLinkParams) => { + const discoverUrl = discover.locator?.getRedirectUrl(discoverLinkParams); + + const navigateToDiscover = () => { + discover.locator?.navigate(discoverLinkParams); + }; + + const linkProps = getRouterLinkProps({ + href: discoverUrl, + onClick: navigateToDiscover, + }); + + return ( + <> + + {discoverLinkTitle} + + + ); +}; +``` diff --git a/packages/kbn-router-utils/index.ts b/packages/kbn-router-utils/index.ts new file mode 100644 index 0000000000000..f5ed5ed7e7604 --- /dev/null +++ b/packages/kbn-router-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 { getRouterLinkProps } from './src/get_router_link_props'; diff --git a/packages/kbn-router-utils/jest.config.js b/packages/kbn-router-utils/jest.config.js new file mode 100644 index 0000000000000..32497360f9419 --- /dev/null +++ b/packages/kbn-router-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-router-utils'], +}; diff --git a/packages/kbn-router-utils/kibana.jsonc b/packages/kbn-router-utils/kibana.jsonc new file mode 100644 index 0000000000000..c255dacb11c70 --- /dev/null +++ b/packages/kbn-router-utils/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/router-utils", + "owner": "@elastic/obs-ux-logs-team" +} diff --git a/packages/kbn-router-utils/package.json b/packages/kbn-router-utils/package.json new file mode 100644 index 0000000000000..d84ddd2c06bc2 --- /dev/null +++ b/packages/kbn-router-utils/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/router-utils", + "private": true, + "version": "1.0.0", + "license": "SSPL-1.0 OR Elastic License 2.0" +} \ No newline at end of file diff --git a/x-pack/plugins/log_explorer/public/utils/get_router_link_props.ts b/packages/kbn-router-utils/src/get_router_link_props/index.ts similarity index 57% rename from x-pack/plugins/log_explorer/public/utils/get_router_link_props.ts rename to packages/kbn-router-utils/src/get_router_link_props/index.ts index a325df1a7e86f..f3def2e88650b 100644 --- a/x-pack/plugins/log_explorer/public/utils/get_router_link_props.ts +++ b/packages/kbn-router-utils/src/get_router_link_props/index.ts @@ -1,8 +1,9 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 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. */ interface GetRouterLinkPropsDeps { @@ -15,6 +16,18 @@ const isModifiedEvent = (event: React.MouseEvent) => const isLeftClickEvent = (event: React.MouseEvent) => event.button === 0; +/** + * + * getRouterLinkProps is an util that enable HTML elements, such buttons, to + * behave as links. + * @example + * const linkProps = getRouterLinkProps({ href: 'https://my-link', onClick: () => {console.log('click event')} }); + * My custom link + * @param href target url + * @param onClick onClick callback + * @returns An object that contains an href and a guardedClick handler that will + * manage behaviours such as leftClickEvent and event with modifiers (Ctrl, Shift, etc) + */ export const getRouterLinkProps = ({ href, onClick }: GetRouterLinkPropsDeps) => { const guardedClickHandler = (event: React.MouseEvent) => { if (event.defaultPrevented) { diff --git a/packages/kbn-router-utils/tsconfig.json b/packages/kbn-router-utils/tsconfig.json new file mode 100644 index 0000000000000..87f865132f4b4 --- /dev/null +++ b/packages/kbn-router-utils/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node", + "react" + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx", + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [] +} diff --git a/packages/kbn-rule-data-utils/package.json b/packages/kbn-rule-data-utils/package.json index bf1c9795a3671..2c7ca5f90f730 100644 --- a/packages/kbn-rule-data-utils/package.json +++ b/packages/kbn-rule-data-utils/package.json @@ -2,5 +2,6 @@ "name": "@kbn/rule-data-utils", "version": "1.0.0", "license": "SSPL-1.0 OR Elastic License 2.0", - "private": true + "private": true, + "sideEffects": false } \ No newline at end of file diff --git a/packages/kbn-rule-data-utils/src/alerts_as_data_rbac.ts b/packages/kbn-rule-data-utils/src/alerts_as_data_rbac.ts index 0427570b1cbc6..2b46eeab849f3 100644 --- a/packages/kbn-rule-data-utils/src/alerts_as_data_rbac.ts +++ b/packages/kbn-rule-data-utils/src/alerts_as_data_rbac.ts @@ -24,6 +24,7 @@ export const AlertConsumers = { SIEM: 'siem', UPTIME: 'uptime', ML: 'ml', + STACK_ALERTS: 'stackAlerts', } as const; export type AlertConsumers = typeof AlertConsumers[keyof typeof AlertConsumers]; export type STATUS_VALUES = 'open' | 'acknowledged' | 'closed' | 'in-progress'; // TODO: remove 'in-progress' after migration to 'acknowledged' diff --git a/packages/kbn-search-api-panels/components/cloud_details.tsx b/packages/kbn-search-api-panels/components/cloud_details.tsx new file mode 100644 index 0000000000000..25e7e660ee356 --- /dev/null +++ b/packages/kbn-search-api-panels/components/cloud_details.tsx @@ -0,0 +1,151 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React, { useState } from 'react'; +import { + EuiCheckableCard, + EuiCodeBlock, + EuiFlexGroup, + EuiFlexItem, + EuiPanel, + EuiSpacer, + EuiText, + EuiThemeProvider, + EuiTitle, + EuiBadge, + EuiPanelProps, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { css } from '@emotion/react'; +import { OverviewPanel } from '..'; +import { ELASTICSEARCH_URL_PLACEHOLDER } from '../constants'; + +export interface CloudDetailsPanelProps { + cloudId?: string; + elasticsearchUrl?: string; + isPanelLeft?: boolean; + overviewPanelProps?: Partial; +} + +enum CloudDetail { + ElasticsearchEndpoint = 'es_url', + CloudId = 'cloud_id', +} + +export const CloudDetailsPanel = ({ + cloudId, + elasticsearchUrl = ELASTICSEARCH_URL_PLACEHOLDER, + isPanelLeft = true, + overviewPanelProps, +}: CloudDetailsPanelProps) => { + const [selectedDetail, setSelectedCloudDetail] = useState( + CloudDetail.ElasticsearchEndpoint + ); + const panelContent = ( + + + + {selectedDetail === CloudDetail.CloudId && cloudId} + {selectedDetail === CloudDetail.ElasticsearchEndpoint && elasticsearchUrl} + + + + ); + return ( + + + + + +
+ +
+
+
+ + + + + + + + + } + checked={selectedDetail === CloudDetail.ElasticsearchEndpoint} + onChange={() => setSelectedCloudDetail(CloudDetail.ElasticsearchEndpoint)} + > + +

+ +

+
+
+ + {Boolean(cloudId) && ( + +
+ +
+ + } + checked={selectedDetail === CloudDetail.CloudId} + onChange={() => setSelectedCloudDetail(CloudDetail.CloudId)} + > + +

+ +

+
+
+ )} +
+ ); +}; diff --git a/x-pack/plugins/serverless_search/public/application/components/pipeline_panel.tsx b/packages/kbn-search-api-panels/components/pipeline_panel.tsx similarity index 54% rename from x-pack/plugins/serverless_search/public/application/components/pipeline_panel.tsx rename to packages/kbn-search-api-panels/components/pipeline_panel.tsx index f5b11247b3395..4ee6d6a7f0003 100644 --- a/x-pack/plugins/serverless_search/public/application/components/pipeline_panel.tsx +++ b/packages/kbn-search-api-panels/components/pipeline_panel.tsx @@ -1,8 +1,9 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ import React from 'react'; @@ -19,11 +20,17 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { useAssetBasePath } from '../hooks/use_asset_base_path'; - -export const PipelinePanel: React.FC = () => { - const assetBasePath = useAssetBasePath(); +interface PipelinePanelProps { + clusterImage: string; + cutImage: string; + reporterImage: string; +} +export const PipelinePanel: React.FC = ({ + clusterImage, + cutImage, + reporterImage, +}) => { return ( @@ -31,24 +38,21 @@ export const PipelinePanel: React.FC = () => { - +

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

{i18n.translate( - 'xpack.serverlessSearch.pipeline.overview.dataEnrichment.description', + 'searchApiPanels.pipeline.overview.dataEnrichment.description', { defaultMessage: 'Add information from external sources or apply transformations to your documents for more contextual, insightful search.', @@ -62,28 +66,22 @@ export const PipelinePanel: React.FC = () => { - +

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

- {i18n.translate( - 'xpack.serverlessSearch.pipeline.overview.extAndStandard.description', - { - defaultMessage: - 'Parse information from your documents to ensure they conform to a standardized format.', - } - )} + {i18n.translate('searchApiPanels.pipeline.overview.extAndStandard.description', { + defaultMessage: + 'Parse information from your documents to ensure they conform to a standardized format.', + })}
@@ -91,28 +89,21 @@ export const PipelinePanel: React.FC = () => { - +

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

- {i18n.translate( - 'xpack.serverlessSearch.pipeline.overview.anonymization.description', - { - defaultMessage: - 'Remove sensitive information from documents before indexing.', - } - )} + {i18n.translate('searchApiPanels.pipeline.overview.anonymization.description', { + defaultMessage: 'Remove sensitive information from documents before indexing.', + })}
diff --git a/packages/kbn-search-api-panels/index.tsx b/packages/kbn-search-api-panels/index.tsx index 6e78a0b388555..ada194f6fab46 100644 --- a/packages/kbn-search-api-panels/index.tsx +++ b/packages/kbn-search-api-panels/index.tsx @@ -11,12 +11,14 @@ import { EuiFlexGroup, EuiFlexItem, EuiTitle, EuiSpacer, EuiImage, EuiText } fro import { i18n } from '@kbn/i18n'; import { AuthenticatedUser } from '@kbn/security-plugin/common'; +export * from './components/cloud_details'; export * from './components/code_box'; export * from './components/github_link'; export * from './components/ingest_data'; export * from './components/ingestions_panel'; export * from './components/language_client_panel'; export * from './components/overview_panel'; +export * from './components/pipeline_panel'; export * from './components/select_client'; export * from './components/try_in_console_button'; export * from './components/install_client'; diff --git a/packages/kbn-search-connectors/components/configuration/connector_configuration_form.tsx b/packages/kbn-search-connectors/components/configuration/connector_configuration_form.tsx index f52add9bce020..af258a807788d 100644 --- a/packages/kbn-search-connectors/components/configuration/connector_configuration_form.tsx +++ b/packages/kbn-search-connectors/components/configuration/connector_configuration_form.tsx @@ -66,12 +66,12 @@ export const ConnectorConfigurationForm: React.FC = ); useEffect(() => { - setConfigView(sortAndFilterConnectorConfiguration(localConfig, isNative)); - }, [localConfig, isNative]); + setLocalConfig((localConf) => ({ ...configuration, ...localConf })); + }, [configuration]); useEffect(() => { - setLocalConfig(configuration); - }, [configuration]); + setConfigView(sortAndFilterConnectorConfiguration(localConfig, isNative)); + }, [localConfig, isNative]); return ( /packages/kbn-search-errors'], +}; diff --git a/packages/kbn-search-errors/kibana.jsonc b/packages/kbn-search-errors/kibana.jsonc new file mode 100644 index 0000000000000..9d4e7e2472b9d --- /dev/null +++ b/packages/kbn-search-errors/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/search-errors", + "owner": "@elastic/kibana-data-discovery" +} diff --git a/packages/kbn-search-errors/package.json b/packages/kbn-search-errors/package.json new file mode 100644 index 0000000000000..7b7dfe1bf0290 --- /dev/null +++ b/packages/kbn-search-errors/package.json @@ -0,0 +1,6 @@ +{ + "name": "@kbn/search-errors", + "private": true, + "version": "1.0.0", + "license": "SSPL-1.0 OR Elastic License 2.0" +} \ No newline at end of file diff --git a/src/plugins/data/public/search/errors/es_error.test.tsx b/packages/kbn-search-errors/src/es_error.test.tsx similarity index 100% rename from src/plugins/data/public/search/errors/es_error.test.tsx rename to packages/kbn-search-errors/src/es_error.test.tsx diff --git a/src/plugins/data/public/search/errors/es_error.tsx b/packages/kbn-search-errors/src/es_error.tsx similarity index 66% rename from src/plugins/data/public/search/errors/es_error.tsx rename to packages/kbn-search-errors/src/es_error.tsx index f5b978b38bc1e..8a5ab1ad3c503 100644 --- a/src/plugins/data/public/search/errors/es_error.tsx +++ b/packages/kbn-search-errors/src/es_error.tsx @@ -9,22 +9,32 @@ import React from 'react'; import { EuiButton, EuiCodeBlock, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { ApplicationStart } from '@kbn/core/public'; -import { KbnError } from '@kbn/kibana-utils-plugin/common'; +import type { ApplicationStart } from '@kbn/core/public'; import { IEsError } from './types'; import { getRootCause } from './utils'; -export class EsError extends KbnError { +/** + * Checks if a given errors originated from Elasticsearch. + * Those params are assigned to the attributes property of an error. + * + * @param e + */ +export function isEsError(e: any): e is IEsError { + return !!e.attributes; +} + +export class EsError extends Error { readonly attributes: IEsError['attributes']; constructor(protected readonly err: IEsError, private readonly openInInspector: () => void) { super( `EsError: ${ getRootCause(err?.attributes?.error)?.reason || - i18n.translate('data.esError.unknownRootCause', { defaultMessage: 'unknown' }) + i18n.translate('searchErrors.esError.unknownRootCause', { defaultMessage: 'unknown' }) }` ); this.attributes = err.attributes; + Object.setPrototypeOf(this, new.target.prototype); } public getErrorMessage() { @@ -48,8 +58,14 @@ export class EsError extends KbnError { public getActions(application: ApplicationStart) { return [ - - {i18n.translate('data.esError.viewDetailsButtonLabel', { + + {i18n.translate('searchErrors.esError.viewDetailsButtonLabel', { defaultMessage: 'View details', })} , diff --git a/src/plugins/data/public/search/errors/painless_error.test.tsx b/packages/kbn-search-errors/src/painless_error.test.tsx similarity index 60% rename from src/plugins/data/public/search/errors/painless_error.test.tsx rename to packages/kbn-search-errors/src/painless_error.test.tsx index 03d4180b2aaac..25787eb650bd9 100644 --- a/src/plugins/data/public/search/errors/painless_error.test.tsx +++ b/packages/kbn-search-errors/src/painless_error.test.tsx @@ -12,7 +12,53 @@ const startMock = coreMock.createStart(); import { mount } from 'enzyme'; import { PainlessError } from './painless_error'; import { findTestSubject } from '@elastic/eui/lib/test'; -import * as searchPhaseException from '../../../common/search/test_data/search_phase_execution_exception.json'; + +const searchPhaseException = { + error: { + root_cause: [ + { + type: 'script_exception', + reason: 'compile error', + script_stack: ['invalid', '^---- HERE'], + script: 'invalid', + lang: 'painless', + position: { + offset: 0, + start: 0, + end: 7, + }, + }, + ], + type: 'search_phase_execution_exception', + reason: 'all shards failed', + phase: 'query', + grouped: true, + failed_shards: [ + { + shard: 0, + index: '.kibana_11', + node: 'b3HX8C96Q7q1zgfVLxEsPA', + reason: { + type: 'script_exception', + reason: 'compile error', + script_stack: ['invalid', '^---- HERE'], + script: 'invalid', + lang: 'painless', + position: { + offset: 0, + start: 0, + end: 7, + }, + caused_by: { + type: 'illegal_argument_exception', + reason: 'cannot resolve symbol [invalid]', + }, + }, + }, + ], + }, + status: 400, +}; describe('PainlessError', () => { beforeEach(() => { diff --git a/src/plugins/data/public/search/errors/painless_error.tsx b/packages/kbn-search-errors/src/painless_error.tsx similarity index 91% rename from src/plugins/data/public/search/errors/painless_error.tsx rename to packages/kbn-search-errors/src/painless_error.tsx index e76adddfb0815..08657ea134ab6 100644 --- a/src/plugins/data/public/search/errors/painless_error.tsx +++ b/packages/kbn-search-errors/src/painless_error.tsx @@ -9,10 +9,10 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButtonEmpty, EuiSpacer, EuiText, EuiCodeBlock } from '@elastic/eui'; -import { ApplicationStart } from '@kbn/core/public'; +import type { ApplicationStart } from '@kbn/core/public'; import type { DataView } from '@kbn/data-views-plugin/common'; -import { IEsError, isEsError } from './types'; -import { EsError } from './es_error'; +import type { IEsError } from './types'; +import { EsError, isEsError } from './es_error'; import { getRootCause } from './utils'; export class PainlessError extends EsError { @@ -37,7 +37,7 @@ export class PainlessError extends EsError { return ( <> - {i18n.translate('data.painlessError.painlessScriptedFieldErrorMessage', { + {i18n.translate('searchErrors.painlessError.painlessScriptedFieldErrorMessage', { defaultMessage: 'Error executing runtime field or scripted field on index pattern {indexPatternName}', values: { @@ -73,7 +73,7 @@ export class PainlessError extends EsError { onClick={() => onClick(this?.indexPattern?.id)} size="s" > - {i18n.translate('data.painlessError.buttonTxt', { + {i18n.translate('searchErrors.painlessError.buttonTxt', { defaultMessage: 'Edit script', })} diff --git a/src/plugins/data/public/search/search_interceptor/utils.ts b/packages/kbn-search-errors/src/render_search_error.ts similarity index 63% rename from src/plugins/data/public/search/search_interceptor/utils.ts rename to packages/kbn-search-errors/src/render_search_error.ts index f31e816de5267..73dd298877cca 100644 --- a/src/plugins/data/public/search/search_interceptor/utils.ts +++ b/packages/kbn-search-errors/src/render_search_error.ts @@ -6,19 +6,13 @@ * Side Public License, v 1. */ -import stringify from 'json-stable-stringify'; -import { Sha256 } from '@kbn/crypto-browser'; import { i18n } from '@kbn/i18n'; import { ReactNode } from 'react'; -import { BfetchRequestError } from '@kbn/bfetch-plugin/public'; -import { ApplicationStart } from '@kbn/core-application-browser'; -import { EsError } from '../errors'; +import { BfetchRequestError } from '@kbn/bfetch-error'; +import type { ApplicationStart } from '@kbn/core-application-browser'; +import { EsError } from './es_error'; -export async function createRequestHash(keys: Record) { - return new Sha256().update(stringify(keys), 'utf8').digest('hex'); -} - -export function getSearchErrorOverrideDisplay({ +export function renderSearchError({ error, application, }: { @@ -27,7 +21,7 @@ export function getSearchErrorOverrideDisplay({ }): { title: string; body: ReactNode; actions?: ReactNode[] } | undefined { if (error instanceof EsError) { return { - title: i18n.translate('data.search.esErrorTitle', { + title: i18n.translate('searchErrors.search.esErrorTitle', { defaultMessage: 'Cannot retrieve search results', }), body: error.getErrorMessage(), @@ -36,12 +30,12 @@ export function getSearchErrorOverrideDisplay({ } if (error.constructor.name === 'HttpFetchError' || error instanceof BfetchRequestError) { - const defaultMsg = i18n.translate('data.errors.fetchError', { + const defaultMsg = i18n.translate('searchErrors.errors.fetchError', { defaultMessage: 'Check your network connection and try again.', }); return { - title: i18n.translate('data.search.httpErrorTitle', { + title: i18n.translate('searchErrors.search.httpErrorTitle', { defaultMessage: 'Unable to connect to the Kibana server', }), body: error.message || defaultMsg, diff --git a/packages/kbn-search-errors/src/types.ts b/packages/kbn-search-errors/src/types.ts new file mode 100644 index 0000000000000..085d5961a2984 --- /dev/null +++ b/packages/kbn-search-errors/src/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 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { estypes } from '@elastic/elasticsearch'; +import type { ConnectionRequestParams } from '@elastic/transport'; +import type { KibanaServerError } from '@kbn/kibana-utils-plugin/common'; + +type SanitizedConnectionRequestParams = Pick< + ConnectionRequestParams, + 'method' | 'path' | 'querystring' +>; + +interface IEsErrorAttributes { + error?: estypes.ErrorCause; + rawResponse?: estypes.SearchResponseBody; + requestParams?: SanitizedConnectionRequestParams; +} + +export type IEsError = KibanaServerError; diff --git a/src/plugins/data/public/search/errors/utils.ts b/packages/kbn-search-errors/src/utils.ts similarity index 100% rename from src/plugins/data/public/search/errors/utils.ts rename to packages/kbn-search-errors/src/utils.ts diff --git a/packages/kbn-search-errors/tsconfig.json b/packages/kbn-search-errors/tsconfig.json new file mode 100644 index 0000000000000..7c50a64bd11ee --- /dev/null +++ b/packages/kbn-search-errors/tsconfig.json @@ -0,0 +1,26 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types", + "types": [ + "jest", + "node", + "react" + ] + }, + "include": [ + "**/*.ts", + "**/*.tsx", + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/i18n", + "@kbn/core", + "@kbn/kibana-utils-plugin", + "@kbn/data-views-plugin", + "@kbn/core-application-browser", + "@kbn/bfetch-error", + ] +} diff --git a/packages/kbn-search-index-documents/lib/fetch_search_results.test.ts b/packages/kbn-search-index-documents/lib/fetch_search_results.test.ts index dc60dcba437bd..dec058aae0bf2 100644 --- a/packages/kbn-search-index-documents/lib/fetch_search_results.test.ts +++ b/packages/kbn-search-index-documents/lib/fetch_search_results.test.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { IScopedClusterClient } from '@kbn/core/server'; +import { ElasticsearchClient } from '@kbn/core/server'; import { DEFAULT_DOCS_PER_PAGE } from '../types'; import { fetchSearchResults } from './fetch_search_results'; @@ -14,9 +14,7 @@ import { fetchSearchResults } from './fetch_search_results'; const DEFAULT_FROM_VALUE = 0; describe('fetchSearchResults lib function', () => { const mockClient = { - asCurrentUser: { - search: jest.fn(), - }, + search: jest.fn(), }; const indexName = 'search-regular-index'; @@ -78,15 +76,13 @@ describe('fetchSearchResults lib function', () => { }); it('should return search results with hits', async () => { - mockClient.asCurrentUser.search.mockImplementation(() => - Promise.resolve(mockSearchResponseWithHits) - ); + mockClient.search.mockImplementation(() => Promise.resolve(mockSearchResponseWithHits)); await expect( - fetchSearchResults(mockClient as unknown as IScopedClusterClient, indexName, query) + fetchSearchResults(mockClient as unknown as ElasticsearchClient, indexName, query) ).resolves.toEqual(regularSearchResultsResponse); - expect(mockClient.asCurrentUser.search).toHaveBeenCalledWith({ + expect(mockClient.search).toHaveBeenCalledWith({ from: DEFAULT_FROM_VALUE, index: indexName, q: query, @@ -95,13 +91,11 @@ describe('fetchSearchResults lib function', () => { }); it('should escape quotes in queries and return results with hits', async () => { - mockClient.asCurrentUser.search.mockImplementation(() => - Promise.resolve(mockSearchResponseWithHits) - ); + mockClient.search.mockImplementation(() => Promise.resolve(mockSearchResponseWithHits)); await expect( fetchSearchResults( - mockClient as unknown as IScopedClusterClient, + mockClient as unknown as ElasticsearchClient, indexName, '"yellow banana"', 0, @@ -109,7 +103,7 @@ describe('fetchSearchResults lib function', () => { ) ).resolves.toEqual(regularSearchResultsResponse); - expect(mockClient.asCurrentUser.search).toHaveBeenCalledWith({ + expect(mockClient.search).toHaveBeenCalledWith({ from: DEFAULT_FROM_VALUE, index: indexName, q: '\\"yellow banana\\"', @@ -118,15 +112,13 @@ describe('fetchSearchResults lib function', () => { }); it('should return search results with hits when no query is passed', async () => { - mockClient.asCurrentUser.search.mockImplementation(() => - Promise.resolve(mockSearchResponseWithHits) - ); + mockClient.search.mockImplementation(() => Promise.resolve(mockSearchResponseWithHits)); await expect( - fetchSearchResults(mockClient as unknown as IScopedClusterClient, indexName) + fetchSearchResults(mockClient as unknown as ElasticsearchClient, indexName) ).resolves.toEqual(regularSearchResultsResponse); - expect(mockClient.asCurrentUser.search).toHaveBeenCalledWith({ + expect(mockClient.search).toHaveBeenCalledWith({ from: DEFAULT_FROM_VALUE, index: indexName, size: DEFAULT_DOCS_PER_PAGE, @@ -134,7 +126,7 @@ describe('fetchSearchResults lib function', () => { }); it('should return empty search results', async () => { - mockClient.asCurrentUser.search.mockImplementationOnce(() => + mockClient.search.mockImplementationOnce(() => Promise.resolve({ ...mockSearchResponseWithHits, hits: { @@ -149,10 +141,10 @@ describe('fetchSearchResults lib function', () => { ); await expect( - fetchSearchResults(mockClient as unknown as IScopedClusterClient, indexName, query) + fetchSearchResults(mockClient as unknown as ElasticsearchClient, indexName, query) ).resolves.toEqual(emptySearchResultsResponse); - expect(mockClient.asCurrentUser.search).toHaveBeenCalledWith({ + expect(mockClient.search).toHaveBeenCalledWith({ from: DEFAULT_FROM_VALUE, index: indexName, q: query, diff --git a/packages/kbn-search-index-documents/lib/fetch_search_results.ts b/packages/kbn-search-index-documents/lib/fetch_search_results.ts index 4427fded71c2c..3abca93516c5a 100644 --- a/packages/kbn-search-index-documents/lib/fetch_search_results.ts +++ b/packages/kbn-search-index-documents/lib/fetch_search_results.ts @@ -7,13 +7,13 @@ */ import { SearchHit } from '@elastic/elasticsearch/lib/api/types'; -import { IScopedClusterClient } from '@kbn/core/server'; +import { ElasticsearchClient } from '@kbn/core/server'; import { DEFAULT_DOCS_PER_PAGE, Paginate } from '../types'; import { escapeLuceneChars } from '../utils/escape_lucene_charts'; import { fetchWithPagination } from '../utils/fetch_with_pagination'; export const fetchSearchResults = async ( - client: IScopedClusterClient, + client: ElasticsearchClient, indexName: string, query?: string, from: number = 0, @@ -21,7 +21,7 @@ export const fetchSearchResults = async ( ): Promise> => { const result = await fetchWithPagination( async () => - await client.asCurrentUser.search({ + await client.search({ from, index: indexName, size, diff --git a/packages/kbn-search-index-documents/package.json b/packages/kbn-search-index-documents/package.json index ad9afa66438a4..5c788d7c8eef3 100644 --- a/packages/kbn-search-index-documents/package.json +++ b/packages/kbn-search-index-documents/package.json @@ -2,5 +2,8 @@ "name": "@kbn/search-index-documents", "private": true, "version": "1.0.0", - "license": "SSPL-1.0 OR Elastic License 2.0" + "license": "SSPL-1.0 OR Elastic License 2.0", + "sideEffects": [ + "*.scss" + ] } \ No newline at end of file diff --git a/packages/kbn-search-response-warnings/package.json b/packages/kbn-search-response-warnings/package.json index 69ccd790806aa..c910f7b1fdd30 100644 --- a/packages/kbn-search-response-warnings/package.json +++ b/packages/kbn-search-response-warnings/package.json @@ -2,5 +2,8 @@ "name": "@kbn/search-response-warnings", "private": true, "version": "1.0.0", - "license": "SSPL-1.0 OR Elastic License 2.0" + "license": "SSPL-1.0 OR Elastic License 2.0", + "sideEffects": [ + "*.scss" + ] } \ No newline at end of file diff --git a/packages/kbn-securitysolution-autocomplete/package.json b/packages/kbn-securitysolution-autocomplete/package.json index 33bcb4e05206e..3f667881758fc 100644 --- a/packages/kbn-securitysolution-autocomplete/package.json +++ b/packages/kbn-securitysolution-autocomplete/package.json @@ -3,5 +3,6 @@ "version": "1.0.0", "description": "Security Solution auto complete", "license": "SSPL-1.0 OR Elastic License 2.0", - "private": true + "private": true, + "sideEffects": false } \ No newline at end of file diff --git a/packages/kbn-securitysolution-autocomplete/src/field/__tests__/__snapshots__/index.test.tsx.snap b/packages/kbn-securitysolution-autocomplete/src/field/__tests__/__snapshots__/index.test.tsx.snap index 8cc75ec2beab8..1a4c9076a20d6 100644 --- a/packages/kbn-securitysolution-autocomplete/src/field/__tests__/__snapshots__/index.test.tsx.snap +++ b/packages/kbn-securitysolution-autocomplete/src/field/__tests__/__snapshots__/index.test.tsx.snap @@ -10,90 +10,7 @@ Object { data-test-subj="fieldAutocompleteComboBox" >
-
-
-
-
- - - machine.os.raw - - - -
-
- - -
-
-
-
-
-
-
- , - "container":
-
-
-
- - - machine.os.raw - -
+ , + "container":
+
+
+
+
+
+ +
+
+ + +
+
+
+
+
, "debug": [Function], "findAllByAltText": [Function], @@ -227,46 +199,32 @@ Object { data-test-subj="fieldAutocompleteComboBox" >
-
- - - machine.os.raw - - - -
+
@@ -280,46 +238,32 @@ Object { data-test-subj="fieldAutocompleteComboBox" >
-
- - - machine.os.raw - - - -
+
@@ -390,79 +334,7 @@ Object { data-test-subj="fieldAutocompleteComboBox" >
-
-
-
-
- - - machine.os.raw - - - -
-
- -
-
-
-
-
-
-
- , - "container":
-
-
-
- - - machine.os.raw - -
+ , + "container":
+
+
+
+
+
+ +
+
+ +
+
+
+
+
, "debug": [Function], "findAllByAltText": [Function], @@ -585,72 +501,7 @@ Object { data-test-subj="fieldAutocompleteComboBox" >
-
-
-
-
- - - machine.os.raw - - - -
-
- -
-
-
-
-
-
-
- , - "container":
-
-
-
- - - machine.os.raw - -
+ , + "container":
+
+
+
+
+
+ +
+
+ +
+
+
+
+
, "debug": [Function], "findAllByAltText": [Function], diff --git a/packages/kbn-securitysolution-autocomplete/src/field/__tests__/index.test.tsx b/packages/kbn-securitysolution-autocomplete/src/field/__tests__/index.test.tsx index 48f7a109646f0..9ac7d9e58ee8b 100644 --- a/packages/kbn-securitysolution-autocomplete/src/field/__tests__/index.test.tsx +++ b/packages/kbn-securitysolution-autocomplete/src/field/__tests__/index.test.tsx @@ -7,7 +7,7 @@ */ import React from 'react'; -import { fireEvent, render, waitFor } from '@testing-library/react'; +import { fireEvent, render, waitFor, within } from '@testing-library/react'; import '@testing-library/jest-dom'; import { FieldComponent } from '..'; @@ -31,7 +31,9 @@ describe('FieldComponent', () => { /> ); expect(wrapper).toMatchSnapshot(); - expect(wrapper.getByTestId('fieldAutocompleteComboBox')).toHaveTextContent('machine.os.raw'); + const comboBox = wrapper.getByTestId('fieldAutocompleteComboBox'); + const input = within(comboBox).getByRole('combobox'); + expect(input).toHaveAttribute('value', 'machine.os.raw'); }); it('should render the component disabled if isDisabled is true', () => { const wrapper = render( diff --git a/packages/kbn-securitysolution-autocomplete/src/field_value_lists/index.test.tsx b/packages/kbn-securitysolution-autocomplete/src/field_value_lists/index.test.tsx index acd1a315d2c48..733e111fb4dd5 100644 --- a/packages/kbn-securitysolution-autocomplete/src/field_value_lists/index.test.tsx +++ b/packages/kbn-securitysolution-autocomplete/src/field_value_lists/index.test.tsx @@ -182,9 +182,9 @@ describe('AutocompleteFieldListsComponent', () => { expect( wrapper - .find(`[data-test-subj="valuesAutocompleteComboBox listsComboxBox"] EuiComboBoxPill`) + .find(`[data-test-subj="valuesAutocompleteComboBox listsComboxBox"] input`) .at(0) - .text() + .props().value ).toEqual('some name'); }); diff --git a/packages/kbn-securitysolution-autocomplete/src/field_value_lists/index.tsx b/packages/kbn-securitysolution-autocomplete/src/field_value_lists/index.tsx index 8a57f90e75ecc..a7b621979a5a8 100644 --- a/packages/kbn-securitysolution-autocomplete/src/field_value_lists/index.tsx +++ b/packages/kbn-securitysolution-autocomplete/src/field_value_lists/index.tsx @@ -125,7 +125,12 @@ export const AutocompleteFieldListsComponent: React.FC {i18n.SEE_DOCUMENTATION} diff --git a/packages/kbn-securitysolution-autocomplete/src/field_value_match/index.test.tsx b/packages/kbn-securitysolution-autocomplete/src/field_value_match/index.test.tsx index 24bb72c3058ac..5d4f8cd89f1cc 100644 --- a/packages/kbn-securitysolution-autocomplete/src/field_value_match/index.test.tsx +++ b/packages/kbn-securitysolution-autocomplete/src/field_value_match/index.test.tsx @@ -166,7 +166,7 @@ describe('AutocompleteFieldMatchComponent', () => { ); expect( - wrapper.find('[data-test-subj="valuesAutocompleteMatch"] EuiComboBoxPill').at(0).text() + wrapper.find('[data-test-subj="valuesAutocompleteMatch"] input').at(0).props().value ).toEqual('127.0.0.1'); }); diff --git a/packages/kbn-securitysolution-autocomplete/src/field_value_wildcard/index.test.tsx b/packages/kbn-securitysolution-autocomplete/src/field_value_wildcard/index.test.tsx index 9eab42f5cec73..ec442385a1fd3 100644 --- a/packages/kbn-securitysolution-autocomplete/src/field_value_wildcard/index.test.tsx +++ b/packages/kbn-securitysolution-autocomplete/src/field_value_wildcard/index.test.tsx @@ -168,7 +168,7 @@ describe('AutocompleteFieldWildcardComponent', () => { ); expect( - wrapper.find('[data-test-subj="valuesAutocompleteWildcard"] EuiComboBoxPill').at(0).text() + wrapper.find('[data-test-subj="valuesAutocompleteWildcard"] input').at(0).props().value ).toEqual('/opt/*/app.dmg'); }); diff --git a/packages/kbn-securitysolution-autocomplete/src/operator/index.test.tsx b/packages/kbn-securitysolution-autocomplete/src/operator/index.test.tsx index 954b14a8ea244..21ed99dce4b72 100644 --- a/packages/kbn-securitysolution-autocomplete/src/operator/index.test.tsx +++ b/packages/kbn-securitysolution-autocomplete/src/operator/index.test.tsx @@ -152,7 +152,7 @@ describe('operator', () => { ); expect( - wrapper.find(`[data-test-subj="operatorAutocompleteComboBox"] EuiComboBoxPill`).at(0).text() + wrapper.find('[data-test-subj="operatorAutocompleteComboBox"] input').at(0).props().value ).toEqual('is'); }); diff --git a/packages/kbn-securitysolution-ecs/package.json b/packages/kbn-securitysolution-ecs/package.json index 002c89dada984..d626893a7ef89 100644 --- a/packages/kbn-securitysolution-ecs/package.json +++ b/packages/kbn-securitysolution-ecs/package.json @@ -2,5 +2,6 @@ "name": "@kbn/securitysolution-ecs", "private": true, "version": "1.0.0", - "license": "SSPL-1.0 OR Elastic License 2.0" + "license": "SSPL-1.0 OR Elastic License 2.0", + "sideEffects": false } diff --git a/packages/kbn-securitysolution-es-utils/package.json b/packages/kbn-securitysolution-es-utils/package.json index 3083acffa0636..13965a346f1d7 100644 --- a/packages/kbn-securitysolution-es-utils/package.json +++ b/packages/kbn-securitysolution-es-utils/package.json @@ -3,5 +3,6 @@ "version": "1.0.0", "description": "security solution elastic search utilities to use across plugins such lists, security_solution, cases, etc...", "license": "SSPL-1.0 OR Elastic License 2.0", - "private": true + "private": true, + "sideEffects": false } \ No newline at end of file diff --git a/packages/kbn-securitysolution-exception-list-components/package.json b/packages/kbn-securitysolution-exception-list-components/package.json index 297b54d4a80a4..132b38574e894 100644 --- a/packages/kbn-securitysolution-exception-list-components/package.json +++ b/packages/kbn-securitysolution-exception-list-components/package.json @@ -2,5 +2,6 @@ "name": "@kbn/securitysolution-exception-list-components", "private": true, "version": "1.0.0", - "license": "SSPL-1.0 OR Elastic License 2.0" + "license": "SSPL-1.0 OR Elastic License 2.0", + "sideEffects": false } \ No newline at end of file diff --git a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/comments/__snapshots__/comments.test.tsx.snap b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/comments/__snapshots__/comments.test.tsx.snap index ff7dddd2da950..eecc554cd4e00 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/comments/__snapshots__/comments.test.tsx.snap +++ b/packages/kbn-securitysolution-exception-list-components/src/exception_item_card/comments/__snapshots__/comments.test.tsx.snap @@ -101,7 +101,7 @@ Object { class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isClosed" id="exceptionItemCardComments" inert="" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > @@ -239,7 +239,7 @@ Object { class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isClosed" id="exceptionItemCardComments" inert="" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > @@ -433,7 +433,7 @@ Object { aria-labelledby="generated-id" class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isOpen" id="exceptionItemCardComments" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > @@ -570,7 +570,7 @@ Object { aria-labelledby="generated-id" class="euiAccordion__childWrapper emotion-euiAccordion__childWrapper-isOpen" id="exceptionItemCardComments" - role="region" + role="group" style="block-size: 0;" tabindex="-1" > diff --git a/packages/kbn-securitysolution-exception-list-components/src/header_menu/__snapshots__/header_menu.test.tsx.snap b/packages/kbn-securitysolution-exception-list-components/src/header_menu/__snapshots__/header_menu.test.tsx.snap index dae6c05236001..5f87fe1383634 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/header_menu/__snapshots__/header_menu.test.tsx.snap +++ b/packages/kbn-securitysolution-exception-list-components/src/header_menu/__snapshots__/header_menu.test.tsx.snap @@ -9,40 +9,8 @@ Object { class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexStart-stretch-row" >
-
- -
-
-
-
- , - "container":
-
-
-
+ , + "container":
+
+
+ +
+
, "debug": [Function], "findAllByAltText": [Function], @@ -124,26 +116,22 @@ Object { class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexStart-stretch-row" >
-
- -
+
@@ -157,7 +145,7 @@ Object { aria-describedby="generated-id" aria-live="off" aria-modal="true" - class="euiPanel euiPanel--plain euiPanel--paddingSmall euiPopover__panel emotion-euiPanel-grow-m-s-plain-euiPopover__panel-hasTransform" + class="euiPanel euiPanel--plain euiPanel--paddingSmall euiPopover__panel emotion-euiPanel-grow-m-s-plain-euiPopover__panel-light-hasTransform" data-popover-panel="true" role="dialog" style="top: 16px; left: -22px; will-change: transform, opacity; z-index: 2000;" @@ -226,26 +214,22 @@ Object { class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexStart-stretch-row" >
-
- -
+
, @@ -312,40 +296,8 @@ Object { class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexStart-stretch-row" >
-
- -
-
-
-
- , - "container":
-
-
-
+ , + "container":
+
+
+ +
+
, "debug": [Function], "findAllByAltText": [Function], @@ -427,45 +403,8 @@ Object { class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexStart-stretch-row" >
-
- -
-
-
-
- , - "container":
-
-
-
+ , + "container":
+
+
+ +
+
, "debug": [Function], "findAllByAltText": [Function], @@ -552,45 +520,8 @@ Object { class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexStart-stretch-row" >
-
- -
-
-
-
- , - "container":
-
-
-
+ , + "container":
+
+
+ +
+
, "debug": [Function], "findAllByAltText": [Function], @@ -677,46 +637,8 @@ Object { class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexStart-stretch-row" >
-
- -
-
-
-
- , - "container":
-
-
-
+ , + "container":
+
+
+ +
+
, "debug": [Function], "findAllByAltText": [Function], @@ -804,45 +756,8 @@ Object { class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexStart-stretch-row" >
-
- -
-
-
-
- , - "container":
-
-
-
+ , + "container":
+
+
+ +
+
, "debug": [Function], "findAllByAltText": [Function], diff --git a/packages/kbn-securitysolution-exception-list-components/src/list_header/__snapshots__/list_header.test.tsx.snap b/packages/kbn-securitysolution-exception-list-components/src/list_header/__snapshots__/list_header.test.tsx.snap index a458aa90dbedc..c83f098c6d090 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/list_header/__snapshots__/list_header.test.tsx.snap +++ b/packages/kbn-securitysolution-exception-list-components/src/list_header/__snapshots__/list_header.test.tsx.snap @@ -197,26 +197,22 @@ Object { class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexStart-stretch-row" >
-
- -
+
@@ -584,26 +580,22 @@ Object { class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexStart-stretch-row" >
-
- -
+
@@ -867,26 +859,22 @@ Object { class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexStart-stretch-row" >
-
- -
+
@@ -1093,26 +1081,22 @@ Object { class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexStart-stretch-row" >
-
- -
+
@@ -1348,27 +1332,23 @@ Object { class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexStart-stretch-row" >
-
- -
+
@@ -1547,27 +1527,23 @@ Object { class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexStart-stretch-row" >
-
- -
+
@@ -1784,26 +1760,22 @@ Object { class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexStart-stretch-row" >
-
- -
+
@@ -1826,7 +1798,7 @@ Object { aria-describedby="generated-id" aria-live="off" aria-modal="true" - class="euiPanel euiPanel--plain euiPanel--paddingSmall euiPopover__panel emotion-euiPanel-grow-m-s-plain-euiPopover__panel-hasTransform" + class="euiPanel euiPanel--plain euiPanel--paddingSmall euiPopover__panel emotion-euiPanel-grow-m-s-plain-euiPopover__panel-light-hasTransform" data-popover-panel="true" role="dialog" style="top: 16px; left: -22px; will-change: transform, opacity; z-index: 2000;" @@ -2053,26 +2025,22 @@ Object { class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexStart-stretch-row" >
-
- -
+
diff --git a/packages/kbn-securitysolution-exception-list-components/src/list_header/menu_items/__snapshots__/menu_items.test.tsx.snap b/packages/kbn-securitysolution-exception-list-components/src/list_header/menu_items/__snapshots__/menu_items.test.tsx.snap index 90c90a4f3eea5..a58f4a042bca3 100644 --- a/packages/kbn-securitysolution-exception-list-components/src/list_header/menu_items/__snapshots__/menu_items.test.tsx.snap +++ b/packages/kbn-securitysolution-exception-list-components/src/list_header/menu_items/__snapshots__/menu_items.test.tsx.snap @@ -16,33 +16,29 @@ Object { class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexStart-stretch-row" >
-
- -
+ + +
@@ -53,26 +49,22 @@ Object { class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexStart-stretch-row" >
-
- -
+
@@ -91,33 +83,29 @@ Object { class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexStart-stretch-row" >
-
- -
+ + +
@@ -128,26 +116,22 @@ Object { class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexStart-stretch-row" >
-
- -
+
@@ -252,26 +236,22 @@ Object { class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexStart-stretch-row" >
-
- -
+
@@ -319,26 +299,22 @@ Object { class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexStart-stretch-row" >
-
- -
+
@@ -443,26 +419,22 @@ Object { class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexStart-stretch-row" >
-
- -
+
@@ -478,7 +450,7 @@ Object { aria-describedby="generated-id" aria-live="off" aria-modal="true" - class="euiPanel euiPanel--plain euiPanel--paddingSmall euiPopover__panel emotion-euiPanel-grow-m-s-plain-euiPopover__panel-hasTransform" + class="euiPanel euiPanel--plain euiPanel--paddingSmall euiPopover__panel emotion-euiPanel-grow-m-s-plain-euiPopover__panel-light-hasTransform" data-popover-panel="true" role="dialog" style="top: 16px; left: -22px; will-change: transform, opacity; z-index: 2000;" @@ -598,26 +570,22 @@ Object { class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexStart-stretch-row" >
-
- -
+
@@ -693,33 +661,29 @@ Object { class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexStart-stretch-row" >
-
- -
+ + +
@@ -730,26 +694,22 @@ Object { class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexStart-stretch-row" >
-
- -
+
@@ -765,7 +725,7 @@ Object { aria-describedby="generated-id" aria-live="off" aria-modal="true" - class="euiPanel euiPanel--plain euiPanel--paddingSmall euiPopover__panel emotion-euiPanel-grow-m-s-plain-euiPopover__panel-hasTransform" + class="euiPanel euiPanel--plain euiPanel--paddingSmall euiPopover__panel emotion-euiPanel-grow-m-s-plain-euiPopover__panel-light-hasTransform" data-popover-panel="true" role="dialog" style="top: 16px; left: -22px; will-change: transform, opacity; z-index: 2000;" @@ -858,33 +818,29 @@ Object { class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexStart-stretch-row" >
-
- -
+ + +
@@ -895,26 +851,22 @@ Object { class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexStart-stretch-row" >
-
- -
+
@@ -990,33 +942,29 @@ Object { class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexStart-stretch-row" >
-
- -
+ + +
@@ -1046,26 +994,22 @@ Object { class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexStart-stretch-row" >
-
- -
+
@@ -1084,33 +1028,29 @@ Object { class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexStart-stretch-row" >
-
- -
+ + +
@@ -1140,26 +1080,22 @@ Object { class="euiFlexGroup emotion-euiFlexGroup-responsive-l-flexStart-stretch-row" >
-
- -
+
diff --git a/packages/kbn-securitysolution-grouping/package.json b/packages/kbn-securitysolution-grouping/package.json index e5baa64c3a1f4..8d54527636296 100644 --- a/packages/kbn-securitysolution-grouping/package.json +++ b/packages/kbn-securitysolution-grouping/package.json @@ -2,5 +2,6 @@ "name": "@kbn/securitysolution-grouping", "private": true, "version": "1.0.0", - "license": "SSPL-1.0 OR Elastic License 2.0" + "license": "SSPL-1.0 OR Elastic License 2.0", + "sideEffects": false } \ No newline at end of file diff --git a/packages/kbn-securitysolution-hook-utils/package.json b/packages/kbn-securitysolution-hook-utils/package.json index 39c3b9aa01335..143a3a9330c65 100644 --- a/packages/kbn-securitysolution-hook-utils/package.json +++ b/packages/kbn-securitysolution-hook-utils/package.json @@ -3,5 +3,6 @@ "version": "1.0.0", "description": "Security Solution utilities for React hooks", "license": "SSPL-1.0 OR Elastic License 2.0", - "private": true + "private": true, + "sideEffects": false } \ No newline at end of file diff --git a/packages/kbn-securitysolution-io-ts-alerting-types/package.json b/packages/kbn-securitysolution-io-ts-alerting-types/package.json index c570745f51677..c923d9d8f95e0 100644 --- a/packages/kbn-securitysolution-io-ts-alerting-types/package.json +++ b/packages/kbn-securitysolution-io-ts-alerting-types/package.json @@ -3,5 +3,6 @@ "version": "1.0.0", "description": "io ts utilities and types to be shared with plugins from the security solution project", "license": "SSPL-1.0 OR Elastic License 2.0", - "private": true + "private": true, + "sideEffects": false } \ No newline at end of file diff --git a/packages/kbn-securitysolution-io-ts-list-types/package.json b/packages/kbn-securitysolution-io-ts-list-types/package.json index 3794f95d0f8b7..64e4295604f98 100644 --- a/packages/kbn-securitysolution-io-ts-list-types/package.json +++ b/packages/kbn-securitysolution-io-ts-list-types/package.json @@ -3,5 +3,6 @@ "version": "1.0.0", "description": "io ts utilities and types to be shared with plugins from the security solution project", "license": "SSPL-1.0 OR Elastic License 2.0", - "private": true + "private": true, + "sideEffects": false } \ No newline at end of file diff --git a/packages/kbn-securitysolution-io-ts-types/package.json b/packages/kbn-securitysolution-io-ts-types/package.json index 6bbb9a2300338..e7d0c505b52d5 100644 --- a/packages/kbn-securitysolution-io-ts-types/package.json +++ b/packages/kbn-securitysolution-io-ts-types/package.json @@ -3,5 +3,6 @@ "version": "1.0.0", "description": "io ts utilities and types to be shared with plugins from the security solution project", "license": "SSPL-1.0 OR Elastic License 2.0", - "private": true + "private": true, + "sideEffects": false } \ No newline at end of file diff --git a/packages/kbn-securitysolution-io-ts-utils/package.json b/packages/kbn-securitysolution-io-ts-utils/package.json index c6bdf4a7d0987..972c6999f9a63 100644 --- a/packages/kbn-securitysolution-io-ts-utils/package.json +++ b/packages/kbn-securitysolution-io-ts-utils/package.json @@ -3,5 +3,6 @@ "version": "1.0.0", "description": "io ts utilities and types to be shared with plugins from the security solution project", "license": "SSPL-1.0 OR Elastic License 2.0", - "private": true + "private": true, + "sideEffects": false } \ No newline at end of file diff --git a/packages/kbn-securitysolution-list-hooks/package.json b/packages/kbn-securitysolution-list-hooks/package.json index 0a21618561b2e..79416813d73ce 100644 --- a/packages/kbn-securitysolution-list-hooks/package.json +++ b/packages/kbn-securitysolution-list-hooks/package.json @@ -3,5 +3,6 @@ "version": "1.0.0", "description": "Security solution list ReactJS hooks", "license": "SSPL-1.0 OR Elastic License 2.0", - "private": true + "private": true, + "sideEffects": false } \ No newline at end of file diff --git a/packages/kbn-securitysolution-list-utils/package.json b/packages/kbn-securitysolution-list-utils/package.json index 0cb4d35b2d5e8..5728241363015 100644 --- a/packages/kbn-securitysolution-list-utils/package.json +++ b/packages/kbn-securitysolution-list-utils/package.json @@ -3,5 +3,6 @@ "version": "1.0.0", "description": "security solution list utilities", "license": "SSPL-1.0 OR Elastic License 2.0", - "private": true + "private": true, + "sideEffects": false } \ No newline at end of file diff --git a/packages/kbn-securitysolution-utils/package.json b/packages/kbn-securitysolution-utils/package.json index 63bcac7f14f15..7cebd60146a90 100644 --- a/packages/kbn-securitysolution-utils/package.json +++ b/packages/kbn-securitysolution-utils/package.json @@ -3,5 +3,6 @@ "version": "1.0.0", "description": "security solution utilities to use across plugins such lists, security_solution, cases, etc...", "license": "SSPL-1.0 OR Elastic License 2.0", - "private": true + "private": true, + "sideEffects": false } \ No newline at end of file diff --git a/packages/kbn-server-route-repository/kibana.jsonc b/packages/kbn-server-route-repository/kibana.jsonc index dbf7fc396428c..c10021164c722 100644 --- a/packages/kbn-server-route-repository/kibana.jsonc +++ b/packages/kbn-server-route-repository/kibana.jsonc @@ -1,5 +1,5 @@ { "type": "shared-common", "id": "@kbn/server-route-repository", - "owner": ["@elastic/obs-knowledge-team", "@elastic/obs-ux-management-team"] + "owner": ["@elastic/obs-knowledge-team"] } diff --git a/packages/kbn-server-route-repository/src/register_routes.ts b/packages/kbn-server-route-repository/src/register_routes.ts index 6698f40b2d5c6..e80d7a4d8cddf 100644 --- a/packages/kbn-server-route-repository/src/register_routes.ts +++ b/packages/kbn-server-route-repository/src/register_routes.ts @@ -63,6 +63,7 @@ export function registerRoutes({ request, context, params: validatedParams, + logger, ...dependencies, }).then((value) => { return { diff --git a/packages/kbn-shared-ux-utility/package.json b/packages/kbn-shared-ux-utility/package.json index 302da6f03a812..16e37be9fae12 100644 --- a/packages/kbn-shared-ux-utility/package.json +++ b/packages/kbn-shared-ux-utility/package.json @@ -2,5 +2,6 @@ "name": "@kbn/shared-ux-utility", "private": true, "version": "1.0.0", - "license": "SSPL-1.0 OR Elastic License 2.0" + "license": "SSPL-1.0 OR Elastic License 2.0", + "sideEffects": false } \ No newline at end of file diff --git a/packages/kbn-std/package.json b/packages/kbn-std/package.json index d8317ac361f53..66c58e27c0c08 100644 --- a/packages/kbn-std/package.json +++ b/packages/kbn-std/package.json @@ -3,5 +3,6 @@ "version": "1.0.0", "author": "Kibana Core", "license": "SSPL-1.0 OR Elastic License 2.0", + "sideEffects": false, "private": true } \ No newline at end of file diff --git a/packages/kbn-test/index.ts b/packages/kbn-test/index.ts index 5be415161a4a5..e5409ee6ee485 100644 --- a/packages/kbn-test/index.ts +++ b/packages/kbn-test/index.ts @@ -13,7 +13,7 @@ export { startServersCli, startServers } from './src/functional_tests/start_serv // @internal export { runTestsCli, runTests } from './src/functional_tests/run_tests'; - +export { SamlSessionManager, type SamlSessionManagerOptions, type HostOptions } from './src/auth'; export { runElasticsearch, runKibanaServer } from './src/functional_tests/lib'; export { getKibanaCliArg, getKibanaCliLoggers } from './src/functional_tests/lib/kibana_cli_args'; @@ -52,6 +52,8 @@ export { getUrl } from './src/jest/get_url'; export { runCheckJestConfigsCli } from './src/jest/run_check_jest_configs_cli'; +export { runCheckFtrCodeOwnersCli } from './src/functional_test_runner/run_check_ftr_code_owners'; + export { runJest } from './src/jest/run'; export * from './src/kbn_archiver_cli'; diff --git a/packages/kbn-test/src/auth/helper.ts b/packages/kbn-test/src/auth/helper.ts new file mode 100644 index 0000000000000..d13e3ef69f37b --- /dev/null +++ b/packages/kbn-test/src/auth/helper.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 * as fs from 'fs'; +import { Role, User } from './types'; + +export const readCloudUsersFromFile = (filePath: string): Array<[Role, User]> => { + if (!fs.existsSync(filePath)) { + throw new Error(`Please define user roles with email/password in ${filePath}`); + } + const data = fs.readFileSync(filePath, 'utf8'); + if (data.length === 0) { + throw new Error(`'${filePath}' is empty: no roles are defined`); + } + + return Object.entries(JSON.parse(data)) as Array<[Role, User]>; +}; diff --git a/packages/kbn-test/src/auth/index.ts b/packages/kbn-test/src/auth/index.ts new file mode 100644 index 0000000000000..00631c3ab2b0a --- /dev/null +++ b/packages/kbn-test/src/auth/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { + SamlSessionManager, + type SamlSessionManagerOptions, + type HostOptions, +} from './session_manager'; diff --git a/x-pack/test_serverless/shared/services/user_manager/saml_auth.ts b/packages/kbn-test/src/auth/saml_auth.ts similarity index 90% rename from x-pack/test_serverless/shared/services/user_manager/saml_auth.ts rename to packages/kbn-test/src/auth/saml_auth.ts index ac69ec402fa7c..996f16eace38a 100644 --- a/x-pack/test_serverless/shared/services/user_manager/saml_auth.ts +++ b/packages/kbn-test/src/auth/saml_auth.ts @@ -1,40 +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. + * 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 { createSAMLResponse as createMockedSAMLResponse } from '@kbn/mock-idp-plugin/common'; import { ToolingLog } from '@kbn/tooling-log'; import axios, { AxiosResponse } from 'axios'; import * as cheerio from 'cheerio'; -import { parse as parseCookie } from 'tough-cookie'; +import { Cookie, parse as parseCookie } from 'tough-cookie'; import Url from 'url'; -import { Session } from './svl_user_manager'; - -export interface CloudSamlSessionParams { - email: string; - password: string; - kbnHost: string; - kbnVersion: string; - log: ToolingLog; -} - -export interface LocalSamlSessionParams { - username: string; - email: string; - fullname: string; - role: string; - kbnHost: string; - log: ToolingLog; -} +import { CloudSamlSessionParams, CreateSamlSessionParams, LocalSamlSessionParams } from './types'; + +export class Session { + readonly cookie; + readonly email; + readonly fullname; + constructor(cookie: Cookie, email: string, fullname: string) { + this.cookie = cookie; + this.email = email; + this.fullname = fullname; + } -export interface CreateSamlSessionParams { - hostname: string; - email: string; - password: string; - log: ToolingLog; + getCookieValue() { + return this.cookie.value; + } } const cleanException = (url: string, ex: any) => { diff --git a/packages/kbn-test/src/auth/session_manager.ts b/packages/kbn-test/src/auth/session_manager.ts new file mode 100644 index 0000000000000..2e498d2bc9c74 --- /dev/null +++ b/packages/kbn-test/src/auth/session_manager.ts @@ -0,0 +1,135 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { REPO_ROOT } from '@kbn/repo-info'; +import { ToolingLog } from '@kbn/tooling-log'; +import { resolve } from 'path'; +import Url from 'url'; +import { KbnClient } from '../kbn_client'; +import { readCloudUsersFromFile } from './helper'; +import { createCloudSAMLSession, createLocalSAMLSession, Session } from './saml_auth'; +import { Role, User } from './types'; + +export interface HostOptions { + protocol: 'http' | 'https'; + hostname: string; + port?: number; + username: string; + password: string; +} + +export interface SamlSessionManagerOptions { + hostOptions: HostOptions; + isCloud: boolean; + log: ToolingLog; +} + +/** + * Manages cookies associated with user roles + */ +export class SamlSessionManager { + private readonly isCloud: boolean; + private readonly kbnHost: string; + private readonly kbnClient: KbnClient; + private readonly log: ToolingLog; + private readonly roleToUserMap: Map; + private readonly sessionCache: Map; + private readonly userRoleFilePath = resolve(REPO_ROOT, '.ftr', 'role_users.json'); + + constructor(options: SamlSessionManagerOptions) { + this.isCloud = options.isCloud; + this.log = options.log; + const hostOptionsWithoutAuth = { + protocol: options.hostOptions.protocol, + hostname: options.hostOptions.hostname, + port: options.hostOptions.port, + }; + this.kbnHost = Url.format(hostOptionsWithoutAuth); + this.kbnClient = new KbnClient({ + log: this.log, + url: Url.format({ + ...hostOptionsWithoutAuth, + auth: `${options.hostOptions.username}:${options.hostOptions.password}`, + }), + }); + this.sessionCache = new Map(); + this.roleToUserMap = new Map(); + } + + /** + * Loads cloud users from '.ftr/role_users.json' + * QAF prepares the file for CI pipelines, make sure to add it manually for local run + */ + private getCloudUsers = () => { + if (this.roleToUserMap.size === 0) { + const data = readCloudUsersFromFile(this.userRoleFilePath); + for (const [roleName, user] of data) { + this.roleToUserMap.set(roleName, user); + } + } + + return this.roleToUserMap; + }; + + private getCloudUserByRole = (role: string) => { + if (this.getCloudUsers().has(role)) { + return this.getCloudUsers().get(role)!; + } else { + throw new Error(`User with '${role}' role is not defined`); + } + }; + + private getSessionByRole = async (role: string) => { + if (this.sessionCache.has(role)) { + return this.sessionCache.get(role)!; + } + + let session: Session; + + if (this.isCloud) { + this.log.debug(`new cloud SAML authentication with '${role}' role`); + const kbnVersion = await this.kbnClient.version.get(); + const { email, password } = this.getCloudUserByRole(role); + session = await createCloudSAMLSession({ + email, + password, + kbnHost: this.kbnHost, + kbnVersion, + log: this.log, + }); + } else { + this.log.debug(`new fake SAML authentication with '${role}' role`); + session = await createLocalSAMLSession({ + username: `elastic_${role}`, + email: `elastic_${role}@elastic.co`, + fullname: `test ${role}`, + role, + kbnHost: this.kbnHost, + log: this.log, + }); + } + + this.sessionCache.set(role, session); + return session; + }; + + async getApiCredentialsForRole(role: string) { + const session = await this.getSessionByRole(role); + return { Cookie: `sid=${session.getCookieValue()}` }; + } + + async getSessionCookieForRole(role: string) { + const session = await this.getSessionByRole(role); + return session.getCookieValue(); + } + + async getUserData(role: string) { + const { email, fullname } = await this.getSessionByRole(role); + return { email, fullname }; + } +} diff --git a/packages/kbn-test/src/auth/sesson_manager.test.ts b/packages/kbn-test/src/auth/sesson_manager.test.ts new file mode 100644 index 0000000000000..88049acddb2e0 --- /dev/null +++ b/packages/kbn-test/src/auth/sesson_manager.test.ts @@ -0,0 +1,161 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ToolingLog } from '@kbn/tooling-log'; +import { Cookie } from 'tough-cookie'; +import { Session } from './saml_auth'; +import { SamlSessionManager } from './session_manager'; +import * as samlAuth from './saml_auth'; +import * as helper from './helper'; +import { Role, User } from './types'; + +const log = new ToolingLog(); + +const cookieInstance = Cookie.parse( + 'sid=kbn_cookie_value; Path=/; Expires=Wed, 01 Oct 2023 07:00:00 GMT' +)!; +const email = 'testuser@elastic.com'; +const fullname = 'Test User'; + +const cloudCookieInstance = Cookie.parse( + 'sid=cloud_cookie_value; Path=/; Expires=Wed, 01 Oct 2023 07:00:00 GMT' +)!; +const cloudEmail = 'viewer@elastic.co'; +const cloudFullname = 'Test Viewer'; + +const cloudUsers = new Array<[Role, User]>(); +cloudUsers.push(['viewer', { email: 'viewer@elastic.co', password: 'p1234' }]); +cloudUsers.push(['admin', { email: 'admin@elastic.co', password: 'p1234' }]); + +const createLocalSAMLSessionMock = jest.spyOn(samlAuth, 'createLocalSAMLSession'); +const createCloudSAMLSessionMock = jest.spyOn(samlAuth, 'createCloudSAMLSession'); +const readCloudUsersFromFileMock = jest.spyOn(helper, 'readCloudUsersFromFile'); + +jest.mock('../kbn_client/kbn_client', () => { + return { + KbnClient: jest.fn(), + }; +}); +const get = jest.fn(); + +beforeEach(() => { + jest.resetAllMocks(); + jest + .requireMock('../kbn_client/kbn_client') + .KbnClient.mockImplementation(() => ({ version: { get } })); + get.mockImplementationOnce(() => Promise.resolve('8.12.0')); + + createLocalSAMLSessionMock.mockResolvedValue(new Session(cookieInstance, email, fullname)); + createCloudSAMLSessionMock.mockResolvedValue( + new Session(cloudCookieInstance, cloudEmail, cloudFullname) + ); + readCloudUsersFromFileMock.mockReturnValue(cloudUsers); +}); + +describe('SamlSessionManager', () => { + describe('for local session', () => { + const hostOptions = { + protocol: 'http' as 'http' | 'https', + hostname: 'localhost', + port: 5620, + username: 'elastic', + password: 'changeme', + }; + const isCloud = false; + test('should create an instance of SamlSessionManager', () => { + const samlSessionManager = new SamlSessionManager({ hostOptions, log, isCloud }); + expect(samlSessionManager).toBeInstanceOf(SamlSessionManager); + }); + + test(`'getSessionCookieForRole' should return the actual cookie value`, async () => { + const samlSessionManager = new SamlSessionManager({ hostOptions, log, isCloud }); + const cookie = await samlSessionManager.getSessionCookieForRole('tester'); + expect(cookie).toBe(cookieInstance.value); + }); + + test(`'getApiCredentialsForRole' should return {Cookie: }`, async () => { + const samlSessionManager = new SamlSessionManager({ hostOptions, log, isCloud }); + const credentials = await samlSessionManager.getApiCredentialsForRole('tester'); + expect(credentials).toEqual({ Cookie: `${cookieInstance.cookieString()}` }); + }); + + test(`'getSessionCookieForRole' should call 'createLocalSAMLSession' only once for the same role`, async () => { + const samlSessionManager = new SamlSessionManager({ hostOptions, log, isCloud }); + await samlSessionManager.getSessionCookieForRole('tester'); + await samlSessionManager.getSessionCookieForRole('admin'); + await samlSessionManager.getSessionCookieForRole('tester'); + expect(createLocalSAMLSessionMock.mock.calls).toHaveLength(2); + expect(createCloudSAMLSessionMock.mock.calls).toHaveLength(0); + }); + + test(`'getUserData' should return the correct email & fullname`, async () => { + const samlSessionManager = new SamlSessionManager({ hostOptions, log, isCloud }); + const data = await samlSessionManager.getUserData('tester'); + expect(data).toEqual({ email, fullname }); + }); + }); + + describe('for cloud session', () => { + const hostOptions = { + protocol: 'https' as 'http' | 'https', + hostname: 'cloud', + username: 'elastic', + password: 'changeme', + }; + const isCloud = true; + test('should create an instance of SamlSessionManager', () => { + const samlSessionManager = new SamlSessionManager({ hostOptions, log, isCloud }); + expect(samlSessionManager).toBeInstanceOf(SamlSessionManager); + }); + + test(`'getSessionCookieForRole' should return the actual cookie value`, async () => { + const samlSessionManager = new SamlSessionManager({ hostOptions, log, isCloud }); + createCloudSAMLSessionMock.mockResolvedValue( + new Session(cloudCookieInstance, cloudEmail, cloudFullname) + ); + const cookie = await samlSessionManager.getSessionCookieForRole('viewer'); + expect(cookie).toBe(cloudCookieInstance.value); + }); + + test(`'getApiCredentialsForRole' should return {Cookie: }`, async () => { + const samlSessionManager = new SamlSessionManager({ hostOptions, log, isCloud }); + const credentials = await samlSessionManager.getApiCredentialsForRole('viewer'); + expect(credentials).toEqual({ Cookie: `${cloudCookieInstance.cookieString()}` }); + }); + + test(`'getSessionCookieForRole' should call 'createCloudSAMLSession' only once for the same role`, async () => { + const samlSessionManager = new SamlSessionManager({ hostOptions, log, isCloud }); + await samlSessionManager.getSessionCookieForRole('viewer'); + await samlSessionManager.getSessionCookieForRole('admin'); + await samlSessionManager.getSessionCookieForRole('viewer'); + expect(createLocalSAMLSessionMock.mock.calls).toHaveLength(0); + expect(createCloudSAMLSessionMock.mock.calls).toHaveLength(2); + }); + + test(`'getUserData' should return the correct email & fullname`, async () => { + const samlSessionManager = new SamlSessionManager({ hostOptions, log, isCloud }); + const data = await samlSessionManager.getUserData('viewer'); + expect(data).toEqual({ email: cloudEmail, fullname: cloudFullname }); + }); + + test(`throws error when roles does not exist`, async () => { + const nonExistingRole = 'tester'; + const samlSessionManager = new SamlSessionManager({ hostOptions, log, isCloud }); + await expect(samlSessionManager.getSessionCookieForRole(nonExistingRole)).rejects.toThrow( + `User with '${nonExistingRole}' role is not defined` + ); + await expect(samlSessionManager.getApiCredentialsForRole(nonExistingRole)).rejects.toThrow( + `User with '${nonExistingRole}' role is not defined` + ); + await expect(samlSessionManager.getUserData(nonExistingRole)).rejects.toThrow( + `User with '${nonExistingRole}' role is not defined` + ); + expect(createCloudSAMLSessionMock.mock.calls).toHaveLength(0); + }); + }); +}); diff --git a/packages/kbn-test/src/auth/types.ts b/packages/kbn-test/src/auth/types.ts new file mode 100644 index 0000000000000..45e5b78b0ba38 --- /dev/null +++ b/packages/kbn-test/src/auth/types.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ToolingLog } from '@kbn/tooling-log'; + +export interface CloudSamlSessionParams { + kbnHost: string; + kbnVersion: string; + email: string; + password: string; + log: ToolingLog; +} + +export interface LocalSamlSessionParams { + kbnHost: string; + email: string; + username: string; + fullname: string; + role: string; + log: ToolingLog; +} + +export interface CreateSamlSessionParams { + hostname: string; + email: string; + password: string; + log: ToolingLog; +} + +export interface User { + readonly email: string; + readonly password: string; +} + +export type Role = string; diff --git a/packages/kbn-test/src/functional_test_runner/run_check_ftr_code_owners.ts b/packages/kbn-test/src/functional_test_runner/run_check_ftr_code_owners.ts new file mode 100644 index 0000000000000..91a6ded43dbac --- /dev/null +++ b/packages/kbn-test/src/functional_test_runner/run_check_ftr_code_owners.ts @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { run } from '@kbn/dev-cli-runner'; +import { createFailError } from '@kbn/dev-cli-errors'; +import { getRepoFiles } from '@kbn/get-repo-files'; +import { getCodeOwnersForFile, getPathsWithOwnersReversed } from '@kbn/code-owners'; + +const TEST_DIRECTORIES = ['test', 'x-pack/test', 'x-pack/test_serverless']; + +const fmtMs = (ms: number) => { + if (ms < 1000) { + return `${Math.round(ms)} ms`; + } + + return `${(Math.round(ms) / 1000).toFixed(2)} s`; +}; + +const fmtList = (list: Iterable) => [...list].map((i) => ` - ${i}`).join('\n'); + +export async function runCheckFtrCodeOwnersCli() { + run( + async ({ log }) => { + const start = performance.now(); + + const missingOwners = new Set(); + + // cache codeowners for quicker lookup + const reversedCodeowners = getPathsWithOwnersReversed(); + + const testFiles = await getRepoFiles(TEST_DIRECTORIES); + for (const { repoRel } of testFiles) { + const owners = getCodeOwnersForFile(repoRel, reversedCodeowners); + if (owners === undefined) { + missingOwners.add(repoRel); + } + } + + const timeSpent = fmtMs(performance.now() - start); + + if (missingOwners.size) { + log.error( + `The following test files do not have a GitHub code owner:\n${fmtList(missingOwners)}` + ); + throw createFailError( + `Found ${missingOwners.size} test files without code owner (checked ${testFiles.length} test files in ${timeSpent})` + ); + } + + log.success( + `All test files have a code owner (checked ${testFiles.length} test files in ${timeSpent})` + ); + }, + { + description: 'Check that all test files are covered by GitHub CODEOWNERS', + } + ); +} diff --git a/packages/kbn-test/tsconfig.json b/packages/kbn-test/tsconfig.json index 7d73db67a0b92..fa22159f0b387 100644 --- a/packages/kbn-test/tsconfig.json +++ b/packages/kbn-test/tsconfig.json @@ -32,5 +32,7 @@ "@kbn/babel-register", "@kbn/repo-packages", "@kbn/core-saved-objects-api-server", + "@kbn/mock-idp-plugin", + "@kbn/code-owners", ] } diff --git a/packages/kbn-text-based-editor/src/editor_footer.tsx b/packages/kbn-text-based-editor/src/editor_footer.tsx index 6bc663713a8d0..b9b183424c77a 100644 --- a/packages/kbn-text-based-editor/src/editor_footer.tsx +++ b/packages/kbn-text-based-editor/src/editor_footer.tsx @@ -26,7 +26,7 @@ import { import { Interpolation, Theme, css } from '@emotion/react'; import { css as classNameCss } from '@emotion/css'; -import type { MonacoError } from './helpers'; +import type { MonacoMessage } from './helpers'; const isMac = navigator.platform.toLowerCase().indexOf('mac') >= 0; const COMMAND_KEY = isMac ? '⌘' : '^'; @@ -98,10 +98,10 @@ export function ErrorsWarningsPopover({ isSpaceReduced, }: { isPopoverOpen: boolean; - items: MonacoError[]; + items: MonacoMessage[]; type: 'error' | 'warning'; setIsPopoverOpen: (flag: boolean) => void; - onErrorClick: (error: MonacoError) => void; + onErrorClick: (error: MonacoMessage) => void; isSpaceReduced?: boolean; }) { const strings = getConstsByType(type, items.length); @@ -109,7 +109,14 @@ export function ErrorsWarningsPopover({ - + { + setIsPopoverOpen(!isPopoverOpen); + }} + /> ; - errors?: MonacoError[]; - warning?: MonacoError[]; + errors?: MonacoMessage[]; + warnings?: MonacoMessage[]; detectTimestamp: boolean; - onErrorClick: (error: MonacoError) => void; + onErrorClick: (error: MonacoMessage) => void; runQuery: () => void; hideRunQueryText?: boolean; disableSubmitAction?: boolean; @@ -199,7 +206,7 @@ export const EditorFooter = memo(function EditorFooter({ lines, containerCSS, errors, - warning, + warnings, detectTimestamp, onErrorClick, runQuery, @@ -209,8 +216,8 @@ export const EditorFooter = memo(function EditorFooter({ isSpaceReduced, }: EditorFooterProps) { const { euiTheme } = useEuiTheme(); - const [isPopoverOpen, setIsPopoverOpen] = useState(false); - + const [isErrorPopoverOpen, setIsErrorPopoverOpen] = useState(false); + const [isWarningPopoverOpen, setIsWarningPopoverOpen] = useState(false); return ( 0 && ( { + if (isOpen) { + setIsWarningPopoverOpen(false); + } + setIsErrorPopoverOpen(isOpen); + }} onErrorClick={onErrorClick} - isSpaceReduced={isSpaceReduced} /> )} - {warning && warning.length > 0 && ( + {warnings && warnings.length > 0 && ( { + if (isOpen) { + setIsErrorPopoverOpen(false); + } + setIsWarningPopoverOpen(isOpen); + }} onErrorClick={onErrorClick} - isSpaceReduced={isSpaceReduced} /> )} diff --git a/packages/kbn-text-based-editor/src/helpers.test.ts b/packages/kbn-text-based-editor/src/helpers.test.ts index f0a4268f857bc..8ba691bb4e3ee 100644 --- a/packages/kbn-text-based-editor/src/helpers.test.ts +++ b/packages/kbn-text-based-editor/src/helpers.test.ts @@ -11,7 +11,7 @@ import { parseWarning, getInlineEditorText, getWrappedInPipesCode, - getIndicesForAutocomplete, + getIndicesList, } from './helpers'; describe('helpers', function () { @@ -23,7 +23,7 @@ describe('helpers', function () { const errors = [error]; expect(parseErrors(errors, 'SELECT miaou from test')).toEqual([ { - endColumn: 13, + endColumn: 14, endLineNumber: 1, message: ' Unknown column [miaou]', severity: 8, @@ -47,7 +47,7 @@ describe('helpers', function () { ) ).toEqual([ { - endColumn: 11, + endColumn: 12, endLineNumber: 3, message: ' Condition expression needs to be boolean, found [TEXT]', severity: 8, @@ -83,7 +83,7 @@ describe('helpers', function () { endLineNumber: 1, message: 'evaluation of [date_parse(geo.dest)] failed, treating result as null. Only first 20 failures recorded.', - severity: 8, + severity: 4, startColumn: 52, startLineNumber: 1, }, @@ -99,7 +99,7 @@ describe('helpers', function () { endLineNumber: 1, message: 'evaluation of [date_parse(geo.dest)] failed, treating result as null. Only first 20 failures recorded.', - severity: 8, + severity: 4, startColumn: 52, startLineNumber: 1, }, @@ -108,7 +108,7 @@ describe('helpers', function () { endLineNumber: 1, message: 'evaluation of [date_parse(geo.src)] failed, treating result as null. Only first 20 failures recorded.', - severity: 8, + severity: 4, startColumn: 84, startLineNumber: 1, }, @@ -124,7 +124,7 @@ describe('helpers', function () { endLineNumber: 1, message: 'Field [geo.coordinates] cannot be retrieved, it is unsupported or not indexed; returning null.', - severity: 8, + severity: 4, startColumn: 1, startLineNumber: 1, }, @@ -133,7 +133,7 @@ describe('helpers', function () { endLineNumber: 1, message: 'Field [ip_range] cannot be retrieved, it is unsupported or not indexed; returning null.', - severity: 8, + severity: 4, startColumn: 1, startLineNumber: 1, }, @@ -142,7 +142,7 @@ describe('helpers', function () { endLineNumber: 1, message: 'Field [timestamp_range] cannot be retrieved, it is unsupported or not indexed; returning null.', - severity: 8, + severity: 4, startColumn: 1, startLineNumber: 1, }, @@ -157,7 +157,7 @@ describe('helpers', function () { endLineNumber: 1, message: 'Field [geo.coordinates] cannot be retrieved, it is unsupported or not indexed; returning null.', - severity: 8, + severity: 4, startColumn: 1, startLineNumber: 1, }, @@ -166,7 +166,7 @@ describe('helpers', function () { endLineNumber: 1, message: 'Field [ip_range] cannot be retrieved, it is unsupported or not indexed; returning null.', - severity: 8, + severity: 4, startColumn: 1, startLineNumber: 1, }, @@ -175,7 +175,7 @@ describe('helpers', function () { endLineNumber: 1, message: 'evaluation of [date_parse(geo.dest)] failed, treating result as null. Only first 20 failures recorded.', - severity: 8, + severity: 4, startColumn: 52, startLineNumber: 1, }, @@ -233,8 +233,8 @@ describe('helpers', function () { }); }); - describe('getIndicesForAutocomplete', function () { - it('should not return system indices', async function () { + describe('getIndicesList', function () { + it('should return also system indices with hidden flag on', async function () { const dataViewsMock = dataViewPluginMocks.createStartContract(); const updatedDataViewsMock = { ...dataViewsMock, @@ -249,8 +249,11 @@ describe('helpers', function () { }, ]), }; - const indices = await getIndicesForAutocomplete(updatedDataViewsMock); - expect(indices).toStrictEqual(['logs']); + const indices = await getIndicesList(updatedDataViewsMock); + expect(indices).toStrictEqual([ + { name: '.system1', hidden: true }, + { name: 'logs', hidden: false }, + ]); }); }); }); diff --git a/packages/kbn-text-based-editor/src/helpers.ts b/packages/kbn-text-based-editor/src/helpers.ts index 234a5ae0089ab..88df8ef6a75e4 100644 --- a/packages/kbn-text-based-editor/src/helpers.ts +++ b/packages/kbn-text-based-editor/src/helpers.ts @@ -11,15 +11,9 @@ import useDebounce from 'react-use/lib/useDebounce'; import { monaco } from '@kbn/monaco'; import { i18n } from '@kbn/i18n'; import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; +import type { MapCache } from 'lodash'; -export interface MonacoError { - message: string; - startColumn: number; - startLineNumber: number; - endColumn: number; - endLineNumber: number; - severity: monaco.MarkerSeverity; -} +export type MonacoMessage = monaco.editor.IMarkerData; export const useDebounceWithOptions = ( fn: Function, @@ -45,7 +39,7 @@ export const useDebounceWithOptions = ( const quotedWarningMessageRegexp = /"(.*?)"/g; -export const parseWarning = (warning: string): MonacoError[] => { +export const parseWarning = (warning: string): MonacoMessage[] => { if (quotedWarningMessageRegexp.test(warning)) { const matches = warning.match(quotedWarningMessageRegexp); if (matches) { @@ -81,7 +75,7 @@ export const parseWarning = (warning: string): MonacoError[] => { startLineNumber, endColumn: startColumn + errorLength - 1, endLineNumber: startLineNumber, - severity: monaco.MarkerSeverity.Error, + severity: monaco.MarkerSeverity.Warning, }; }); } @@ -94,14 +88,18 @@ export const parseWarning = (warning: string): MonacoError[] => { startLineNumber: 1, endColumn: 10, endLineNumber: 1, - severity: monaco.MarkerSeverity.Error, + severity: monaco.MarkerSeverity.Warning, }, ]; }; -export const parseErrors = (errors: Error[], code: string): MonacoError[] => { +export const parseErrors = (errors: Error[], code: string): MonacoMessage[] => { return errors.map((error) => { - if (error.message.includes('line')) { + if ( + // Found while testing random commands (as inlinestats) + !error.message.includes('esql_illegal_argument_exception') && + error.message.includes('line') + ) { const text = error.message.split('line')[1]; const [lineNumber, startPosition, errorMessage] = text.split(':'); // initialize the length to 10 in case no error word found @@ -114,7 +112,7 @@ export const parseErrors = (errors: Error[], code: string): MonacoError[] => { message: errorMessage, startColumn: Number(startPosition), startLineNumber: Number(lineNumber), - endColumn: Number(startPosition) + errorLength, + endColumn: Number(startPosition) + errorLength + 1, endLineNumber: Number(lineNumber), severity: monaco.MarkerSeverity.Error, }; @@ -193,11 +191,23 @@ export const getWrappedInPipesCode = (code: string, isWrapped: boolean): string return codeNoLines.join(isWrapped ? ' | ' : '\n| '); }; -export const getIndicesForAutocomplete = async (dataViews: DataViewsPublicPluginStart) => { +export const getIndicesList = async (dataViews: DataViewsPublicPluginStart) => { const indices = await dataViews.getIndices({ showAllIndices: false, pattern: '*', isRollupIndex: () => false, }); - return indices.filter((index) => !index.name.startsWith('.')).map((i) => i.name); + return indices.map((index) => ({ name: index.name, hidden: index.name.startsWith('.') })); +}; + +// refresh the esql cache entry after 10 minutes +const CACHE_INVALIDATE_DELAY = 10 * 60 * 1000; + +export const clearCacheWhenOld = (cache: MapCache, esqlQuery: string) => { + if (cache.has(esqlQuery)) { + const cacheEntry = cache.get(esqlQuery); + if (Date.now() - cacheEntry.timestamp > CACHE_INVALIDATE_DELAY) { + cache.delete(esqlQuery); + } + } }; diff --git a/packages/kbn-text-based-editor/src/text_based_languages_editor.tsx b/packages/kbn-text-based-editor/src/text_based_languages_editor.tsx index 9d175ef86ecf0..adcff950575e3 100644 --- a/packages/kbn-text-based-editor/src/text_based_languages_editor.tsx +++ b/packages/kbn-text-based-editor/src/text_based_languages_editor.tsx @@ -6,22 +6,22 @@ * Side Public License, v 1. */ -import React, { useRef, memo, useEffect, useState, useCallback } from 'react'; +import React, { useRef, memo, useEffect, useState, useCallback, useMemo } from 'react'; import classNames from 'classnames'; +import memoize from 'lodash/memoize'; import { SQLLang, monaco, ESQL_LANG_ID, ESQL_THEME_ID, ESQLLang, - ESQLCustomAutocompleteCallbacks, + type ESQLCallbacks, } from '@kbn/monaco'; import type { AggregateQuery } from '@kbn/es-query'; import { getAggregateQueryMode, getLanguageDisplayName } from '@kbn/es-query'; import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import type { ExpressionsStart } from '@kbn/expressions-plugin/public'; import type { IndexManagementPluginSetup } from '@kbn/index-management-plugin/public'; -import type { SerializedEnrichPolicy } from '@kbn/index-management-plugin/common'; import { type LanguageDocumentationSections, LanguageDocumentationPopover, @@ -50,13 +50,14 @@ import { } from './text_based_languages_editor.styles'; import { useDebounceWithOptions, - parseErrors, parseWarning, getInlineEditorText, getDocumentationSections, - MonacoError, + type MonacoMessage, getWrappedInPipesCode, - getIndicesForAutocomplete, + parseErrors, + getIndicesList, + clearCacheWhenOld, } from './helpers'; import { EditorFooter } from './editor_footer'; import { ResizableButton } from './resizable_button'; @@ -136,7 +137,6 @@ const languageId = (language: string) => { let clickedOutside = false; let initialRender = true; let updateLinesFromModel = false; -let currentCursorContent = ''; let lines = 1; export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ @@ -146,8 +146,8 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ expandCodeEditor, isCodeEditorExpanded, detectTimestamp = false, - errors, - warning, + errors: serverErrors, + warning: serverWarning, isDisabled, isDarkMode, hideMinimizeButton, @@ -163,6 +163,8 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ const { dataViews, expressions, indexManagementApiService, application } = kibana.services; const [code, setCode] = useState(queryString ?? ''); const [codeOneLiner, setCodeOneLiner] = useState(''); + // To make server side errors less "sticky", register the state of the code when submitting + const [codeWhenSubmitted, setCodeStateOnSubmission] = useState(code); const [editorHeight, setEditorHeight] = useState( isCodeEditorExpanded ? EDITOR_INITIAL_HEIGHT_EXPANDED : EDITOR_INITIAL_HEIGHT ); @@ -171,13 +173,27 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ const [isCompactFocused, setIsCompactFocused] = useState(isCodeEditorExpanded); const [isCodeEditorExpandedFocused, setIsCodeEditorExpandedFocused] = useState(false); const [isWordWrapped, setIsWordWrapped] = useState(false); - const [editorErrors, setEditorErrors] = useState([]); - const [editorWarning, setEditorWarning] = useState([]); + + const [editorMessages, setEditorMessages] = useState<{ + errors: MonacoMessage[]; + warnings: MonacoMessage[]; + }>({ + errors: serverErrors ? parseErrors(serverErrors, code) : [], + warnings: serverWarning ? parseWarning(serverWarning) : [], + }); + + const onQuerySubmit = useCallback(() => { + const currentValue = editor1.current?.getValue(); + if (currentValue != null) { + setCodeStateOnSubmission(currentValue); + } + onTextLangQuerySubmit({ [language]: currentValue } as AggregateQuery); + }, [language, onTextLangQuerySubmit]); const [documentationSections, setDocumentationSections] = useState(); - const policiesRef = useRef([]); + const codeRef = useRef(code); // Registers a command to redirect users to the index management page // to create a new policy. The command is called by the buildNoPoliciesAvailableDefinition @@ -193,8 +209,8 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ isCompactFocused, editorHeight, isCodeEditorExpanded, - Boolean(errors?.length), - Boolean(warning), + Boolean(editorMessages.errors.length), + Boolean(editorMessages.warnings.length), isCodeEditorExpandedFocused, Boolean(documentationSections), Boolean(editorIsInline) @@ -247,11 +263,6 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ [editorHeight] ); - const onQuerySubmit = useCallback(() => { - const currentValue = editor1.current?.getValue(); - onTextLangQuerySubmit({ [language]: currentValue } as AggregateQuery); - }, [language, onTextLangQuerySubmit]); - const restoreInitialMode = () => { setIsCodeEditorExpandedFocused(false); if (isCodeEditorExpanded) return; @@ -294,30 +305,117 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ updateLinesFromModel = true; }, []); + const { cache: esqlFieldsCache, memoizedFieldsFromESQL } = useMemo(() => { + // need to store the timing of the first request so we can atomically clear the cache per query + const fn = memoize( + (...args: [{ esql: string }, ExpressionsStart]) => ({ + timestamp: Date.now(), + result: fetchFieldsFromESQL(...args), + }), + ({ esql }) => esql + ); + return { cache: fn.cache, memoizedFieldsFromESQL: fn }; + }, []); + + const esqlCallbacks: ESQLCallbacks = useMemo( + () => ({ + getSources: async () => { + return await getIndicesList(dataViews); + }, + getFieldsFor: async ({ query: queryToExecute }: { query?: string } | undefined = {}) => { + if (queryToExecute) { + // ES|QL with limit 0 returns only the columns and is more performant + const esqlQuery = { + esql: `${queryToExecute} | limit 0`, + }; + // Check if there's a stale entry and clear it + clearCacheWhenOld(esqlFieldsCache, esqlQuery.esql); + try { + const table = await memoizedFieldsFromESQL(esqlQuery, expressions).result; + return table?.columns.map((c) => ({ name: c.name, type: c.meta.type })) || []; + } catch (e) { + // no action yet + } + } + return []; + }, + getPolicies: async () => { + const { data: policies, error } = + (await indexManagementApiService?.getAllEnrichPolicies()) || {}; + if (error || !policies) { + return []; + } + return policies.map(({ type, query: policyQuery, ...rest }) => rest); + }, + }), + [dataViews, expressions, indexManagementApiService, esqlFieldsCache, memoizedFieldsFromESQL] + ); + + const queryValidation = useCallback( + async ({ active }: { active: boolean }) => { + if (!editorModel.current || language !== 'esql' || editorModel.current.isDisposed()) return; + monaco.editor.setModelMarkers(editorModel.current, 'Unified search', []); + const { warnings: parserWarnings, errors: parserErrors } = await ESQLLang.validate( + editorModel.current, + code, + esqlCallbacks + ); + const markers = []; + + if (parserErrors.length) { + markers.push(...parserErrors); + } + if (active) { + setEditorMessages({ errors: parserErrors, warnings: parserWarnings }); + monaco.editor.setModelMarkers(editorModel.current, 'Unified search', markers); + return; + } + }, + [esqlCallbacks, language, code] + ); + useDebounceWithOptions( () => { if (!editorModel.current) return; - if (warning && (!errors || !errors.length)) { - const parsedWarning = parseWarning(warning); - setEditorWarning(parsedWarning); - } else { - setEditorWarning([]); - } - if (errors && errors.length) { - const parsedErrors = parseErrors(errors, code); - setEditorErrors(parsedErrors); - monaco.editor.setModelMarkers(editorModel.current, 'Unified search', parsedErrors); + const subscription = { active: true }; + if (code === codeWhenSubmitted) { + if (serverErrors || serverWarning) { + const parsedErrors = parseErrors(serverErrors || [], code); + const parsedWarning = serverWarning ? parseWarning(serverWarning) : []; + setEditorMessages({ + errors: parsedErrors, + warnings: parsedErrors.length ? [] : parsedWarning, + }); + monaco.editor.setModelMarkers( + editorModel.current, + 'Unified search', + parsedErrors.length ? parsedErrors : [] + ); + return; + } } else { - monaco.editor.setModelMarkers(editorModel.current, 'Unified search', []); - setEditorErrors([]); + queryValidation(subscription).catch((error) => { + // console.log({ error }); + }); } + return () => (subscription.active = false); }, { skipFirstRender: false }, 256, - [errors, warning] + [serverErrors, serverWarning, code] + ); + + const suggestionProvider = useMemo( + () => (language === 'esql' ? ESQLLang.getSuggestionProvider?.(esqlCallbacks) : undefined), + [language, esqlCallbacks] ); - const onErrorClick = useCallback(({ startLineNumber, startColumn }: MonacoError) => { + const hoverProvider = useMemo( + () => (language === 'esql' ? ESQLLang.getHoverProvider?.(esqlCallbacks) : undefined), + [language, esqlCallbacks] + ); + + const onErrorClick = useCallback(({ startLineNumber, startColumn }: MonacoMessage) => { if (!editor1.current) { return; } @@ -351,7 +449,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ const text = getInlineEditorText(queryString, Boolean(hasLines)); const queryLength = text.length; const unusedSpace = - (errors && errors.length) || warning + editorMessages.errors.length || editorMessages.warnings.length ? EDITOR_ONE_LINER_UNUSED_SPACE_WITH_ERRORS : EDITOR_ONE_LINER_UNUSED_SPACE; const charactersAlowed = Math.floor((width - unusedSpace) / FONT_WIDTH); @@ -364,7 +462,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ } } }, - [isCompactFocused, queryString, errors, warning] + [isCompactFocused, queryString, editorMessages] ); useEffect(() => { @@ -417,73 +515,6 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ } }, [language, documentationSections]); - const getSourceIdentifiers: ESQLCustomAutocompleteCallbacks['getSourceIdentifiers'] = - useCallback(async () => { - return await getIndicesForAutocomplete(dataViews); - }, [dataViews]); - - const getFieldsIdentifiers: ESQLCustomAutocompleteCallbacks['getFieldsIdentifiers'] = useCallback( - async (ctx) => { - const pipes = currentCursorContent?.split('|'); - pipes?.pop(); - const validContent = pipes?.join('|'); - if (validContent) { - // ES|QL with limit 0 returns only the columns and is more performant - const esqlQuery = { - esql: `${validContent} | limit 0`, - }; - try { - const table = await fetchFieldsFromESQL(esqlQuery, expressions); - return table?.columns.map((c) => c.name) || []; - } catch (e) { - // no action yet - } - } - return []; - }, - [expressions] - ); - - const getPoliciesIdentifiers: ESQLCustomAutocompleteCallbacks['getPoliciesIdentifiers'] = - useCallback( - async (ctx) => { - const { data: policies, error } = - (await indexManagementApiService?.getAllEnrichPolicies()) || {}; - policiesRef.current = policies || []; - if (error || !policies) { - return []; - } - return policies.map(({ name, sourceIndices }) => ({ name, indices: sourceIndices })); - }, - [indexManagementApiService] - ); - - const getPolicyFieldsIdentifiers: ESQLCustomAutocompleteCallbacks['getPolicyFieldsIdentifiers'] = - useCallback( - async (ctx) => - policiesRef.current - .filter(({ name }) => ctx.userDefinedVariables.policyIdentifiers.includes(name)) - .flatMap(({ enrichFields }) => enrichFields), - [] - ); - - const getPolicyMatchingFieldIdentifiers: ESQLCustomAutocompleteCallbacks['getPolicyMatchingFieldIdentifiers'] = - useCallback( - async (ctx) => { - // try to load the list if none is present yet but - // at least one policy is declared in the userDefinedVariables - // (this happens if the user pastes an ESQL statement with the policy name in it) - if (!policiesRef.current.length && ctx.userDefinedVariables.policyIdentifiers.length) { - await getPoliciesIdentifiers(ctx); - } - const matchingField = policiesRef.current.find(({ name }) => - ctx.userDefinedVariables.policyIdentifiers.includes(name) - )?.matchField; - return matchingField ? [matchingField] : []; - }, - [getPoliciesIdentifiers] - ); - const codeEditorOptions: CodeEditorProps['options'] = { automaticLayout: false, accessibilitySupport: 'off', @@ -675,44 +706,60 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ )} )} - {!isCompactFocused && errors && errors.length > 0 && ( + {!isCompactFocused && editorMessages.errors.length > 0 && ( - {errors.length} - - )} - {!isCompactFocused && warning && (!errors || errors.length === 0) && ( - - {editorWarning.length} + {editorMessages.errors.length} )} + {!isCompactFocused && + editorMessages.warnings.length > 0 && + editorMessages.errors.length === 0 && ( + + {editorMessages.warnings.length} + + )} { + if (isCompactFocused || !hoverProvider?.provideHover) { + return { contents: [] }; + } + return hoverProvider?.provideHover(model, position, token); + }, + }} onChange={onQueryUpdate} editorDidMount={(editor) => { editor1.current = editor; @@ -736,7 +783,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ endColumn: currentPosition?.column ?? 1, }); if (content) { - currentCursorContent = content || editor.getValue(); + codeRef.current = content || editor.getValue(); } }); @@ -752,9 +799,7 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ editor.addCommand( // eslint-disable-next-line no-bitwise monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter, - function () { - onQuerySubmit(); - } + onQuerySubmit ); if (!isCodeEditorExpanded) { editor.onDidContentSizeChange((e) => { @@ -767,10 +812,13 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ { + if (editorMessages.errors.some((e) => e.source !== 'client')) { + onQuerySubmit(); + } + }} detectTimestamp={detectTimestamp} editorIsInline={editorIsInline} disableSubmitAction={disableSubmitAction} @@ -858,8 +906,6 @@ export const TextBasedLanguagesEditor = memo(function TextBasedLanguagesEditor({ )} {isCodeEditorExpanded && ( diff --git a/packages/kbn-ui-shared-deps-src/BUILD.bazel b/packages/kbn-ui-shared-deps-src/BUILD.bazel index 95b9c7ac51e27..cd97c193f9f86 100644 --- a/packages/kbn-ui-shared-deps-src/BUILD.bazel +++ b/packages/kbn-ui-shared-deps-src/BUILD.bazel @@ -28,6 +28,7 @@ webpack_cli( "//packages/kbn-datemath", "//packages/kbn-analytics", "//packages/kbn-es-query", + "//packages/kbn-search-errors", "//packages/kbn-std", "//packages/kbn-safer-lodash-set", "//packages/kbn-peggy", diff --git a/packages/kbn-ui-shared-deps-src/src/definitions.js b/packages/kbn-ui-shared-deps-src/src/definitions.js index e95ff4ac0a732..9ae258dca6bc8 100644 --- a/packages/kbn-ui-shared-deps-src/src/definitions.js +++ b/packages/kbn-ui-shared-deps-src/src/definitions.js @@ -89,6 +89,7 @@ const externals = { uuid: '__kbnSharedDeps__.Uuid', '@kbn/analytics': '__kbnSharedDeps__.KbnAnalytics', '@kbn/es-query': '__kbnSharedDeps__.KbnEsQuery', + '@kbn/search-errors': '__kbnSharedDeps__.KbnSearchErrors', '@kbn/std': '__kbnSharedDeps__.KbnStd', '@kbn/safer-lodash-set': '__kbnSharedDeps__.SaferLodashSet', '@kbn/shared-ux-error-boundary': '__kbnSharedDeps__.KbnSharedUxErrorBoundary', diff --git a/packages/kbn-ui-shared-deps-src/src/entry.js b/packages/kbn-ui-shared-deps-src/src/entry.js index 6e30acf963ab2..6ba856cbbc2d1 100644 --- a/packages/kbn-ui-shared-deps-src/src/entry.js +++ b/packages/kbn-ui-shared-deps-src/src/entry.js @@ -64,6 +64,7 @@ export const TsLib = require('tslib'); export const Uuid = require('uuid'); export const KbnAnalytics = require('@kbn/analytics'); export const KbnEsQuery = require('@kbn/es-query'); +export const KbnSearchErrors = require('@kbn/search-errors'); export const KbnStd = require('@kbn/std'); export const SaferLodashSet = require('@kbn/safer-lodash-set'); diff --git a/packages/kbn-ui-shared-deps-src/tsconfig.json b/packages/kbn-ui-shared-deps-src/tsconfig.json index 6b89a4adff0df..33f7503df2416 100644 --- a/packages/kbn-ui-shared-deps-src/tsconfig.json +++ b/packages/kbn-ui-shared-deps-src/tsconfig.json @@ -11,22 +11,11 @@ "**/*.js", ], "exclude": [ + "src/entry.js", "**/*.config.js", "target/**/*", ], "kbn_references": [ - "@kbn/ui-theme", - "@kbn/i18n", - "@kbn/i18n-react", - "@kbn/monaco", - "@kbn/datemath", - "@kbn/flot-charts", - "@kbn/analytics", - "@kbn/es-query", - "@kbn/rison", - "@kbn/std", - "@kbn/safer-lodash-set", "@kbn/repo-info", - "@kbn/shared-ux-error-boundary" ] } diff --git a/packages/kbn-unified-data-table/__mocks__/services.ts b/packages/kbn-unified-data-table/__mocks__/services.ts index 2c74668644497..40f626a335cf7 100644 --- a/packages/kbn-unified-data-table/__mocks__/services.ts +++ b/packages/kbn-unified-data-table/__mocks__/services.ts @@ -8,7 +8,7 @@ import { of } from 'rxjs'; import { uiActionsPluginMock } from '@kbn/ui-actions-plugin/public/mocks'; import { expressionsPluginMock } from '@kbn/expressions-plugin/public/mocks'; -import { chromeServiceMock, coreMock } from '@kbn/core/public/mocks'; +import { chromeServiceMock, coreMock, themeServiceMock } from '@kbn/core/public/mocks'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { fieldFormatsMock } from '@kbn/field-formats-plugin/common/mocks'; import { IUiSettingsClient, ToastsStart } from '@kbn/core/public'; @@ -41,10 +41,7 @@ export function createServicesMock() { ...uiSettingsMock, }; - const theme = { - theme$: of({ darkMode: false }), - }; - + const theme = themeServiceMock.createSetupContract({ darkMode: false }); corePluginMock.theme = theme; const dataPlugin = dataPluginMock.createStartContract(); diff --git a/packages/kbn-unified-data-table/src/components/data_table.test.tsx b/packages/kbn-unified-data-table/src/components/data_table.test.tsx index f898c4707717e..e69d1a88200ba 100644 --- a/packages/kbn-unified-data-table/src/components/data_table.test.tsx +++ b/packages/kbn-unified-data-table/src/components/data_table.test.tsx @@ -308,7 +308,7 @@ describe('UnifiedDataTable', () => { columns: ['message'], }); - expect(component.find(EuiDataGrid).prop('sorting')).toMatchInlineSnapshot(` + expect(component.find(EuiDataGrid).last().prop('sorting')).toMatchInlineSnapshot(` Object { "columns": Array [ Object { @@ -332,7 +332,7 @@ describe('UnifiedDataTable', () => { columns: ['bytes', 'message'], }); - expect(component.find(EuiDataGrid).prop('sorting')).toMatchInlineSnapshot(` + expect(component.find(EuiDataGrid).last().prop('sorting')).toMatchInlineSnapshot(` Object { "columns": Array [ Object { @@ -359,7 +359,7 @@ describe('UnifiedDataTable', () => { onUpdateRowHeight: jest.fn(), }); - expect(component.find(EuiDataGrid).prop('toolbarVisibility')).toMatchInlineSnapshot(` + expect(component.find(EuiDataGrid).first().prop('toolbarVisibility')).toMatchInlineSnapshot(` Object { "additionalControls": null, "showColumnSelector": false, @@ -385,7 +385,7 @@ describe('UnifiedDataTable', () => { onUpdateRowHeight: jest.fn(), }); - expect(component.find(EuiDataGrid).prop('toolbarVisibility')).toMatchInlineSnapshot(` + expect(component.find(EuiDataGrid).first().prop('toolbarVisibility')).toMatchInlineSnapshot(` Object { "additionalControls": null, "showColumnSelector": false, @@ -406,7 +406,7 @@ describe('UnifiedDataTable', () => { onUpdateSampleSize: undefined, }); - expect(component.find(EuiDataGrid).prop('toolbarVisibility')).toMatchInlineSnapshot(` + expect(component.find(EuiDataGrid).first().prop('toolbarVisibility')).toMatchInlineSnapshot(` Object { "additionalControls": null, "showColumnSelector": false, diff --git a/packages/kbn-unified-doc-viewer/src/components/doc_viewer/__snapshots__/doc_viewer.test.tsx.snap b/packages/kbn-unified-doc-viewer/src/components/doc_viewer/__snapshots__/doc_viewer.test.tsx.snap index 83dde56a57228..3bbf6a09a5019 100644 --- a/packages/kbn-unified-doc-viewer/src/components/doc_viewer/__snapshots__/doc_viewer.test.tsx.snap +++ b/packages/kbn-unified-doc-viewer/src/components/doc_viewer/__snapshots__/doc_viewer.test.tsx.snap @@ -12,7 +12,7 @@ exports[` Render with 3 different tabs 1`] = ` Array [ Object { "content": Render with 3 different tabs 1`] = ` } title="Render function" />, - "data-test-subj": "docViewerTab-0", - "id": "kbn_doc_viewer_tab_0", + "data-test-subj": "docViewerTab-function", + "id": "kbn_doc_viewer_tab_function", "name": "Render function", }, Object { "content": Render with 3 different tabs 1`] = ` } title="React component" />, - "data-test-subj": "docViewerTab-1", - "id": "kbn_doc_viewer_tab_1", + "data-test-subj": "docViewerTab-component", + "id": "kbn_doc_viewer_tab_component", "name": "React component", }, Object { "content": Render with 3 different tabs 1`] = ` } title="Invalid doc view" />, - "data-test-subj": "docViewerTab-2", - "id": "kbn_doc_viewer_tab_2", + "data-test-subj": "docViewerTab-invalid", + "id": "kbn_doc_viewer_tab_invalid", "name": "Invalid doc view", }, ] diff --git a/packages/kbn-unified-doc-viewer/src/components/doc_viewer/doc_viewer.test.tsx b/packages/kbn-unified-doc-viewer/src/components/doc_viewer/doc_viewer.test.tsx index a839606a268e7..f40b15b90571b 100644 --- a/packages/kbn-unified-doc-viewer/src/components/doc_viewer/doc_viewer.test.tsx +++ b/packages/kbn-unified-doc-viewer/src/components/doc_viewer/doc_viewer.test.tsx @@ -17,16 +17,19 @@ import { DocViewsRegistry } from '../..'; describe('', () => { test('Render with 3 different tabs', () => { const registry = new DocViewsRegistry(); - registry.addDocView({ order: 10, title: 'Render function', render: jest.fn() }); - registry.addDocView({ order: 20, title: 'React component', component: () =>
test
}); + registry.add({ id: 'function', order: 10, title: 'Render function', render: jest.fn() }); + registry.add({ + id: 'component', + order: 20, + title: 'React component', + component: () =>
test
, + }); // @ts-expect-error This should be invalid and will throw an error when rendering - registry.addDocView({ order: 30, title: 'Invalid doc view' }); + registry.add({ id: 'invalid', order: 30, title: 'Invalid doc view' }); const renderProps = { hit: {} } as DocViewRenderProps; - const wrapper = shallow( - - ); + const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); }); @@ -38,7 +41,8 @@ describe('', () => { } const registry = new DocViewsRegistry(); - registry.addDocView({ + registry.add({ + id: 'component', order: 10, title: 'React component', component: SomeComponent, @@ -49,9 +53,7 @@ describe('', () => { } as DocViewRenderProps; const errorMsg = 'Catch me if you can!'; - const wrapper = mount( - - ); + const wrapper = mount(); const error = new Error(errorMsg); wrapper.find(SomeComponent).simulateError(error); const errorMsgComponent = findTestSubject(wrapper, 'docViewerError'); diff --git a/packages/kbn-unified-doc-viewer/src/components/doc_viewer/doc_viewer.tsx b/packages/kbn-unified-doc-viewer/src/components/doc_viewer/doc_viewer.tsx index 901208f8b3988..fab4673eb8ed5 100644 --- a/packages/kbn-unified-doc-viewer/src/components/doc_viewer/doc_viewer.tsx +++ b/packages/kbn-unified-doc-viewer/src/components/doc_viewer/doc_viewer.tsx @@ -22,25 +22,25 @@ export interface DocViewerProps extends DocViewRenderProps { * a `render` function. */ export function DocViewer({ docViews, ...renderProps }: DocViewerProps) { - const tabs = docViews.map(({ title, render, component }: DocView, idx: number) => { + const tabs = docViews.map(({ id, title, render, component }: DocView) => { return { - id: `kbn_doc_viewer_tab_${idx}`, + id: `kbn_doc_viewer_tab_${id}`, name: title, content: ( ), - ['data-test-subj']: `docViewerTab-${idx}`, + ['data-test-subj']: `docViewerTab-${id}`, }; }); if (!tabs.length) { - // There there's a minimum of 2 tabs active in Discover. + // There's a minimum of 2 tabs active in Discover. // This condition takes care of unit tests with 0 tabs. return null; } diff --git a/packages/kbn-unified-doc-viewer/src/components/doc_viewer/doc_viewer_tab.test.tsx b/packages/kbn-unified-doc-viewer/src/components/doc_viewer/doc_viewer_tab.test.tsx index f48487aa5d5d7..991be3b0031d0 100644 --- a/packages/kbn-unified-doc-viewer/src/components/doc_viewer/doc_viewer_tab.test.tsx +++ b/packages/kbn-unified-doc-viewer/src/components/doc_viewer/doc_viewer_tab.test.tsx @@ -16,9 +16,9 @@ describe('DocViewerTab', () => { test('changing columns triggers an update', () => { const hit = buildDataTableRecord({ _index: 'test', _id: '1' }, dataViewMock); const props = { + id: 'doc_view_test', title: 'test', component: jest.fn(), - id: 1, render: jest.fn(), renderProps: { hit, diff --git a/packages/kbn-unified-doc-viewer/src/components/doc_viewer/doc_viewer_tab.tsx b/packages/kbn-unified-doc-viewer/src/components/doc_viewer/doc_viewer_tab.tsx index ce88120c1d196..ad0f7d9461f58 100644 --- a/packages/kbn-unified-doc-viewer/src/components/doc_viewer/doc_viewer_tab.tsx +++ b/packages/kbn-unified-doc-viewer/src/components/doc_viewer/doc_viewer_tab.tsx @@ -13,7 +13,7 @@ import { DocViewerError } from './doc_viewer_error'; import type { DocViewRenderFn, DocViewRenderProps } from '../../types'; interface Props { - id: number; + id: string; renderProps: DocViewRenderProps; title: string; render?: DocViewRenderFn; diff --git a/packages/kbn-unified-doc-viewer/src/services/doc_views_registry.test.tsx b/packages/kbn-unified-doc-viewer/src/services/doc_views_registry.test.tsx new file mode 100644 index 0000000000000..e8f631814d287 --- /dev/null +++ b/packages/kbn-unified-doc-viewer/src/services/doc_views_registry.test.tsx @@ -0,0 +1,89 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { DocViewsRegistry } from './doc_views_registry'; + +const fnDocView = { + id: 'function-doc-view', + order: 10, + title: 'Render function', + render: jest.fn(), +}; +const componentDocView = { + id: 'component-doc-view', + order: 20, + title: 'React component', + component: () =>
test
, +}; + +describe('DocViewerRegistry', () => { + test('can be initialized from an array of doc views', () => { + const registry = new DocViewsRegistry([fnDocView, componentDocView]); + + expect(registry.getAll()).toHaveLength(2); + }); + + test('can be initialized from another DocViewsRegistry instance', () => { + const registry = new DocViewsRegistry([fnDocView, componentDocView]); + const newRegistry = new DocViewsRegistry(registry); + + expect(registry.getAll()).toHaveLength(2); + expect(newRegistry.getAll()).toHaveLength(2); + expect(registry).not.toBe(newRegistry); + }); + + describe('#add', () => { + test('should add a doc view to the registry in the correct order', () => { + const registry = new DocViewsRegistry([componentDocView]); + + registry.add(fnDocView); + + const docViews = registry.getAll(); + + expect(docViews[0]).toHaveProperty('id', 'function-doc-view'); + expect(docViews[1]).toHaveProperty('id', 'component-doc-view'); + }); + + test('should throw an error when the passed doc view already exists for the given id', () => { + const registry = new DocViewsRegistry([fnDocView]); + + expect(() => registry.add(fnDocView)).toThrow( + 'DocViewsRegistry#add: a DocView is already registered with id "function-doc-view".' + ); + }); + }); + + describe('#removeById', () => { + test('should remove a doc view given the passed id', () => { + const registry = new DocViewsRegistry([fnDocView, componentDocView]); + + const docViews = registry.getAll(); + + expect(docViews[0]).toHaveProperty('id', 'function-doc-view'); + expect(docViews[1]).toHaveProperty('id', 'component-doc-view'); + + registry.removeById('function-doc-view'); + + expect(registry.getAll()[0]).toHaveProperty('id', 'component-doc-view'); + }); + }); + + describe('#clone', () => { + test('should return a new DocViewRegistry instance starting from the current one', () => { + const registry = new DocViewsRegistry([fnDocView, componentDocView]); + + const clonedRegistry = registry.clone(); + const docViews = clonedRegistry.getAll(); + + expect(docViews[0]).toHaveProperty('id', 'function-doc-view'); + expect(docViews[1]).toHaveProperty('id', 'component-doc-view'); + expect(registry).not.toBe(clonedRegistry); + }); + }); +}); diff --git a/packages/kbn-unified-doc-viewer/src/services/doc_views_registry.ts b/packages/kbn-unified-doc-viewer/src/services/doc_views_registry.ts index 620c8bc3058de..9cada7ed497a3 100644 --- a/packages/kbn-unified-doc-viewer/src/services/doc_views_registry.ts +++ b/packages/kbn-unified-doc-viewer/src/services/doc_views_registry.ts @@ -5,9 +5,7 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ - -import type { DataTableRecord } from '@kbn/discover-utils/types'; -import { DocView, DocViewInput, DocViewInputFn } from './types'; +import type { DocView, DocViewFactory } from './types'; export enum ElasticRequestState { Loading, @@ -18,24 +16,49 @@ export enum ElasticRequestState { } export class DocViewsRegistry { - private docViews: DocView[] = []; + private docViews: Map; + + constructor(initialValue?: DocViewsRegistry | DocView[]) { + if (initialValue instanceof DocViewsRegistry) { + this.docViews = new Map(initialValue.docViews); + } else if (Array.isArray(initialValue)) { + this.docViews = new Map(initialValue.map((docView) => [docView.id, docView])); + } else { + this.docViews = new Map(); + } + } - /** - * Extends and adds the given doc view to the registry array - */ - addDocView(docViewRaw: DocViewInput | DocViewInputFn) { + getAll() { + return [...this.docViews.values()]; + } + + add(docViewRaw: DocView | DocViewFactory) { const docView = typeof docViewRaw === 'function' ? docViewRaw() : docViewRaw; - this.docViews.push({ - ...docView, - shouldShow: docView.shouldShow ?? (() => true), - }); + + if (this.docViews.has(docView.id)) { + throw new Error( + `DocViewsRegistry#add: a DocView is already registered with id "${docView.id}".` + ); + } + + this.docViews.set(docView.id, docView); + // Sort the doc views at insertion time to perform this operation once and not on every retrieval. + this.sortDocViews(); } - /** - * Returns a sorted array of doc_views for rendering tabs - */ - getDocViewsSorted(hit: DataTableRecord) { - return this.docViews - .filter((docView) => docView.shouldShow(hit)) - .sort((a, b) => (Number(a.order) > Number(b.order) ? 1 : -1)); + + removeById(id: string) { + this.docViews.delete(id); + } + + clone() { + return new DocViewsRegistry(this); + } + + private sortDocViews() { + const sortedEntries = [...this.docViews.entries()].sort( + ([_currKey, curr], [_nextKey, next]) => curr.order - next.order + ); + + this.docViews = new Map(sortedEntries); } } diff --git a/packages/kbn-unified-doc-viewer/src/services/types.ts b/packages/kbn-unified-doc-viewer/src/services/types.ts index 25493db97d09e..b67c61de6ae88 100644 --- a/packages/kbn-unified-doc-viewer/src/services/types.ts +++ b/packages/kbn-unified-doc-viewer/src/services/types.ts @@ -9,6 +9,7 @@ import type { DataView, DataViewField } from '@kbn/data-views-plugin/public'; import type { AggregateQuery, Query } from '@kbn/es-query'; import type { DataTableRecord, IgnoredReason } from '@kbn/discover-utils/types'; +import { DocViewsRegistry } from './doc_views_registry'; export interface FieldMapping { filterable?: boolean; @@ -40,6 +41,7 @@ export interface DocViewRenderProps { filter?: DocViewFilterFn; onAddColumn?: (columnName: string) => void; onRemoveColumn?: (columnName: string) => void; + docViewsRegistry?: DocViewsRegistry | ((prevRegistry: DocViewsRegistry) => DocViewsRegistry); } export type DocViewerComponent = React.FC; export type DocViewRenderFn = ( @@ -48,8 +50,8 @@ export type DocViewRenderFn = ( ) => () => void; export interface BaseDocViewInput { + id: string; order: number; - shouldShow?: (hit: DataTableRecord) => boolean; title: string; } @@ -65,13 +67,9 @@ interface ComponentDocViewInput extends BaseDocViewInput { directive?: undefined; } -export type DocViewInput = ComponentDocViewInput | RenderDocViewInput; +export type DocView = ComponentDocViewInput | RenderDocViewInput; -export type DocView = DocViewInput & { - shouldShow: NonNullable; -}; - -export type DocViewInputFn = () => DocViewInput; +export type DocViewFactory = () => DocView; export interface FieldRecordLegacy { action: { diff --git a/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx b/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx index 929627537bbb9..c2d5fd0f32632 100755 --- a/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx +++ b/packages/kbn-unified-field-list/src/components/field_stats/field_stats.tsx @@ -8,6 +8,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import type { DataView, DataViewField } from '@kbn/data-views-plugin/common'; +import { getTimeZone } from '@kbn/visualization-utils'; import { ES_FIELD_TYPES, KBN_FIELD_TYPES } from '@kbn/field-types'; import { getEsQueryConfig } from '@kbn/data-service/src/es_query'; import type { IUiSettingsClient } from '@kbn/core/public'; @@ -28,6 +29,7 @@ import { Settings, TooltipType, Tooltip, + PartialTheme, } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; import { buildEsQuery, Query, Filter, AggregateQuery } from '@kbn/es-query'; @@ -222,22 +224,18 @@ const FieldStatsComponent: React.FC = ({ }; }, []); - const chartTheme = charts.theme.useChartsTheme(); const chartBaseTheme = charts.theme.useChartsBaseTheme(); - const customChartTheme: typeof chartTheme = useMemo(() => { + const chartThemeOverrides = useMemo(() => { return color ? { - ...chartTheme, barSeriesStyle: { - ...chartTheme.barSeriesStyle, rect: { - ...(chartTheme.barSeriesStyle?.rect || {}), fill: color, }, }, } - : chartTheme; - }, [chartTheme, color]); + : {}; + }, [color]); const { isLoading, @@ -485,7 +483,7 @@ const FieldStatsComponent: React.FC = ({ = ({ yAccessors={['count']} xScaleType={ScaleType.Time} yScaleType={ScaleType.Linear} - timeZone="local" + timeZone={getTimeZone(uiSettings)} /> @@ -537,7 +535,7 @@ const FieldStatsComponent: React.FC = ({ diff --git a/packages/kbn-unified-field-list/tsconfig.json b/packages/kbn-unified-field-list/tsconfig.json index eeca808e1bee7..dce08dcdebaf5 100644 --- a/packages/kbn-unified-field-list/tsconfig.json +++ b/packages/kbn-unified-field-list/tsconfig.json @@ -32,6 +32,7 @@ "@kbn/shared-ux-button-toolbar", "@kbn/field-utils", "@kbn/ml-ui-actions", + "@kbn/visualization-utils", ], "exclude": ["target/**/*"] } diff --git a/packages/kbn-unsaved-changes-badge/src/components/unsaved_changes_badge/__snapshots__/unsaved_changes_badge.test.tsx.snap b/packages/kbn-unsaved-changes-badge/src/components/unsaved_changes_badge/__snapshots__/unsaved_changes_badge.test.tsx.snap index 441d85d917ef3..141a8c7b437b8 100644 --- a/packages/kbn-unsaved-changes-badge/src/components/unsaved_changes_badge/__snapshots__/unsaved_changes_badge.test.tsx.snap +++ b/packages/kbn-unsaved-changes-badge/src/components/unsaved_changes_badge/__snapshots__/unsaved_changes_badge.test.tsx.snap @@ -3,34 +3,30 @@ exports[` should show all menu items 1`] = `
-
- -
+ + +
`; diff --git a/packages/kbn-unsaved-changes-badge/src/utils/__snapshots__/get_top_nav_unsaved_changes_badge.test.tsx.snap b/packages/kbn-unsaved-changes-badge/src/utils/__snapshots__/get_top_nav_unsaved_changes_badge.test.tsx.snap index eafe487e0265c..a2cca8a1cc1e3 100644 --- a/packages/kbn-unsaved-changes-badge/src/utils/__snapshots__/get_top_nav_unsaved_changes_badge.test.tsx.snap +++ b/packages/kbn-unsaved-changes-badge/src/utils/__snapshots__/get_top_nav_unsaved_changes_badge.test.tsx.snap @@ -3,34 +3,30 @@ exports[`getTopNavUnsavedChangesBadge() should work correctly 1`] = `
-
- -
+ + +
`; diff --git a/packages/kbn-url-state/index.test.ts b/packages/kbn-url-state/index.test.ts index 7e646a387d245..75b899532028f 100644 --- a/packages/kbn-url-state/index.test.ts +++ b/packages/kbn-url-state/index.test.ts @@ -52,7 +52,24 @@ describe('useSyncToUrl', () => { expect(window.history.pushState).toHaveBeenCalledWith( {}, '', - '#should_be_there?namespace=(test:foo)' + '#should_be_there?namespace=(test%3Afoo)' + ); + }); + + it('should escape values correctly', () => { + window.location.hash = '#should_be_there'; + + const { result } = renderHook(() => useUrlState('namespace', 'test')); + + act(() => { + result.current[1]('foo#bar'); + jest.runAllTimers(); + }); + + expect(window.history.pushState).toHaveBeenCalledWith( + {}, + '', + '#should_be_there?namespace=(test%3Afoo%23bar)' ); }); diff --git a/packages/kbn-url-state/index.ts b/packages/kbn-url-state/index.ts index d165d75e1f956..17c90fdc8d2aa 100644 --- a/packages/kbn-url-state/index.ts +++ b/packages/kbn-url-state/index.ts @@ -99,9 +99,10 @@ export const useUrlState = (urlNamespace: string, key: string) => { if (!Object.prototype.hasOwnProperty.call(cache.namespaces, ns)) { continue; } - searchParams[ns] = encode(cache.namespaces[ns]); + searchParams[ns] = encodeURIComponent(encode(cache.namespaces[ns])); } + // NOTE: don't re-encode the entire url params string const newSearch = stringify(searchParams, { encode: false }); if (window.location.search === newSearch) { diff --git a/packages/kbn-user-profile-components/package.json b/packages/kbn-user-profile-components/package.json index a4f8db277f5a2..96c776c301884 100644 --- a/packages/kbn-user-profile-components/package.json +++ b/packages/kbn-user-profile-components/package.json @@ -2,5 +2,6 @@ "name": "@kbn/user-profile-components", "version": "1.0.0", "private": true, - "license": "SSPL-1.0 OR Elastic License 2.0" + "license": "SSPL-1.0 OR Elastic License 2.0", + "sideEffects": false } \ No newline at end of file diff --git a/packages/kbn-utils/src/path/index.test.ts b/packages/kbn-utils/src/path/index.test.ts index 163dfbef598dc..608f3cb2cfeb2 100644 --- a/packages/kbn-utils/src/path/index.test.ts +++ b/packages/kbn-utils/src/path/index.test.ts @@ -6,10 +6,8 @@ * Side Public License, v 1. */ -import { join } from 'path'; import { accessSync, constants } from 'fs'; -import { rm, mkdtemp, writeFile } from 'fs/promises'; -import { getConfigPath, getDataPath, getLogsPath, getConfigDirectory, buildDataPaths } from '.'; +import { getConfigPath, getDataPath, getLogsPath, getConfigDirectory } from '.'; import { REPO_ROOT } from '@kbn/repo-info'; expect.addSnapshotSerializer( @@ -48,97 +46,3 @@ describe('Default path finder', () => { expect(() => accessSync(configPath, constants.R_OK)).not.toThrow(); }); }); - -describe('Custom data path finder', () => { - const originalArgv = process.argv; - - beforeEach(() => { - process.argv = originalArgv; - }); - - it('ignores the path.data flag when no value is provided', () => { - process.argv = ['--foo', 'bar', '--path.data', '--baz', 'xyz']; - - expect(buildDataPaths()).toMatchInlineSnapshot(` - Array [ - /data, - "/var/lib/kibana", - ] - `); - }); - - describe('overrides path.data when provided as command line argument', () => { - it('with absolute path', () => { - process.argv = ['--foo', 'bar', '--path.data', '/some/data/path', '--baz', 'xyz']; - - /* - * Test buildDataPaths since getDataPath returns the first valid directory and - * custom paths do not exist in environment. Custom directories are built during env init. - */ - expect(buildDataPaths()).toMatchInlineSnapshot(` - Array [ - "/some/data/path", - /data, - "/var/lib/kibana", - ] - `); - }); - - it('with relative path', () => { - process.argv = ['--foo', 'bar', '--path.data', 'data2', '--baz', 'xyz']; - - /* - * Test buildDataPaths since getDataPath returns the first valid directory and - * custom paths do not exist in environment. Custom directories are built during env init. - */ - expect(buildDataPaths()).toMatchInlineSnapshot(` - Array [ - /data2, - /data, - "/var/lib/kibana", - ] - `); - }); - }); - - describe('overrides path.data when provided by kibana.yml', () => { - let tempDir: string; - let tempConfigFile: string; - - beforeAll(async () => { - tempDir = await mkdtemp('config-test'); - tempConfigFile = join(tempDir, 'kibana.yml'); - }); - - afterAll(async () => { - await rm(tempDir, { recursive: true }); - delete process.env.KBN_PATH_CONF; - }); - - it('with absolute path', async () => { - process.env.KBN_PATH_CONF = tempDir; - await writeFile(tempConfigFile, `path.data: /path/from/yml`); - - expect(buildDataPaths()).toMatchInlineSnapshot(` - Array [ - "/path/from/yml", - /data, - "/var/lib/kibana", - ] - `); - }); - - it('with relative path', async () => { - process.env.KBN_PATH_CONF = tempDir; - await writeFile(tempConfigFile, `path.data: data2`); - - expect(buildDataPaths()).toMatchInlineSnapshot(` - Array [ - /data2, - /data, - "/var/lib/kibana", - ] - `); - }); - }); -}); diff --git a/packages/kbn-utils/src/path/index.ts b/packages/kbn-utils/src/path/index.ts index caeaa9a493f17..63ca454dd04fd 100644 --- a/packages/kbn-utils/src/path/index.ts +++ b/packages/kbn-utils/src/path/index.ts @@ -6,22 +6,18 @@ * Side Public License, v 1. */ -import { join, resolve } from 'path'; +import { join } from 'path'; import { accessSync, constants } from 'fs'; import { TypeOf, schema } from '@kbn/config-schema'; import { REPO_ROOT } from '@kbn/repo-info'; -import { getConfigFromFiles } from '@kbn/config'; -import getopts from 'getopts'; const isString = (v: any): v is string => typeof v === 'string'; -const buildConfigPaths = () => { - return [ - process.env.KBN_PATH_CONF && resolve(process.env.KBN_PATH_CONF, 'kibana.yml'), - join(REPO_ROOT, 'config/kibana.yml'), - '/etc/kibana/kibana.yml', - ].filter(isString); -}; +const CONFIG_PATHS = [ + process.env.KBN_PATH_CONF && join(process.env.KBN_PATH_CONF, 'kibana.yml'), + join(REPO_ROOT, 'config/kibana.yml'), + '/etc/kibana/kibana.yml', +].filter(isString); const CONFIG_DIRECTORIES = [ process.env.KBN_PATH_CONF, @@ -29,6 +25,8 @@ const CONFIG_DIRECTORIES = [ '/etc/kibana', ].filter(isString); +const DATA_PATHS = [join(REPO_ROOT, 'data'), '/var/lib/kibana'].filter(isString); + const LOGS_PATHS = [join(REPO_ROOT, 'logs'), '/var/log/kibana'].filter(isString); function findFile(paths: string[]) { @@ -43,29 +41,11 @@ function findFile(paths: string[]) { return availablePath || paths[0]; } -export const buildDataPaths = (): string[] => { - const configDataPath = getConfigFromFiles([getConfigPath()]).path?.data; - const argv = process.argv.slice(2); - const options = getopts(argv, { - string: ['pathData'], - alias: { - pathData: 'path.data', - }, - }); - - return [ - !!options.pathData && resolve(REPO_ROOT, options.pathData), - configDataPath && resolve(REPO_ROOT, configDataPath), - join(REPO_ROOT, 'data'), - '/var/lib/kibana', - ].filter(isString); -}; - /** * Get the path of kibana.yml * @internal */ -export const getConfigPath = () => findFile(buildConfigPaths()); +export const getConfigPath = () => findFile(CONFIG_PATHS); /** * Get the directory containing configuration files @@ -77,7 +57,7 @@ export const getConfigDirectory = () => findFile(CONFIG_DIRECTORIES); * Get the directory containing runtime data * @internal */ -export const getDataPath = () => findFile(buildDataPaths()); +export const getDataPath = () => findFile(DATA_PATHS); /** * Get the directory containing logs diff --git a/packages/kbn-utils/tsconfig.json b/packages/kbn-utils/tsconfig.json index f6e7fb408bfa2..6baa222ef8c37 100644 --- a/packages/kbn-utils/tsconfig.json +++ b/packages/kbn-utils/tsconfig.json @@ -13,7 +13,6 @@ "kbn_references": [ "@kbn/config-schema", "@kbn/repo-info", - "@kbn/config", ], "exclude": [ "target/**/*", diff --git a/packages/kbn-visualization-ui-components/components/query_input/filter_query_input.tsx b/packages/kbn-visualization-ui-components/components/query_input/filter_query_input.tsx index e998e48eefe99..ac24f7bb81860 100644 --- a/packages/kbn-visualization-ui-components/components/query_input/filter_query_input.tsx +++ b/packages/kbn-visualization-ui-components/components/query_input/filter_query_input.tsx @@ -101,7 +101,7 @@ export function FilterQueryInput({ /packages/kbn-visualization-utils'], +}; diff --git a/packages/kbn-visualization-utils/kibana.jsonc b/packages/kbn-visualization-utils/kibana.jsonc new file mode 100644 index 0000000000000..6589338ddb579 --- /dev/null +++ b/packages/kbn-visualization-utils/kibana.jsonc @@ -0,0 +1,5 @@ +{ + "type": "shared-common", + "id": "@kbn/visualization-utils", + "owner": "@elastic/kibana-visualizations" +} diff --git a/packages/kbn-visualization-utils/package.json b/packages/kbn-visualization-utils/package.json new file mode 100644 index 0000000000000..867d82159aa52 --- /dev/null +++ b/packages/kbn-visualization-utils/package.json @@ -0,0 +1,7 @@ +{ + "name": "@kbn/visualization-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-visualization-utils/src/get_timezone.test.ts b/packages/kbn-visualization-utils/src/get_timezone.test.ts new file mode 100644 index 0000000000000..b911e98bff5d0 --- /dev/null +++ b/packages/kbn-visualization-utils/src/get_timezone.test.ts @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 moment from 'moment-timezone'; +import { IUiSettingsClient } from '@kbn/core/public'; +import { getTimeZone } from './get_timezone'; + +describe('getTimeZone', () => { + const originalTimezone = moment.tz.guess(); + + beforeAll(() => { + moment.tz.setDefault('America/New_York'); + }); + + afterAll(() => { + if (originalTimezone) { + moment.tz.setDefault(originalTimezone); + } + }); + + it('returns local time zone when uiSettings returns Browser', () => { + expect( + getTimeZone({ + get: () => 'Browser', + isDefault: () => true, + } as unknown as IUiSettingsClient) + ).toEqual('America/New_York'); + }); + + it('returns timezone defined on uiSettings', () => { + const timezone = 'America/Toronto'; + expect( + getTimeZone({ + get: () => timezone, + isDefault: () => false, + } as unknown as IUiSettingsClient) + ).toEqual(timezone); + }); +}); diff --git a/src/plugins/chart_expressions/expression_heatmap/public/utils/get_timezone.ts b/packages/kbn-visualization-utils/src/get_timezone.ts similarity index 100% rename from src/plugins/chart_expressions/expression_heatmap/public/utils/get_timezone.ts rename to packages/kbn-visualization-utils/src/get_timezone.ts diff --git a/packages/kbn-visualization-utils/tsconfig.json b/packages/kbn-visualization-utils/tsconfig.json new file mode 100644 index 0000000000000..1afc36bf0b0be --- /dev/null +++ b/packages/kbn-visualization-utils/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["*.ts", "src/**/*", "__mocks__/**/*.ts"], + "compilerOptions": { + "outDir": "target/types" + }, + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/core", + ] +} diff --git a/packages/kbn-xstate-utils/src/dev_tools.ts b/packages/kbn-xstate-utils/src/dev_tools.ts index fa16b808b3aec..15b5779677a5b 100644 --- a/packages/kbn-xstate-utils/src/dev_tools.ts +++ b/packages/kbn-xstate-utils/src/dev_tools.ts @@ -6,4 +6,52 @@ * Side Public License, v 1. */ +import { + isArray, + isBoolean, + isDate, + isNil, + isNumber, + isPlainObject, + isString, + mapValues, +} from 'lodash'; + export const isDevMode = () => process.env.NODE_ENV !== 'production'; + +export const getDevToolsOptions = (): boolean | object => + isDevMode() + ? { + actionSanitizer: sanitizeAction, + stateSanitizer: sanitizeState, + } + : false; + +const redactComplexValues = (value: unknown): unknown => { + if (isString(value) || isNumber(value) || isBoolean(value) || isDate(value) || isNil(value)) { + return value; + } + + if (isArray(value)) { + if (value.length > 100) { + return '[redacted large array]'; + } + return value.map(redactComplexValues); + } + + if ((isPlainObject as (v: unknown) => v is object)(value)) { + if (Object.keys(value).length > 100) { + return '[redacted large object]'; + } + return mapValues(value, (innerValue: unknown) => redactComplexValues(innerValue)); + } + + return `[redacted complex value of type ${typeof value}]`; +}; + +const sanitizeAction = redactComplexValues; + +const sanitizeState = (state: Record) => ({ + value: state.value, + context: redactComplexValues(state.context), +}); diff --git a/packages/kbn-xstate-utils/src/index.ts b/packages/kbn-xstate-utils/src/index.ts index 2cf5853db6e08..02cd8a2b176b1 100644 --- a/packages/kbn-xstate-utils/src/index.ts +++ b/packages/kbn-xstate-utils/src/index.ts @@ -7,6 +7,6 @@ */ export * from './actions'; +export * from './dev_tools'; export * from './notification_channel'; export * from './types'; -export * from './dev_tools'; diff --git a/packages/kbn-zod-helpers/package.json b/packages/kbn-zod-helpers/package.json index 6d27a7e70f859..7ddaa86413951 100644 --- a/packages/kbn-zod-helpers/package.json +++ b/packages/kbn-zod-helpers/package.json @@ -3,5 +3,6 @@ "license": "SSPL-1.0 OR Elastic License 2.0", "name": "@kbn/zod-helpers", "private": true, - "version": "1.0.0" + "version": "1.0.0", + "sideEffects": false } \ No newline at end of file diff --git a/packages/react/kibana_context/README.mdx b/packages/react/kibana_context/README.mdx index 28fd9ecb1600f..8738461245e65 100644 --- a/packages/react/kibana_context/README.mdx +++ b/packages/react/kibana_context/README.mdx @@ -1,9 +1,9 @@ --- -id: react/context -slug: /react/context +id: kibDevReactKibanaContext +slug: /kibana-dev-docs/react-context title: React Contexts in Kibana description: Kibana uses React Context to manage several global states. This is a collection of packages supporting those states. -tags: ['shared-ux', 'react', 'context'] +tags: ['kibana', 'dev','shared-ux', 'react', 'context'] date: 2023-07-25 --- diff --git a/packages/shared-ux/button_toolbar/src/buttons/toolbar_button/toolbar_button.tsx b/packages/shared-ux/button_toolbar/src/buttons/toolbar_button/toolbar_button.tsx index 84039857d0891..1e005d110e6cf 100644 --- a/packages/shared-ux/button_toolbar/src/buttons/toolbar_button/toolbar_button.tsx +++ b/packages/shared-ux/button_toolbar/src/buttons/toolbar_button/toolbar_button.tsx @@ -23,7 +23,7 @@ type ButtonRenderStyle = 'standard' | 'iconButton'; interface ToolbarButtonCommonProps extends Pick< EuiButtonPropsForButton, - 'onClick' | 'iconType' | 'size' | 'data-test-subj' | 'isDisabled' + 'onClick' | 'iconType' | 'size' | 'data-test-subj' | 'isDisabled' | 'aria-label' > { /** * Render style of the toolbar button @@ -162,7 +162,7 @@ const ToolbarIconButton = ({ is rendered 1`] = `
-
- -
+ + +
`; diff --git a/scripts/check_ftr_code_owners.js b/scripts/check_ftr_code_owners.js new file mode 100644 index 0000000000000..67161a997e0bc --- /dev/null +++ b/scripts/check_ftr_code_owners.js @@ -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. + */ + +require('../src/setup_node_env'); +require('@kbn/test').runCheckFtrCodeOwnersCli(); diff --git a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts index 8748eb64196d7..88bf504e25d25 100644 --- a/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts +++ b/src/core/server/integration_tests/ci_checks/saved_objects/check_registered_types.test.ts @@ -77,6 +77,7 @@ describe('checking migration metadata changes on all registered SO types', () => "cases-oracle": "afd99cd22b5551ac336b7c0f30f9ee31aa2b9f20", "cases-telemetry": "f219eb7e26772884342487fc9602cfea07b3cedc", "cases-user-actions": "483f10db9b3bd1617948d7032a98b7791bf87414", + "cloud-security-posture-settings": "675e47dd958fbce6c70a20baac12af3145e7c0ef", "config": "179b3e2bc672626aafce3cf92093a113f456af38", "config-global": "8e8a134a2952df700d7d4ec51abb794bbd4cf6da", "connector_token": "5a9ac29fe9c740eb114e9c40517245c71706b005", @@ -107,7 +108,7 @@ describe('checking migration metadata changes on all registered SO types', () => "infrastructure-ui-source": "113182d6895764378dfe7fa9fa027244f3a457c4", "ingest-agent-policies": "7633e578f60c074f8267bc50ec4763845e431437", "ingest-download-sources": "279a68147e62e4d8858c09ad1cf03bd5551ce58d", - "ingest-outputs": "20bd44ce6016079c3b28f1b2bc241e7715be48f8", + "ingest-outputs": "e36a25e789f22b4494be728321f4304a040e286b", "ingest-package-policies": "f4c2767e852b700a8b82678925b86bac08958b43", "ingest_manager_settings": "64955ef1b7a9ffa894d4bb9cf863b5602bfa6885", "inventory-view": "b8683c8e352a286b4aca1ab21003115a4800af83", @@ -143,7 +144,7 @@ describe('checking migration metadata changes on all registered SO types', () => "siem-ui-timeline": "d3de8ff3617be8f2a799d66b1471b9be6124bf40", "siem-ui-timeline-note": "0a32fb776907f596bedca292b8c646496ae9c57b", "siem-ui-timeline-pinned-event": "082daa3ce647b33873f6abccf340bdfa32057c8d", - "slo": "2048ab6791df2e1ae0936f29c20765cb8d2fcfaa", + "slo": "9a9995e4572de1839651c43b5fc4dc8276bb5815", "space": "8de4ec513e9bbc6b2f1d635161d850be7747d38e", "spaces-usage-stats": "3abca98713c52af8b30300e386c7779b3025a20e", "synthetics-monitor": "33ddc4b8979f378edf58bcc7ba13c5c5b572f42d", diff --git a/src/core/server/integration_tests/saved_objects/migrations/group2/cleanup.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group2/cleanup.test.ts index 8ce37bd473a8a..026eaa462aa54 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group2/cleanup.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group2/cleanup.test.ts @@ -42,7 +42,7 @@ describe('migration v2', () => { }); it('clean ups if migration fails', async () => { - const { runMigrations, client } = await setupNextMinor(); + const { runMigrations } = await setupNextMinor(); await expect(runMigrations()).rejects.toThrowErrorMatchingInlineSnapshot(` "Unable to complete saved object migrations for the [${defaultKibanaIndex}] index: Migrations failed. Reason: 1 corrupt saved object documents were found: corrupt:2baf4de0-a6d4-11ed-ba5a-39196fc76e60 @@ -63,18 +63,6 @@ describe('migration v2', () => { ); expect(logRecordWithPit).toBeTruthy(); - - const pitId = logRecordWithPit.right.pitId; - expect(pitId).toBeTruthy(); - - await expect( - client.search({ - body: { - pit: { id: pitId }, - }, - }) - // throws an exception that cannot search with closed PIT - ).rejects.toThrow(/search_phase_execution_exception/); }); afterEach(async () => { diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/split_failed_to_clone.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/split_failed_to_clone.test.ts index da9657e1765d1..1642e4059a885 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group3/split_failed_to_clone.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/split_failed_to_clone.test.ts @@ -59,7 +59,8 @@ const RELOCATE_TYPES: Record = { export const logFilePath = Path.join(__dirname, 'split_failed_to_clone.test.log'); -describe('when splitting .kibana into multiple indices and one clone fails', () => { +// Failing: See https://github.com/elastic/kibana/issues/163253 +describe.skip('when splitting .kibana into multiple indices and one clone fails', () => { let esServer: TestElasticsearchUtils['es']; let typeRegistry: ISavedObjectTypeRegistry; let migratorTestKitFactory: () => Promise; diff --git a/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts index cf8461f07713b..1d6c5faca4be9 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group3/type_registrations.test.ts @@ -29,6 +29,7 @@ const previouslyRegisteredTypes = [ 'canvas-element', 'canvas-workpad', 'canvas-workpad-template', + 'cloud-security-posture-settings', 'cases', 'cases-comments', 'cases-configure', diff --git a/src/core/server/integration_tests/saved_objects/migrations/group5/dot_kibana_split.test.ts b/src/core/server/integration_tests/saved_objects/migrations/group5/dot_kibana_split.test.ts index 19856c39f8f86..2ee52b84ad13b 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/group5/dot_kibana_split.test.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/group5/dot_kibana_split.test.ts @@ -198,6 +198,7 @@ describe('split .kibana index into multiple system indices', () => { "cases-oracle", "cases-telemetry", "cases-user-actions", + "cloud-security-posture-settings", "config", "config-global", "connector_token", diff --git a/src/core/server/integration_tests/saved_objects/migrations/kibana_migrator_test_kit.ts b/src/core/server/integration_tests/saved_objects/migrations/kibana_migrator_test_kit.ts index 1f6e9a7a58c77..e88a876edbc63 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/kibana_migrator_test_kit.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/kibana_migrator_test_kit.ts @@ -12,7 +12,7 @@ import { SemVer } from 'semver'; import { defaultsDeep } from 'lodash'; import { BehaviorSubject, firstValueFrom, map } from 'rxjs'; -import { ConfigService, Env } from '@kbn/config'; +import { ConfigService, Env, BuildFlavor } from '@kbn/config'; import { getEnvOptions } from '@kbn/config-mocks'; import { REPO_ROOT } from '@kbn/repo-info'; import { KibanaMigrator } from '@kbn/core-saved-objects-migration-server-internal'; @@ -278,6 +278,7 @@ interface GetMigratorParams { loggerFactory: LoggerFactory; kibanaVersion: string; kibanaBranch: string; + buildFlavor?: BuildFlavor; nodeRoles: NodeRoles; } @@ -290,6 +291,7 @@ const getMigrator = async ({ loggerFactory, kibanaVersion, kibanaBranch, + buildFlavor = 'traditional', nodeRoles, }: GetMigratorParams) => { const savedObjectsConf = await firstValueFrom( @@ -301,8 +303,8 @@ const getMigrator = async ({ const soConfig = new SavedObjectConfig(savedObjectsConf, savedObjectsMigrationConf); const docLinks: DocLinksServiceStart = { - ...getDocLinksMeta({ kibanaBranch }), - links: getDocLinks({ kibanaBranch }), + ...getDocLinksMeta({ kibanaBranch, buildFlavor }), + links: getDocLinks({ kibanaBranch, buildFlavor }), }; const esCapabilities = await getCapabilitiesFromClient(client); diff --git a/src/core/server/integration_tests/saved_objects/migrations/test_utils.ts b/src/core/server/integration_tests/saved_objects/migrations/test_utils.ts index 610981bab56ab..85e2d7b318fc6 100644 --- a/src/core/server/integration_tests/saved_objects/migrations/test_utils.ts +++ b/src/core/server/integration_tests/saved_objects/migrations/test_utils.ts @@ -17,12 +17,18 @@ import JSON5 from 'json5'; export const getDocVersion = () => { const env = Env.createDefault(REPO_ROOT, getEnvOptions()); - return getDocLinksMeta({ kibanaBranch: env.packageInfo.branch }).version; + return getDocLinksMeta({ + kibanaBranch: env.packageInfo.branch, + buildFlavor: env.packageInfo.buildFlavor, + }).version; }; export const getMigrationDocLink = () => { const env = Env.createDefault(REPO_ROOT, getEnvOptions()); - const docLinks = getDocLinks({ kibanaBranch: env.packageInfo.branch }); + const docLinks = getDocLinks({ + kibanaBranch: env.packageInfo.branch, + buildFlavor: env.packageInfo.buildFlavor, + }); return docLinks.kibanaUpgradeSavedObjects; }; diff --git a/src/dev/license_checker/config.ts b/src/dev/license_checker/config.ts index 08d4267e52b63..b0919d0ab6141 100644 --- a/src/dev/license_checker/config.ts +++ b/src/dev/license_checker/config.ts @@ -85,7 +85,7 @@ export const LICENSE_OVERRIDES = { 'jsts@1.6.2': ['Eclipse Distribution License - v 1.0'], // cf. https://github.com/bjornharrtell/jsts '@mapbox/jsonlint-lines-primitives@2.0.2': ['MIT'], // license in readme https://github.com/tmcw/jsonlint '@elastic/ems-client@8.5.1': ['Elastic License 2.0'], - '@elastic/eui@90.0.1-backport.0': ['SSPL-1.0 OR Elastic License 2.0'], + '@elastic/eui@91.0.0-backport.0': ['SSPL-1.0 OR Elastic License 2.0'], 'language-subtag-registry@0.3.21': ['CC-BY-4.0'], // retired ODC‑By license https://github.com/mattcg/language-subtag-registry 'buffers@0.1.1': ['MIT'], // license in importing module https://www.npmjs.com/package/binary }; diff --git a/src/plugins/ai_assistant_management/observability/public/helpers/test_helper.tsx b/src/plugins/ai_assistant_management/observability/public/helpers/test_helper.tsx index 2e1b8e0def9b4..b03b1fd8a37d8 100644 --- a/src/plugins/ai_assistant_management/observability/public/helpers/test_helper.tsx +++ b/src/plugins/ai_assistant_management/observability/public/helpers/test_helper.tsx @@ -13,8 +13,7 @@ import { render as testLibRender } from '@testing-library/react'; import { coreMock } from '@kbn/core/public/mocks'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; import translations from '@kbn/translations-plugin/translations/ja-JP.json'; - -import { mockObservabilityAIAssistantService } from '@kbn/observability-ai-assistant-plugin/public'; +import { observabilityAIAssistantPluginMock } from '@kbn/observability-ai-assistant-plugin/public/mock'; import { RouterProvider } from '@kbn/typed-react-router-config'; import { AppContextProvider } from '../context/app_context'; import { RedirectToHomeIfUnauthorized } from '../routes/components/redirect_to_home_if_unauthorized'; @@ -62,14 +61,7 @@ export const render = (component: React.ReactNode, params?: { show: boolean }) = http: coreStart.http, application: coreStart.application, notifications: coreStart.notifications, - observabilityAIAssistant: { - service: mockObservabilityAIAssistantService, - useGenAIConnectors: () => ({ - loading: false, - selectConnector: () => {}, - reloadConnectors: () => {}, - }), - }, + observabilityAIAssistant: observabilityAIAssistantPluginMock.createStartContract(), uiSettings: coreStart.uiSettings, setBreadcrumbs: () => {}, }} diff --git a/src/plugins/ai_assistant_management/observability/public/hooks/use_create_knowledge_base_entry.ts b/src/plugins/ai_assistant_management/observability/public/hooks/use_create_knowledge_base_entry.ts index 5d367a8e66023..9c68437774255 100644 --- a/src/plugins/ai_assistant_management/observability/public/hooks/use_create_knowledge_base_entry.ts +++ b/src/plugins/ai_assistant_management/observability/public/hooks/use_create_knowledge_base_entry.ts @@ -29,7 +29,7 @@ export function useCreateKnowledgeBaseEntry() { { entry: Omit< KnowledgeBaseEntry, - '@timestamp' | 'confidence' | 'is_correction' | 'public' | 'labels' | 'role' + '@timestamp' | 'confidence' | 'is_correction' | 'public' | 'labels' | 'role' | 'doc_id' >; } >( diff --git a/src/plugins/ai_assistant_management/observability/public/routes/components/knowledge_base_edit_manual_entry_flyout.tsx b/src/plugins/ai_assistant_management/observability/public/routes/components/knowledge_base_edit_manual_entry_flyout.tsx index 80918e00a768d..aa973bb4db940 100644 --- a/src/plugins/ai_assistant_management/observability/public/routes/components/knowledge_base_edit_manual_entry_flyout.tsx +++ b/src/plugins/ai_assistant_management/observability/public/routes/components/knowledge_base_edit_manual_entry_flyout.tsx @@ -50,7 +50,6 @@ export function KnowledgeBaseEditManualEntryFlyout({ createEntry({ entry: { id: newEntryId, - doc_id: newEntryId, text: newEntryText, }, }).then(onClose); diff --git a/src/plugins/ai_assistant_management/observability/public/routes/components/knowledge_base_tab.test.tsx b/src/plugins/ai_assistant_management/observability/public/routes/components/knowledge_base_tab.test.tsx index a7abaca3210f6..4a5ad8daf96d5 100644 --- a/src/plugins/ai_assistant_management/observability/public/routes/components/knowledge_base_tab.test.tsx +++ b/src/plugins/ai_assistant_management/observability/public/routes/components/knowledge_base_tab.test.tsx @@ -80,7 +80,7 @@ describe('KnowledgeBaseTab', () => { getByTestId('knowledgeBaseEditManualEntryFlyoutSaveButton').click(); - expect(createMock).toHaveBeenCalledWith({ entry: { id: 'foo', doc_id: 'foo', text: '' } }); + expect(createMock).toHaveBeenCalledWith({ entry: { id: 'foo', text: '' } }); }); }); diff --git a/src/plugins/bfetch/common/index.ts b/src/plugins/bfetch/common/index.ts index caa3219541cc5..8f4b28c48e25f 100644 --- a/src/plugins/bfetch/common/index.ts +++ b/src/plugins/bfetch/common/index.ts @@ -16,4 +16,3 @@ export { DISABLE_BFETCH, BFETCH_ROUTE_VERSION_LATEST, } from './constants'; -export { BfetchRequestError } from './bfetch_error'; diff --git a/src/plugins/bfetch/common/util/normalize_error.ts b/src/plugins/bfetch/common/util/normalize_error.ts index ab34f89a9feed..0bbec5eb86e01 100644 --- a/src/plugins/bfetch/common/util/normalize_error.ts +++ b/src/plugins/bfetch/common/util/normalize_error.ts @@ -6,8 +6,8 @@ * Side Public License, v 1. */ +import { BfetchRequestError } from '@kbn/bfetch-error'; import { ErrorLike } from '../batch'; -import { BfetchRequestError } from '..'; export const normalizeError = (err: any): E => { if (!err) { diff --git a/src/plugins/bfetch/public/index.ts b/src/plugins/bfetch/public/index.ts index aba764781f78e..a4c60cdf127d7 100644 --- a/src/plugins/bfetch/public/index.ts +++ b/src/plugins/bfetch/public/index.ts @@ -15,7 +15,6 @@ export { split } from './streaming'; export type { BatchedFunc } from './batching/types'; export { DISABLE_BFETCH } from '../common/constants'; -export { BfetchRequestError } from '../common/bfetch_error'; export function plugin(initializerContext: PluginInitializerContext) { return new BfetchPublicPlugin(initializerContext); diff --git a/src/plugins/bfetch/public/streaming/from_streaming_xhr.ts b/src/plugins/bfetch/public/streaming/from_streaming_xhr.ts index 6e65e556f53c4..a2778f181f516 100644 --- a/src/plugins/bfetch/public/streaming/from_streaming_xhr.ts +++ b/src/plugins/bfetch/public/streaming/from_streaming_xhr.ts @@ -7,7 +7,7 @@ */ import { Observable, Subject } from 'rxjs'; -import { BfetchRequestError } from '../../common'; +import { BfetchRequestError } from '@kbn/bfetch-error'; /** * Creates observable from streaming XMLHttpRequest, where each event diff --git a/src/plugins/bfetch/tsconfig.json b/src/plugins/bfetch/tsconfig.json index 48e56a689b447..49e273f958e0a 100644 --- a/src/plugins/bfetch/tsconfig.json +++ b/src/plugins/bfetch/tsconfig.json @@ -11,6 +11,7 @@ "@kbn/config-schema", "@kbn/std", "@kbn/core-http-common", + "@kbn/bfetch-error", ], "exclude": [ "target/**/*", diff --git a/src/plugins/chart_expressions/expression_gauge/public/components/__snapshots__/gauge_component.test.tsx.snap b/src/plugins/chart_expressions/expression_gauge/public/components/__snapshots__/gauge_component.test.tsx.snap index 17eb7b358bb47..afd6ce21ad36f 100644 --- a/src/plugins/chart_expressions/expression_gauge/public/components/__snapshots__/gauge_component.test.tsx.snap +++ b/src/plugins/chart_expressions/expression_gauge/public/components/__snapshots__/gauge_component.test.tsx.snap @@ -6,7 +6,514 @@ exports[`GaugeComponent renders the chart 1`] = ` > diff --git a/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.tsx b/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.tsx index ea8342462f05e..6565673cea0da 100644 --- a/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.tsx +++ b/src/plugins/chart_expressions/expression_gauge/public/components/gauge_component.tsx @@ -261,8 +261,6 @@ export const GaugeComponent: FC = memo( return null; } - const chartTheme = chartsThemeService.useChartsTheme(); - const metricColumn = table.columns.find((col) => col.id === accessors.metric); const chartData = table.rows.filter( @@ -366,7 +364,7 @@ export const GaugeComponent: FC = memo( } debugState={window._echDebugStateFlag ?? false} - theme={[{ background: { color: 'transparent' } }, chartTheme]} + theme={[{ background: { color: 'transparent' } }]} baseTheme={chartBaseTheme} ariaLabel={args.ariaLabel} ariaUseDefaultSummary={!args.ariaLabel} diff --git a/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.test.tsx b/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.test.tsx index 083e6430614da..bf9524c186134 100644 --- a/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.test.tsx +++ b/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.test.tsx @@ -379,6 +379,63 @@ describe('HeatmapComponent', function () { }); }); + it('should keep the minimum open end', () => { + const newData: Datatable = { + type: 'datatable', + rows: [{ 'col-0-1': -3 }], + columns: [{ id: 'col-0-1', name: 'Count', meta: { type: 'number' } }], + }; + const newProps = { + ...wrapperProps, + data: newData, + args: { + ...wrapperProps.args, + palette: { + params: { + colors: ['#6092c0', '#a8bfda', '#ebeff5', '#ecb385', '#e7664c'], + stops: [1, 2, 3, 4, 5], + range: 'number', + gradient: true, + continuity: 'above', + rangeMin: -Infinity, + rangeMax: null, + }, + }, + }, + } as unknown as HeatmapRenderProps; + const component = mountWithIntl(); + expect(component.find(Heatmap).prop('colorScale')).toEqual({ + bands: [ + { + start: -Infinity, + end: 1, + color: '#6092c0', + }, + { + start: 1, + end: 2, + color: '#a8bfda', + }, + { + start: 2, + end: 3, + color: '#ebeff5', + }, + { + start: 3, + end: 4, + color: '#ecb385', + }, + { + start: 4, + end: Infinity, + color: '#e7664c', + }, + ], + type: 'bands', + }); + }); + it('renders the axis titles', () => { const component = shallowWithIntl(); expect(component.find(Heatmap).prop('xAxisTitle')).toEqual('Dest'); diff --git a/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.tsx b/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.tsx index f0794318e6001..d36f8f54b9365 100644 --- a/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.tsx +++ b/src/plugins/chart_expressions/expression_heatmap/public/components/heatmap_component.tsx @@ -97,11 +97,7 @@ function shiftAndNormalizeStops( if (params.range === 'percent') { result = min + ((max - min) * value) / 100; } - // a division by zero safeguard - if (!Number.isFinite(result)) { - return 1; - } - return Number(result.toFixed(2)); + return result; } ); } @@ -159,7 +155,6 @@ export const HeatmapComponent: FC = memo( overrides, }) => { const chartRef = useRef(null); - const chartTheme = chartsThemeService.useChartsTheme(); const isDarkTheme = chartsThemeService.useDarkMode(); // legacy heatmap legend is handled by the uiState const [showLegend, setShowLegend] = useState(() => { @@ -515,11 +510,9 @@ export const HeatmapComponent: FC = memo( let overwriteArrayIdx; if (endValue === Number.POSITIVE_INFINITY) { - overwriteArrayIdx = `≥ ${metricFormatter.convert(startValue)}`; + overwriteArrayIdx = `≥ ${valueFormatter(startValue)}`; } else { - overwriteArrayIdx = `${metricFormatter.convert(start)} - ${metricFormatter.convert( - endValue - )}`; + overwriteArrayIdx = `${valueFormatter(start)} - ${valueFormatter(endValue)}`; } const overwriteColor = overwriteColors?.[overwriteArrayIdx]; @@ -547,13 +540,8 @@ export const HeatmapComponent: FC = memo( grid: { stroke: { width: - args.gridConfig.strokeWidth ?? - chartTheme.axes?.gridLine?.horizontal?.strokeWidth ?? - 1, - color: - args.gridConfig.strokeColor ?? - chartTheme.axes?.gridLine?.horizontal?.stroke ?? - '#D3DAE6', + args.gridConfig.strokeWidth ?? chartBaseTheme.axes.gridLine.horizontal.strokeWidth, + color: args.gridConfig.strokeColor ?? chartBaseTheme.axes.gridLine.horizontal.stroke, }, }, cell: { @@ -572,13 +560,13 @@ export const HeatmapComponent: FC = memo( yAxisLabel: { visible: !!yAxisColumn && args.gridConfig.isYAxisLabelVisible, // eui color subdued - textColor: chartTheme.axes?.tickLabel?.fill ?? '#6a717d', + textColor: chartBaseTheme.axes.tickLabel.fill, padding: yAxisColumn?.name ? 8 : 0, }, xAxisLabel: { visible: Boolean(args.gridConfig.isXAxisLabelVisible && xAxisColumn), // eui color subdued - textColor: chartTheme.axes?.tickLabel?.fill ?? `#6a717d`, + textColor: chartBaseTheme.axes.tickLabel.fill, padding: xAxisColumn?.name ? 8 : 0, }, brushMask: { @@ -719,7 +707,6 @@ export const HeatmapComponent: FC = memo( debugState={window._echDebugStateFlag ?? false} theme={[ themeOverrides, - chartTheme, ...(Array.isArray(settingsThemeOverrides) ? settingsThemeOverrides : [settingsThemeOverrides]), diff --git a/src/plugins/chart_expressions/expression_heatmap/public/expression_renderers/heatmap_renderer.tsx b/src/plugins/chart_expressions/expression_heatmap/public/expression_renderers/heatmap_renderer.tsx index fa899fe3ce295..1d324ad63be55 100644 --- a/src/plugins/chart_expressions/expression_heatmap/public/expression_renderers/heatmap_renderer.tsx +++ b/src/plugins/chart_expressions/expression_heatmap/public/expression_renderers/heatmap_renderer.tsx @@ -8,6 +8,7 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; import { render, unmountComponentAtNode } from 'react-dom'; +import { getTimeZone } from '@kbn/visualization-utils'; import type { PersistedState } from '@kbn/visualizations-plugin/public'; import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import { ExpressionRenderDefinition } from '@kbn/expressions-plugin/common/expression_renderers'; @@ -29,7 +30,6 @@ import { getPaletteService, getUISettings, } from '../services'; -import { getTimeZone } from '../utils/get_timezone'; interface ExpressioHeatmapRendererDependencies { getStartDeps: StartServicesGetter; diff --git a/src/plugins/chart_expressions/expression_heatmap/tsconfig.json b/src/plugins/chart_expressions/expression_heatmap/tsconfig.json index 10e4ea05105a1..cdce4c3406ede 100644 --- a/src/plugins/chart_expressions/expression_heatmap/tsconfig.json +++ b/src/plugins/chart_expressions/expression_heatmap/tsconfig.json @@ -28,6 +28,7 @@ "@kbn/analytics", "@kbn/chart-expressions-common", "@kbn/i18n-react", + "@kbn/visualization-utils", ], "exclude": [ "target/**/*", diff --git a/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_trendline_function.test.ts b/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_trendline_function.test.ts index 6dcfe4c79147a..30f55a9679269 100644 --- a/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_trendline_function.test.ts +++ b/src/plugins/chart_expressions/expression_metric/common/expression_functions/metric_trendline_function.test.ts @@ -8,11 +8,10 @@ import { Datatable, ExecutionContext } from '@kbn/expressions-plugin/common'; import { Adapters } from '@kbn/inspector-plugin/common'; -import { SerializableRecord } from '@kbn/utility-types'; import { TrendlineArguments } from '../types'; import { metricTrendlineFunction } from './metric_trendline_function'; -const fakeContext = {} as ExecutionContext; +const fakeContext = {} as ExecutionContext; const fakeInput = {} as Datatable; const metricTrendline = (args: TrendlineArguments) => metricTrendlineFunction().fn(fakeInput, args, fakeContext); diff --git a/src/plugins/chart_expressions/expression_metric/public/__mocks__/theme_service.ts b/src/plugins/chart_expressions/expression_metric/public/__mocks__/theme_service.ts index b00ec8a6ee569..c3c23e41c01db 100644 --- a/src/plugins/chart_expressions/expression_metric/public/__mocks__/theme_service.ts +++ b/src/plugins/chart_expressions/expression_metric/public/__mocks__/theme_service.ts @@ -8,7 +8,6 @@ export const getThemeService = () => { return { - useChartsTheme: () => ({}), useChartsBaseTheme: () => ({ metric: { minHeight: 64 } }), }; }; diff --git a/src/plugins/chart_expressions/expression_metric/public/components/metric_vis.tsx b/src/plugins/chart_expressions/expression_metric/public/components/metric_vis.tsx index a0d02562d7623..ba3ef124d7ed2 100644 --- a/src/plugins/chart_expressions/expression_metric/public/components/metric_vis.tsx +++ b/src/plugins/chart_expressions/expression_metric/public/components/metric_vis.tsx @@ -154,7 +154,6 @@ export const MetricVis = ({ filterable, overrides, }: MetricVisComponentProps) => { - const chartTheme = getThemeService().useChartsTheme(); const onRenderChange = useCallback( (isRendered) => { if (isRendered) { @@ -167,8 +166,7 @@ export const MetricVis = ({ const [scrollChildHeight, setScrollChildHeight] = useState('100%'); const scrollContainerRef = useRef(null); const scrollDimensions = useResizeObserver(scrollContainerRef.current); - - const baseTheme = getThemeService().useChartsBaseTheme(); + const chartBaseTheme = getThemeService().useChartsBaseTheme(); const primaryMetricColumn = getColumnByAccessor(config.dimensions.metric, data.columns)!; const formatPrimaryMetric = getMetricFormatter(config.dimensions.metric, data.columns); @@ -275,7 +273,7 @@ export const MetricVis = ({ } = config; const numRows = metricConfigs.length / maxCols; - const minHeight = chartTheme.metric?.minHeight ?? baseTheme.metric.minHeight; + const minHeight = chartBaseTheme.metric.minHeight; useEffect(() => { const minimumRequiredVerticalSpace = minHeight * numRows; @@ -327,18 +325,18 @@ export const MetricVis = ({ locale={i18n.getLocale()} theme={[ { - background: { color: 'transparent' }, + background: { color: defaultColor }, metric: { - background: defaultColor, barBackground: euiThemeVars.euiColorLightShade, + emptyBackground: euiThemeVars.euiColorEmptyShade, + blendingBackground: euiThemeVars.euiColorEmptyShade, }, - ...chartTheme, }, ...(Array.isArray(settingsThemeOverrides) ? settingsThemeOverrides : [settingsThemeOverrides]), ]} - baseTheme={baseTheme} + baseTheme={chartBaseTheme} onRenderChange={onRenderChange} onElementClick={ filterable diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/__mocks__/theme.ts b/src/plugins/chart_expressions/expression_partition_vis/public/__mocks__/theme.ts index c66cd26fed719..7d141f15f5c7c 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/__mocks__/theme.ts +++ b/src/plugins/chart_expressions/expression_partition_vis/public/__mocks__/theme.ts @@ -5,12 +5,11 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { from } from 'rxjs'; + +import { themeServiceMock } from '@kbn/core/public/mocks'; import { ThemeService } from '@kbn/charts-plugin/public/services'; const theme = new ThemeService(); -theme.init({ - theme$: from([{ darkMode: false }]), -}); +theme.init(themeServiceMock.createSetupContract({ darkMode: false })); export { theme }; diff --git a/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap b/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap index 451f25f93f9e2..241e2ac6f4faf 100644 --- a/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap +++ b/src/plugins/chart_expressions/expression_partition_vis/public/components/__snapshots__/partition_vis_component.test.tsx.snap @@ -236,7 +236,514 @@ exports[`PartitionVisComponent should render correct structure for donut 1`] = ` /> - } - onElementClick={[Function]} - onRenderChange={[Function]} - showLegend={true} - theme={ - Array [ - Object { - "background": Object { - "color": "transparent", + baseTheme={ + Object { + "arcSeriesStyle": Object { + "arc": Object { + "opacity": 1, + "stroke": "black", + "strokeWidth": 1, + "visible": true, }, }, - Object { - "chartMargins": Object { - "bottom": 0, - "left": 0, - "right": 0, - "top": 0, + "areaSeriesStyle": Object { + "area": Object { + "opacity": 0.3, + "visible": true, }, - "partition": Object { - "circlePadding": 4, - "emptySizeRatio": 0, - "fontFamily": undefined, - "linkLabel": Object { - "fontSize": 11, - "maxCount": 5, - "maxTextLength": 100, - "textColor": undefined, + "fit": Object { + "area": Object { + "fill": "__use__series__color__", + "opacity": 0.15, + "visible": true, + }, + "line": Object { + "dash": Array [ + 5, + 5, + ], + "opacity": 1, + "stroke": "__use__series__color__", + "visible": true, }, - "maxFontSize": 16, - "minFontSize": 10, - "outerSizeRatio": undefined, - "sectorLineStroke": undefined, - "sectorLineWidth": 1.5, + }, + "isolatedPoint": Object { + "fill": "white", + "opacity": 1, + "radius": 2, + "stroke": "__use__series__color__", + "strokeWidth": 1, + "visible": true, + }, + "line": Object { + "opacity": 1, + "strokeWidth": 2, + "visible": true, + }, + "point": Object { + "fill": "#FFF", + "opacity": 1, + "radius": 3, + "stroke": "__use__series__color__", + "strokeWidth": 2, + "visible": false, }, }, - Object {}, - Object { - "legend": Object { - "labelOptions": Object { - "maxLines": 1, + "axes": Object { + "axisLine": Object { + "stroke": "#eaedf3", + "strokeWidth": 1, + "visible": true, + }, + "axisPanelTitle": Object { + "fill": "#333", + "fontFamily": "sans-serif", + "fontSize": 10, + "padding": Object { + "inner": 8, + "outer": 0, + }, + "visible": true, + }, + "axisTitle": Object { + "fill": "#343741", + "fontFamily": "Inter, BlinkMacSystemFont, Helvetica, Arial, sans-serif", + "fontSize": 12, + "padding": Object { + "inner": 10, + "outer": 0, + }, + "visible": true, + }, + "gridLine": Object { + "horizontal": Object { + "dash": Array [ + 0, + 0, + ], + "opacity": 1, + "stroke": "#eaedf3", + "strokeWidth": 1, + "visible": true, + }, + "lumaSteps": Array [ + 224, + 184, + 128, + 96, + 64, + 32, + 16, + 8, + 4, + 2, + 1, + 0, + 0, + 0, + 0, + 0, + ], + "vertical": Object { + "dash": Array [ + 4, + 4, + ], + "opacity": 1, + "stroke": "#eaedf3", + "strokeWidth": 1, + "visible": true, + }, + }, + "tickLabel": Object { + "alignment": Object { + "horizontal": "near", + "vertical": "near", }, + "fill": "#646a77", + "fontFamily": "Inter, BlinkMacSystemFont, Helvetica, Arial, sans-serif", + "fontSize": 10, + "fontStyle": "normal", + "offset": Object { + "reference": "local", + "x": 0, + "y": 0, + }, + "padding": Object { + "inner": 10, + "outer": 8, + }, + "rotation": 0, + "visible": true, + }, + "tickLine": Object { + "padding": 10, + "size": 10, + "stroke": "#eaedf3", + "strokeWidth": 1, + "visible": false, }, }, - Object {}, - ] - } - /> - + } + onElementClick={[Function]} + onRenderChange={[Function]} + showLegend={true} + theme={ + Array [ + Object { + "background": Object { + "color": "transparent", + }, + }, + Object { + "chartMargins": Object { + "bottom": 0, + "left": 0, + "right": 0, + "top": 0, + }, + "partition": Object { + "circlePadding": 4, + "emptySizeRatio": 0, + "fontFamily": "Inter, BlinkMacSystemFont, Helvetica, Arial, sans-serif", + "linkLabel": Object { + "fontSize": 11, + "maxCount": 5, + "maxTextLength": 100, + "textColor": "#343741", + }, + "maxFontSize": 16, + "minFontSize": 10, + "outerSizeRatio": undefined, + "sectorLineStroke": "#FFF", + "sectorLineWidth": 1.5, + }, + }, + Object { + "legend": Object { + "labelOptions": Object { + "maxLines": 1, + }, + }, + }, + Object {}, + ] + } + /> + { hasOpenedOnAggBasedEditor, } = props; const visParams = useMemo(() => filterOutConfig(visType, preVisParams), [preVisParams, visType]); - const chartTheme = props.chartsThemeService.useChartsTheme(); const chartBaseTheme = props.chartsThemeService.useChartsBaseTheme(); const { @@ -377,12 +376,19 @@ const PartitionVisComponent = (props: PartitionVisComponentProps) => { getPartitionTheme( visType, visParams, - chartTheme, + chartBaseTheme, containerDimensions, rescaleFactor, hasOpenedOnAggBasedEditor ), - [visType, visParams, chartTheme, containerDimensions, rescaleFactor, hasOpenedOnAggBasedEditor] + [ + visType, + visParams, + chartBaseTheme, + containerDimensions, + rescaleFactor, + hasOpenedOnAggBasedEditor, + ] ); const fixedViewPort = document.getElementById('app-fixed-viewport'); @@ -574,7 +580,6 @@ const PartitionVisComponent = (props: PartitionVisComponentProps) => { // Chart background should be transparent for the usage at Canvas. { background: { color: 'transparent' } }, themeOverrides, - chartTheme, { legend: { labelOptions: { diff --git a/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx b/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx index a00bbb93e4a58..0c7f855c66390 100644 --- a/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx +++ b/src/plugins/chart_expressions/expression_tagcloud/public/components/tagcloud_component.tsx @@ -12,7 +12,13 @@ import { i18n } from '@kbn/i18n'; import { throttle } from 'lodash'; import { EuiIconTip, EuiResizeObserver } from '@elastic/eui'; import { IconChartTagcloud } from '@kbn/chart-icons'; -import { Chart, Settings, Wordcloud, RenderChangeListener } from '@elastic/charts'; +import { + Chart, + Settings, + Wordcloud, + RenderChangeListener, + LEGACY_LIGHT_THEME, +} from '@elastic/charts'; import { EmptyPlaceholder } from '@kbn/charts-plugin/public'; import { PaletteRegistry, @@ -234,6 +240,8 @@ export const TagCloudChart = ({
(null); - const chartTheme = chartsThemeService.useChartsTheme(); const chartBaseTheme = chartsThemeService.useChartsBaseTheme(); const darkMode = chartsThemeService.useDarkMode(); const filteredLayers = getFilteredLayers(layers); @@ -796,9 +796,7 @@ export function XYChart({ legendSize={LegendSizeToPixels[legend.legendSize ?? DEFAULT_LEGEND_SIZE]} theme={[ { - ...chartTheme, barSeriesStyle: { - ...chartTheme.barSeriesStyle, ...valueLabelsStyling, }, background: { @@ -809,7 +807,8 @@ export function XYChart({ }, // if not title or labels are shown for axes, add some padding if required by reference line markers chartMargins: { - ...chartTheme.chartPaddings, + // Temporary margin defaults + ...LEGACY_LIGHT_THEME.chartMargins, ...computeChartMargins( linesPaddings, { ...tickLabelsVisibilitySettings, x: xAxisConfig?.showLabels }, @@ -993,9 +992,9 @@ export function XYChart({ rangeAnnotations.length && shouldHideDetails ? OUTSIDE_RECT_ANNOTATION_WIDTH_SUGGESTION : shouldUseNewTimeAxis - ? Number(MULTILAYER_TIME_AXIS_STYLE.tickLine?.padding || 0) + - Number(chartTheme.axes?.tickLabel?.fontSize || 0) - : Number(chartTheme.axes?.tickLine?.size) || OUTSIDE_RECT_ANNOTATION_WIDTH + ? Number(MULTILAYER_TIME_AXIS_STYLE.tickLine?.padding ?? 0) + + chartBaseTheme.axes.tickLabel.fontSize + : Math.max(chartBaseTheme.axes.tickLine.size, OUTSIDE_RECT_ANNOTATION_WIDTH) } /> ) : null} diff --git a/src/plugins/charts/common/static/styles/multilayer_timeaxis.ts b/src/plugins/charts/common/static/styles/multilayer_timeaxis.ts index 478cc7b52a73a..4b22feaa92751 100644 --- a/src/plugins/charts/common/static/styles/multilayer_timeaxis.ts +++ b/src/plugins/charts/common/static/styles/multilayer_timeaxis.ts @@ -20,7 +20,7 @@ export const MULTILAYER_TIME_AXIS_STYLE: RecursivePartial = { }, tickLine: { visible: true, - size: 0.0001, + size: 0, padding: 4, }, }; diff --git a/src/plugins/charts/public/services/theme/README.md b/src/plugins/charts/public/services/theme/README.md index fb4f941f79344..eca74a14019aa 100644 --- a/src/plugins/charts/public/services/theme/README.md +++ b/src/plugins/charts/public/services/theme/README.md @@ -8,11 +8,7 @@ The `theme` service offers utilities to interact with the kibana theme. EUI prov Default `baseTheme` from `@elastic/charts` (i.e. light). -## `chartsDefaultTheme` - -Default `theme` from `@elastic/eui` (i.e. light). - -## `useChartsTheme` and `useChartsBaseTheme` +## `useChartsBaseTheme` A **React hook** for simple fetching of the correct EUI `theme` and `baseTheme`. @@ -23,7 +19,7 @@ import { Chart, Settings } from '@elastic/charts'; export const YourComponent = () => ( {/* ... */} @@ -31,20 +27,18 @@ export const YourComponent = () => ( ); ``` -## `chartsTheme$` and `chartsBaseTheme$` +## `chartsBaseTheme$` An **`Observable`** of the current charts `theme` and `baseTheme`. Use this implementation for more flexible updates to the chart theme without full page refreshes. ```tsx import { npStart } from 'ui/new_platform'; -import { EuiChartThemeType } from '@elastic/eui/src/themes/charts/themes'; import { Subscription, combineLatest } from 'rxjs'; import { Chart, Settings, Theme } from '@elastic/charts'; interface YourComponentProps {}; interface YourComponentState { - chartsTheme: EuiChartThemeType['theme']; chartsBaseTheme: Theme; } @@ -52,16 +46,14 @@ export class YourComponent extends Component - this.setState({ chartsTheme, chartsBaseTheme }) + ).subscribe(([chartsBaseTheme]) => + this.setState({ chartsBaseTheme }) ); } @@ -72,21 +64,14 @@ export class YourComponent extends Component - + {/* ... */} ); } } ``` - -## Why have `theme` and `baseTheme`? - -The `theme` prop is a recursive partial `Theme` that overrides properties from the `baseTheme`. This allows changes to the `Theme` TS type in `@elastic/charts` without having to update the `@elastic/eui` themes for every ``. diff --git a/src/plugins/charts/public/services/theme/mock.ts b/src/plugins/charts/public/services/theme/mock.ts index ec5d00508c510..d41f9f20dd382 100644 --- a/src/plugins/charts/public/services/theme/mock.ts +++ b/src/plugins/charts/public/services/theme/mock.ts @@ -6,14 +6,11 @@ * Side Public License, v 1. */ -import { EUI_CHARTS_THEME_LIGHT } from '@elastic/eui/dist/eui_charts_theme'; +import { LIGHT_THEME } from '@elastic/charts'; import { ThemeService } from './theme'; export const themeServiceMock: ThemeService = { - chartsDefaultTheme: EUI_CHARTS_THEME_LIGHT.theme, - chartsTheme$: jest.fn(() => ({ - subscribe: jest.fn(), - })), + chartsDefaultBaseTheme: LIGHT_THEME, chartsBaseTheme$: jest.fn(() => ({ subscribe: jest.fn(), })), @@ -21,6 +18,5 @@ export const themeServiceMock: ThemeService = { subscribe: jest.fn(), })), useDarkMode: jest.fn().mockReturnValue(false), - useChartsTheme: jest.fn().mockReturnValue({}), - useChartsBaseTheme: jest.fn().mockReturnValue({}), + useChartsBaseTheme: jest.fn().mockReturnValue(LIGHT_THEME), } as any; diff --git a/src/plugins/charts/public/services/theme/theme.test.tsx b/src/plugins/charts/public/services/theme/theme.test.tsx index d13050d0a506f..d71d2beda6bb0 100644 --- a/src/plugins/charts/public/services/theme/theme.test.tsx +++ b/src/plugins/charts/public/services/theme/theme.test.tsx @@ -13,7 +13,6 @@ import { renderHook, act } from '@testing-library/react-hooks'; import { render, act as renderAct } from '@testing-library/react'; import { LIGHT_THEME, DARK_THEME } from '@elastic/charts'; -import { EUI_CHARTS_THEME_DARK, EUI_CHARTS_THEME_LIGHT } from '@elastic/eui/dist/eui_charts_theme'; import { ThemeService } from './theme'; import { coreMock } from '@kbn/core/public/mocks'; @@ -51,31 +50,6 @@ describe('ThemeService', () => { }); }); - describe('chartsTheme$', () => { - it('returns the light theme when not in dark mode', async () => { - setUpMockTheme.theme$ = createTheme$Mock(false); - const themeService = new ThemeService(); - themeService.init(setUpMockTheme); - - expect(await themeService.chartsTheme$.pipe(take(1)).toPromise()).toEqual( - EUI_CHARTS_THEME_LIGHT.theme - ); - }); - - describe('in dark mode', () => { - it(`returns the dark theme`, async () => { - // Fake dark theme turned returning true - setUpMockTheme.theme$ = createTheme$Mock(true); - const themeService = new ThemeService(); - themeService.init(setUpMockTheme); - - expect(await themeService.chartsTheme$.pipe(take(1)).toPromise()).toEqual( - EUI_CHARTS_THEME_DARK.theme - ); - }); - }); - }); - describe('chartsBaseTheme$', () => { it('returns the light theme when not in dark mode', async () => { setUpMockTheme.theme$ = createTheme$Mock(false); @@ -98,60 +72,6 @@ describe('ThemeService', () => { }); }); - describe('useChartsTheme', () => { - it('updates when the user profile settings change', () => { - setUpMockTheme.theme$ = createTheme$Mock(false); - const themeService = new ThemeService(); - themeService.init(setUpMockTheme); - const { useChartsTheme } = themeService; - - const { result } = renderHook(() => useChartsTheme()); - expect(result.current).toBe(EUI_CHARTS_THEME_LIGHT.theme); - - act(() => { - setUpMockTheme.theme$ = createTheme$Mock(true); - themeService.init(setUpMockTheme); - }); - expect(result.current).toBe(EUI_CHARTS_THEME_DARK.theme); - act(() => { - setUpMockTheme.theme$ = createTheme$Mock(false); - themeService.init(setUpMockTheme); - }); - expect(result.current).toBe(EUI_CHARTS_THEME_LIGHT.theme); - }); - - it('should not rerender when emitting the same value', () => { - setUpMockTheme.theme$ = createTheme$Mock(false); - const themeService = new ThemeService(); - themeService.init(setUpMockTheme); - const { useChartsTheme } = themeService; - - const renderCounter = jest.fn(); - const Wrapper = () => { - useChartsTheme(); - renderCounter(); - return null; - }; - - render(); - expect(renderCounter).toHaveBeenCalledTimes(1); - renderAct(() => { - setUpMockTheme.theme$ = createTheme$Mock(true); - themeService.init(setUpMockTheme); - }); - expect(renderCounter).toHaveBeenCalledTimes(2); - renderAct(() => { - setUpMockTheme.theme$ = createTheme$Mock(true); - themeService.init(setUpMockTheme); - }); - renderAct(() => { - setUpMockTheme.theme$ = createTheme$Mock(true); - themeService.init(setUpMockTheme); - }); - expect(renderCounter).toHaveBeenCalledTimes(2); - }); - }); - describe('useBaseChartTheme', () => { it('updates when the theme setting change', () => { setUpMockTheme.theme$ = createTheme$Mock(false); diff --git a/src/plugins/charts/public/services/theme/theme.ts b/src/plugins/charts/public/services/theme/theme.ts index dff300a02aa8a..b8ee2301dbb82 100644 --- a/src/plugins/charts/public/services/theme/theme.ts +++ b/src/plugins/charts/public/services/theme/theme.ts @@ -11,20 +11,14 @@ import { Observable, BehaviorSubject } from 'rxjs'; import { CoreSetup, CoreTheme } from '@kbn/core/public'; import { DARK_THEME, LIGHT_THEME, PartialTheme, Theme } from '@elastic/charts'; -import { EUI_CHARTS_THEME_DARK, EUI_CHARTS_THEME_LIGHT } from '@elastic/eui/dist/eui_charts_theme'; export class ThemeService { /** Returns default charts theme */ - public readonly chartsDefaultTheme = EUI_CHARTS_THEME_LIGHT.theme; public readonly chartsDefaultBaseTheme = LIGHT_THEME; private theme$?: Observable; - private _chartsTheme$ = new BehaviorSubject(this.chartsDefaultTheme); private _chartsBaseTheme$ = new BehaviorSubject(this.chartsDefaultBaseTheme); - /** An observable of the current charts theme */ - public chartsTheme$ = this._chartsTheme$.asObservable(); - /** An observable of the current charts base theme */ public chartsBaseTheme$ = this._chartsBaseTheme$.asObservable(); @@ -51,22 +45,11 @@ export class ThemeService { return value; }; - /** A React hook for consuming the charts theme */ + /** + * @deprecated No longer need to use theme on top of baseTheme, see https://github.com/elastic/kibana/pull/170914#issuecomment-1823014121 + */ public useChartsTheme = (): PartialTheme => { - const [value, update] = useState(this._chartsTheme$.getValue()); - const ref = useRef(value); - - useEffect(() => { - const s = this.chartsTheme$.subscribe((val) => { - if (val !== ref.current) { - ref.current = val; - update(val); - } - }); - return () => s.unsubscribe(); - }, []); - - return value; + return {}; }; /** A React hook for consuming the charts theme */ @@ -91,8 +74,6 @@ export class ThemeService { public init(theme: CoreSetup['theme']) { this.theme$ = theme.theme$; this.theme$.subscribe(({ darkMode }) => { - const selectedTheme = darkMode ? EUI_CHARTS_THEME_DARK.theme : EUI_CHARTS_THEME_LIGHT.theme; - this._chartsTheme$.next(selectedTheme); this._chartsBaseTheme$.next(darkMode ? DARK_THEME : LIGHT_THEME); }); } diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/inference.delete_model.json b/src/plugins/console/server/lib/spec_definitions/json/generated/inference.delete_model.json new file mode 100644 index 0000000000000..c7066c0a48b19 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/inference.delete_model.json @@ -0,0 +1,27 @@ +{ + "inference.delete_model": { + "url_params": { + "error_trace": "__flag__", + "filter_path": [], + "human": "__flag__", + "pretty": "__flag__" + }, + "url_components": { + "task_type": [ + "sparse_embedding", + "text_embedding" + ] + }, + "methods": [ + "DELETE" + ], + "patterns": [ + "_inference/{task_type}/{model_id}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/delete-inference-api.html", + "availability": { + "stack": true, + "serverless": false + } + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/inference.get_model.json b/src/plugins/console/server/lib/spec_definitions/json/generated/inference.get_model.json new file mode 100644 index 0000000000000..40745a385fa75 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/inference.get_model.json @@ -0,0 +1,27 @@ +{ + "inference.get_model": { + "url_params": { + "error_trace": "__flag__", + "filter_path": [], + "human": "__flag__", + "pretty": "__flag__" + }, + "url_components": { + "task_type": [ + "sparse_embedding", + "text_embedding" + ] + }, + "methods": [ + "GET" + ], + "patterns": [ + "_inference/{task_type}/{model_id}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/get-inference-api.html", + "availability": { + "stack": true, + "serverless": false + } + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/inference.inference.json b/src/plugins/console/server/lib/spec_definitions/json/generated/inference.inference.json new file mode 100644 index 0000000000000..e8f959aa3330a --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/inference.inference.json @@ -0,0 +1,27 @@ +{ + "inference.inference": { + "url_params": { + "error_trace": "__flag__", + "filter_path": [], + "human": "__flag__", + "pretty": "__flag__" + }, + "url_components": { + "task_type": [ + "sparse_embedding", + "text_embedding" + ] + }, + "methods": [ + "POST" + ], + "patterns": [ + "_inference/{task_type}/{model_id}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/post-inference-api.html", + "availability": { + "stack": true, + "serverless": false + } + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/inference.put_model.json b/src/plugins/console/server/lib/spec_definitions/json/generated/inference.put_model.json new file mode 100644 index 0000000000000..69e26a1103c02 --- /dev/null +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/inference.put_model.json @@ -0,0 +1,27 @@ +{ + "inference.put_model": { + "url_params": { + "error_trace": "__flag__", + "filter_path": [], + "human": "__flag__", + "pretty": "__flag__" + }, + "url_components": { + "task_type": [ + "sparse_embedding", + "text_embedding" + ] + }, + "methods": [ + "PUT" + ], + "patterns": [ + "_inference/{task_type}/{model_id}" + ], + "documentation": "https://www.elastic.co/guide/en/elasticsearch/reference/master/put-inference-api.html", + "availability": { + "stack": true, + "serverless": false + } + } +} diff --git a/src/plugins/console/server/lib/spec_definitions/json/generated/ml.start_trained_model_deployment.json b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.start_trained_model_deployment.json index 972ae313c20fb..f09c45526e4f2 100644 --- a/src/plugins/console/server/lib/spec_definitions/json/generated/ml.start_trained_model_deployment.json +++ b/src/plugins/console/server/lib/spec_definitions/json/generated/ml.start_trained_model_deployment.json @@ -6,6 +6,7 @@ "human": "__flag__", "pretty": "__flag__", "cache_size": [], + "deployment_id": "", "number_of_allocations": [ "1" ], diff --git a/src/plugins/content_management/docs/conent_management_landing.mdx b/src/plugins/content_management/docs/conent_management_landing.mdx new file mode 100644 index 0000000000000..b2855d105c7db --- /dev/null +++ b/src/plugins/content_management/docs/conent_management_landing.mdx @@ -0,0 +1,22 @@ +--- +id: kibContentManagement +slug: /kibana-dev-docs/content-management +title: Content Management +date: 2023-04-13 +tags: ['kibana', 'dev', 'content management', 'saved objects'] +--- + + + +Kibana Saved Objects have traditionally been considered content, +but the content management project aims to provide a single abstraction for Kibana and solution content. +This includes the registration and collection of content metadata as well as the rendering capabilities, caching layer, backward compatible CRUD & Search APIs, +and eventually a collection of new features that enhance content items by adding additional capabilities for personalization, private objects and content folders. + +## Links + +- +- diff --git a/src/plugins/content_management/server/plugin.ts b/src/plugins/content_management/server/plugin.ts index 4913c5cac08f6..6a276147687c1 100755 --- a/src/plugins/content_management/server/plugin.ts +++ b/src/plugins/content_management/server/plugin.ts @@ -19,13 +19,20 @@ import type { Context as RpcContext } from './rpc'; import { ContentManagementServerSetup, ContentManagementServerStart, - SetupDependencies, + ContentManagementServerSetupDependencies, + ContentManagementServerStartDependencies, } from './types'; import { EventStreamService } from './event_stream'; import { procedureNames } from '../common/rpc'; export class ContentManagementPlugin - implements Plugin + implements + Plugin< + ContentManagementServerSetup, + ContentManagementServerStart, + ContentManagementServerSetupDependencies, + ContentManagementServerStartDependencies + > { private readonly logger: Logger; private readonly core: Core; diff --git a/src/plugins/content_management/server/types.ts b/src/plugins/content_management/server/types.ts index 3d11f487fbe7d..79251838b1a56 100644 --- a/src/plugins/content_management/server/types.ts +++ b/src/plugins/content_management/server/types.ts @@ -9,7 +9,10 @@ import { CoreApi } from './core'; // eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface SetupDependencies {} +export interface ContentManagementServerSetupDependencies {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ContentManagementServerStartDependencies {} // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface ContentManagementServerSetup extends CoreApi {} diff --git a/src/plugins/controls/common/options_list/ip_search.ts b/src/plugins/controls/common/options_list/ip_search.ts index f371fbb1f6506..565c2ed2a1df6 100644 --- a/src/plugins/controls/common/options_list/ip_search.ts +++ b/src/plugins/controls/common/options_list/ip_search.ts @@ -17,6 +17,10 @@ interface IpSegments { type: 'ipv4' | 'ipv6' | 'unknown'; } +export const getIsValidFullIp = (searchString: string) => { + return ipaddr.IPv4.isValidFourPartDecimal(searchString) || ipaddr.IPv6.isValid(searchString); +}; + export const getIpSegments = (searchString: string): IpSegments => { if (searchString.indexOf('.') !== -1) { // ipv4 takes priority - so if search string contains both `.` and `:` then it will just be an invalid ipv4 search diff --git a/src/plugins/controls/common/options_list/is_valid_search.test.ts b/src/plugins/controls/common/options_list/is_valid_search.test.ts new file mode 100644 index 0000000000000..0334e1eb809e7 --- /dev/null +++ b/src/plugins/controls/common/options_list/is_valid_search.test.ts @@ -0,0 +1,118 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { isValidSearch } from './is_valid_search'; + +describe('test validity of search strings', () => { + describe('number field', () => { + it('valid search - basic integer', () => { + expect(isValidSearch({ searchString: '123', fieldType: 'number' })).toBe(true); + }); + + it('valid search - floating point number', () => { + expect(isValidSearch({ searchString: '12.34', fieldType: 'number' })).toBe(true); + }); + + it('valid search - negative number', () => { + expect(isValidSearch({ searchString: '-42', fieldType: 'number' })).toBe(true); + }); + + it('invalid search - invalid character search string', () => { + expect(isValidSearch({ searchString: '1!a23', fieldType: 'number' })).toBe(false); + }); + }); + + // we do not currently support searching date fields, so they will always be invalid + describe('date field', () => { + it('invalid search - formatted date', () => { + expect(isValidSearch({ searchString: 'December 12, 2023', fieldType: 'date' })).toBe(false); + }); + + it('invalid search - invalid character search string', () => { + expect(isValidSearch({ searchString: '!!12/12/23?', fieldType: 'date' })).toBe(false); + }); + }); + + // only testing exact match validity here - the remainder of testing is covered by ./ip_search.test.ts + describe('ip field', () => { + it('valid search - ipv4', () => { + expect( + isValidSearch({ + searchString: '1.2.3.4', + fieldType: 'ip', + searchTechnique: 'exact', + }) + ).toBe(true); + }); + + it('valid search - full ipv6', () => { + expect( + isValidSearch({ + searchString: 'fbbe:a363:9e14:987c:49cf:d4d0:d8c8:bc42', + fieldType: 'ip', + searchTechnique: 'exact', + }) + ).toBe(true); + }); + + it('valid search - partial ipv6', () => { + expect( + isValidSearch({ + searchString: 'fbbe:a363::', + fieldType: 'ip', + searchTechnique: 'exact', + }) + ).toBe(true); + }); + + it('invalid search - invalid character search string', () => { + expect( + isValidSearch({ + searchString: '!!123.abc?', + fieldType: 'ip', + searchTechnique: 'exact', + }) + ).toBe(false); + }); + + it('invalid search - ipv4', () => { + expect( + isValidSearch({ + searchString: '1.2.3.256', + fieldType: 'ip', + searchTechnique: 'exact', + }) + ).toBe(false); + }); + + it('invalid search - ipv6', () => { + expect( + isValidSearch({ + searchString: '::fbbe:a363::', + fieldType: 'ip', + searchTechnique: 'exact', + }) + ).toBe(false); + }); + }); + + // string field searches can never be invalid + describe('string field', () => { + it('valid search - basic search string', () => { + expect(isValidSearch({ searchString: 'abc', fieldType: 'string' })).toBe(true); + }); + + it('valid search - numeric search string', () => { + expect(isValidSearch({ searchString: '123', fieldType: 'string' })).toBe(true); + }); + + it('valid search - complex search string', () => { + expect(isValidSearch({ searchString: '!+@abc*&[]', fieldType: 'string' })).toBe(true); + }); + }); +}); diff --git a/src/plugins/controls/common/options_list/is_valid_search.ts b/src/plugins/controls/common/options_list/is_valid_search.ts new file mode 100644 index 0000000000000..2d69271f991fc --- /dev/null +++ b/src/plugins/controls/common/options_list/is_valid_search.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { getIpRangeQuery, getIsValidFullIp } from './ip_search'; +import { OptionsListSearchTechnique } from './suggestions_searching'; + +/** + * ipaddr is a fairly large library - therefore, this function needs to be separate from + * the `suggestions_searching` file (which is used in the OptionsListEditorOptions component, + * which is in the factory and not async imported) + */ + +export const isValidSearch = ({ + searchString, + fieldType, + searchTechnique, +}: { + searchString?: string; + fieldType?: string; + searchTechnique?: OptionsListSearchTechnique; +}): boolean => { + if (!searchString || searchString.length === 0) return true; + + switch (fieldType) { + case 'number': { + return !isNaN(Number(searchString)); + } + case 'date': { + /** searching is not currently supported for date fields */ + return false; + } + case 'ip': { + if (searchTechnique === 'exact') { + /** + * exact match searching will throw an error if the search string isn't a **full** IP, + * so we need a slightly different validity check here than for other search techniques + */ + return getIsValidFullIp(searchString); + } + return getIpRangeQuery(searchString).validSearch; + } + default: { + /** string searches are always considered to be valid */ + return true; + } + } +}; diff --git a/src/plugins/controls/common/options_list/suggestions_searching.ts b/src/plugins/controls/common/options_list/suggestions_searching.ts new file mode 100644 index 0000000000000..a68788bba322e --- /dev/null +++ b/src/plugins/controls/common/options_list/suggestions_searching.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export type OptionsListSearchTechnique = 'prefix' | 'wildcard' | 'exact'; + +export const getDefaultSearchTechnique = (type: string): OptionsListSearchTechnique | undefined => { + const compatibleSearchTechniques = getCompatibleSearchTechniques(type); + return compatibleSearchTechniques.length > 0 ? compatibleSearchTechniques[0] : undefined; +}; + +export const getCompatibleSearchTechniques = (type?: string): OptionsListSearchTechnique[] => { + switch (type) { + case 'string': { + return ['prefix', 'wildcard', 'exact']; + } + case 'ip': { + return ['prefix', 'exact']; + } + case 'number': { + return ['exact']; + } + default: { + return []; + } + } +}; diff --git a/src/plugins/controls/common/options_list/types.ts b/src/plugins/controls/common/options_list/types.ts index 460f7df080a88..d7f75d5c268c2 100644 --- a/src/plugins/controls/common/options_list/types.ts +++ b/src/plugins/controls/common/options_list/types.ts @@ -6,17 +6,15 @@ * Side Public License, v 1. */ -import { FieldSpec, DataView, RuntimeFieldSpec } from '@kbn/data-views-plugin/common'; -import type { Filter, Query, BoolQuery, TimeRange } from '@kbn/es-query'; +import { DataView, FieldSpec, RuntimeFieldSpec } from '@kbn/data-views-plugin/common'; +import type { BoolQuery, Filter, Query, TimeRange } from '@kbn/es-query'; -import type { OptionsListSortingType } from './suggestions_sorting'; import type { DataControlInput } from '../types'; +import { OptionsListSearchTechnique } from './suggestions_searching'; +import type { OptionsListSortingType } from './suggestions_sorting'; export const OPTIONS_LIST_CONTROL = 'optionsListControl'; -export type OptionsListSearchTechnique = 'prefix' | 'wildcard'; -export const OPTIONS_LIST_DEFAULT_SEARCH_TECHNIQUE: OptionsListSearchTechnique = 'prefix'; - export interface OptionsListEmbeddableInput extends DataControlInput { searchTechnique?: OptionsListSearchTechnique; sort?: OptionsListSortingType; diff --git a/src/plugins/controls/common/range_slider/mocks.tsx b/src/plugins/controls/common/range_slider/mocks.tsx index 049e882a32681..d656102c26cf9 100644 --- a/src/plugins/controls/common/range_slider/mocks.tsx +++ b/src/plugins/controls/common/range_slider/mocks.tsx @@ -5,8 +5,15 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ - import { RangeSliderEmbeddableInput } from '..'; +import { + ControlFactory, + ControlOutput, + RangeSliderEmbeddable, + RangeSliderEmbeddableFactory, +} from '../../public'; +import * as rangeSliderStateModule from '../../public/range_slider/range_slider_reducers'; +import { RangeSliderComponentState } from '../../public/range_slider/types'; export const mockRangeSliderEmbeddableInput = { id: 'sample options list', @@ -14,3 +21,40 @@ export const mockRangeSliderEmbeddableInput = { dataViewId: 'sample id', value: ['0', '10'], } as RangeSliderEmbeddableInput; + +const mockRangeSliderComponentState = { + field: { name: 'bytes', type: 'number', aggregatable: true }, + min: undefined, + max: undefined, + error: undefined, + isInvalid: false, +} as RangeSliderComponentState; + +const mockRangeSliderOutput = { + loading: false, +} as ControlOutput; + +export const mockRangeSliderEmbeddable = async (partialState?: { + explicitInput?: Partial; + componentState?: Partial; +}) => { + const rangeSliderFactoryStub = new RangeSliderEmbeddableFactory(); + const rangeSliderControlFactory = rangeSliderFactoryStub as unknown as ControlFactory; + rangeSliderControlFactory.getDefaultInput = () => ({}); + + // initial component state can be provided by overriding the defaults. + const initialComponentState = { + ...mockRangeSliderComponentState, + ...partialState?.componentState, + }; + jest + .spyOn(rangeSliderStateModule, 'getDefaultComponentState') + .mockImplementation(() => initialComponentState); + + const mockEmbeddable = (await rangeSliderControlFactory.create({ + ...mockRangeSliderEmbeddableInput, + ...partialState?.explicitInput, + })) as RangeSliderEmbeddable; + mockEmbeddable.getOutput = jest.fn().mockReturnValue(mockRangeSliderOutput); + return mockEmbeddable; +}; diff --git a/src/plugins/controls/public/control_group/actions/edit_control_action.tsx b/src/plugins/controls/public/control_group/actions/edit_control_action.tsx index 7e21482c97416..8f457925eb99c 100644 --- a/src/plugins/controls/public/control_group/actions/edit_control_action.tsx +++ b/src/plugins/controls/public/control_group/actions/edit_control_action.tsx @@ -9,18 +9,18 @@ import React from 'react'; import { EuiButtonIcon, EuiToolTip } from '@elastic/eui'; -import { toMountPoint } from '@kbn/kibana-react-plugin/public'; import { isErrorEmbeddable, ViewMode } from '@kbn/embeddable-plugin/public'; +import { toMountPoint } from '@kbn/react-kibana-mount'; import { Action, IncompatibleActionError } from '@kbn/ui-actions-plugin/public'; -import { pluginServices } from '../../services'; -import { EditControlFlyout } from './edit_control_flyout'; -import { DeleteControlAction } from './delete_control_action'; -import { ControlGroupStrings } from '../control_group_strings'; import { ACTION_EDIT_CONTROL, ControlGroupContainer } from '..'; +import { pluginServices } from '../../services'; import { ControlEmbeddable, DataControlInput } from '../../types'; +import { ControlGroupStrings } from '../control_group_strings'; import { ControlGroupContainerContext, setFlyoutRef } from '../embeddable/control_group_container'; import { isControlGroup } from '../embeddable/control_group_helpers'; +import { DeleteControlAction } from './delete_control_action'; +import { EditControlFlyout } from './edit_control_flyout'; export interface EditControlActionContext { embeddable: ControlEmbeddable; @@ -33,13 +33,14 @@ export class EditControlAction implements Action { private getEmbeddableFactory; private openFlyout; - private theme$; + private theme; + private i18n; constructor(private deleteControlAction: DeleteControlAction) { ({ embeddable: { getEmbeddableFactory: this.getEmbeddableFactory }, overlays: { openFlyout: this.openFlyout }, - theme: { theme$: this.theme$ }, + core: { theme: this.theme, i18n: this.i18n }, } = pluginServices.getServices()); } @@ -105,7 +106,7 @@ export class EditControlAction implements Action { /> , - { theme$: this.theme$ } + { theme: this.theme, i18n: this.i18n } ), { 'aria-label': ControlGroupStrings.manageControl.getFlyoutEditTitle(), diff --git a/src/plugins/controls/public/control_group/component/control_error_component.tsx b/src/plugins/controls/public/control_group/component/control_error_component.tsx index 409455f0e304e..6cdd1b5fc1fff 100644 --- a/src/plugins/controls/public/control_group/component/control_error_component.tsx +++ b/src/plugins/controls/public/control_group/component/control_error_component.tsx @@ -43,7 +43,6 @@ export const ControlError = ({ error }: ControlErrorProps) => { button={popoverButton} isOpen={isPopoverOpen} className="errorEmbeddableCompact__popover" - anchorClassName="errorEmbeddableCompact__popoverAnchor" closePopover={() => setPopoverOpen(false)} > - i18n.translate('controls.controlGroup.manageControl.dataSource.noControlTypeMessage', { - defaultMessage: 'No field selected yet', - }), getFieldTitle: () => i18n.translate('controls.controlGroup.manageControl.dataSource.fieldTitle', { defaultMessage: 'Field', @@ -47,6 +44,46 @@ export const ControlGroupStrings = { i18n.translate('controls.controlGroup.manageControl.dataSource.controlTypesTitle', { defaultMessage: 'Control type', }), + getControlTypeErrorMessage: ({ + fieldSelected, + controlType, + }: { + fieldSelected?: boolean; + controlType?: string; + }) => { + if (!fieldSelected) { + return i18n.translate( + 'controls.controlGroup.manageControl.dataSource.controlTypErrorMessage.noField', + { + defaultMessage: 'Select a field first.', + } + ); + } + + switch (controlType) { + /** + * Note that options list controls are currently compatible with every field type; so, there is no + * need to have a special error message for these. + */ + case RANGE_SLIDER_CONTROL: { + return i18n.translate( + 'controls.controlGroup.manageControl.dataSource.controlTypeErrorMessage.rangeSlider', + { + defaultMessage: 'Range sliders are only compatible with number fields.', + } + ); + } + default: { + /** This shouldn't ever happen - but, adding just in case as a fallback. */ + return i18n.translate( + 'controls.controlGroup.manageControl.dataSource.controlTypeErrorMessage.default', + { + defaultMessage: 'Select a compatible control type.', + } + ); + } + } + }, }, displaySettings: { getFormGroupTitle: () => @@ -231,6 +268,7 @@ export const ControlGroupStrings = { 'Selections in one control narrow down available options in the next. Controls are chained from left to right.', }), }, + /** TODO: These translations aren't used but they will be once https://github.com/elastic/kibana/issues/162985 is resolved */ querySync: { getQuerySettingsTitle: () => i18n.translate('controls.controlGroup.management.query.searchSettingsTitle', { diff --git a/src/plugins/controls/public/control_group/editor/control_editor.test.tsx b/src/plugins/controls/public/control_group/editor/control_editor.test.tsx index 33c627df20460..d6e079261e183 100644 --- a/src/plugins/controls/public/control_group/editor/control_editor.test.tsx +++ b/src/plugins/controls/public/control_group/editor/control_editor.test.tsx @@ -6,29 +6,33 @@ * Side Public License, v 1. */ -import React from 'react'; import { ReactWrapper } from 'enzyme'; +import React from 'react'; import { act } from 'react-dom/test-utils'; -import { findTestSubject, mountWithIntl } from '@kbn/test-jest-helpers'; import { stubDataView } from '@kbn/data-views-plugin/common/data_view.stub'; +import { findTestSubject, mountWithIntl } from '@kbn/test-jest-helpers'; -import { ControlGroupInput } from '../types'; -import { pluginServices } from '../../services'; -import { ControlEditor, EditControlProps } from './control_editor'; import { OptionsListEmbeddableFactory } from '../..'; +import { + OptionsListEmbeddableInput, + OPTIONS_LIST_CONTROL, + RANGE_SLIDER_CONTROL, +} from '../../../common'; import { DEFAULT_CONTROL_GROW, DEFAULT_CONTROL_WIDTH, } from '../../../common/control_group/control_group_constants'; -import { mockControlGroupContainer, mockOptionsListEmbeddable } from '../../../common/mocks'; +import { + mockControlGroupContainer, + mockOptionsListEmbeddable, + mockRangeSliderEmbeddable, +} from '../../../common/mocks'; import { RangeSliderEmbeddableFactory } from '../../range_slider'; +import { pluginServices } from '../../services'; import { ControlGroupContainerContext } from '../embeddable/control_group_container'; -import { - OptionsListEmbeddableInput, - OPTIONS_LIST_CONTROL, - RANGE_SLIDER_CONTROL, -} from '../../../common'; +import { ControlGroupInput } from '../types'; +import { ControlEditor, EditControlProps } from './control_editor'; describe('Data control editor', () => { interface MountOptions { @@ -96,10 +100,13 @@ describe('Data control editor', () => { await selectField('machine.os.raw'); }); - test('creates an options list control', async () => { - expect(findTestSubject(controlEditor, 'control-editor-type').text()).toEqual( - 'Options list' - ); + test('can only create an options list control', async () => { + expect( + findTestSubject(controlEditor, 'create__optionsListControl').instance() + ).toBeEnabled(); + expect( + findTestSubject(controlEditor, 'create__rangeSliderControl').instance() + ).not.toBeEnabled(); }); test('has custom settings', async () => { @@ -113,6 +120,8 @@ describe('Data control editor', () => { 'optionsListControl__searchOptionsRadioGroup' ); expect(searchOptions.exists()).toBe(true); + const options = searchOptions.find('div.euiRadioGroup__item'); + expect(options.length).toBe(3); }); }); @@ -122,18 +131,23 @@ describe('Data control editor', () => { await selectField('clientip'); }); - test('creates an options list control', async () => { - expect(findTestSubject(controlEditor, 'control-editor-type').text()).toEqual( - 'Options list' - ); + test('can only create an options list control', async () => { + expect( + findTestSubject(controlEditor, 'create__optionsListControl').instance() + ).toBeEnabled(); + expect( + findTestSubject(controlEditor, 'create__rangeSliderControl').instance() + ).not.toBeEnabled(); }); - test('does not have custom search options', async () => { + test('has custom search options', async () => { const searchOptions = findTestSubject( controlEditor, 'optionsListControl__searchOptionsRadioGroup' ); - expect(searchOptions.exists()).toBe(false); + expect(searchOptions.exists()).toBe(true); + const options = searchOptions.find('div.euiRadioGroup__item'); + expect(options.length).toBe(2); }); }); @@ -143,13 +157,38 @@ describe('Data control editor', () => { await selectField('bytes'); }); - test('creates a range slider control', async () => { - expect(findTestSubject(controlEditor, 'control-editor-type').text()).toEqual( - 'Range slider' + test('can create an options list or range slider control', async () => { + expect( + findTestSubject(controlEditor, 'create__optionsListControl').instance() + ).toBeEnabled(); + expect( + findTestSubject(controlEditor, 'create__rangeSliderControl').instance() + ).toBeEnabled(); + }); + + test('defaults to options list creation', async () => { + expect( + findTestSubject(controlEditor, 'create__optionsListControl').prop('aria-pressed') + ).toBe(true); + }); + + test('when creating options list, has custom settings', async () => { + findTestSubject(controlEditor, 'create__optionsListControl').simulate('click'); + const customSettings = findTestSubject(controlEditor, 'control-editor-custom-settings'); + expect(customSettings.exists()).toBe(true); + }); + + test('when creating options list, does not have custom search options', async () => { + findTestSubject(controlEditor, 'create__optionsListControl').simulate('click'); + const searchOptions = findTestSubject( + controlEditor, + 'optionsListControl__searchOptionsRadioGroup' ); + expect(searchOptions.exists()).toBe(false); }); - test('does not have any custom settings', async () => { + test('when creating range slider, does not have custom settings', async () => { + findTestSubject(controlEditor, 'create__rangeSliderControl').simulate('click'); const searchOptions = findTestSubject(controlEditor, 'control-editor-custom-settings'); expect(searchOptions.exists()).toBe(false); }); @@ -174,15 +213,28 @@ describe('Data control editor', () => { }); describe('editing existing options list control', () => { - const openOptionsListEditor = async (explicitInput?: Partial) => { - const control = await mockOptionsListEmbeddable({ - explicitInput: { - title: 'machine.os.raw', - dataViewId: stubDataView.id, - fieldName: 'machine.os.raw', - ...explicitInput, - }, - }); + const openEditor = async ( + type: string, + explicitInput?: Partial + ) => { + const control = + type === 'optionsList' + ? await mockOptionsListEmbeddable({ + explicitInput: { + title: 'machine.os.raw', + dataViewId: stubDataView.id, + fieldName: 'machine.os.raw', + ...explicitInput, + }, + }) + : await mockRangeSliderEmbeddable({ + explicitInput: { + title: 'bytes', + dataViewId: stubDataView.id, + fieldName: 'bytes', + ...explicitInput, + }, + }); await mountComponent({ componentOptions: { isCreate: false, embeddable: control }, }); @@ -190,23 +242,45 @@ describe('Data control editor', () => { describe('control title', () => { test('auto-fills default', async () => { - await openOptionsListEditor(); + await openEditor('optionsList'); const titleInput = findTestSubject(controlEditor, 'control-editor-title-input'); expect(titleInput.prop('value')).toBe('machine.os.raw'); expect(titleInput.prop('placeholder')).toBe('machine.os.raw'); }); test('auto-fills custom title', async () => { - await openOptionsListEditor({ title: 'Custom Title' }); + await openEditor('optionsList', { title: 'Custom Title' }); const titleInput = findTestSubject(controlEditor, 'control-editor-title-input'); expect(titleInput.prop('value')).toBe('Custom Title'); expect(titleInput.prop('placeholder')).toBe('machine.os.raw'); }); }); + describe('control type', () => { + test('selects the default control type', async () => { + await openEditor('optionsList', { fieldName: 'bytes' }); + expect( + findTestSubject(controlEditor, 'create__optionsListControl').prop('aria-pressed') + ).toBe(true); + expect( + findTestSubject(controlEditor, 'create__rangeSliderControl').prop('aria-pressed') + ).toBe(false); + }); + + test('selects the given, non-default control type', async () => { + await openEditor('rangeSlider', { fieldName: 'bytes' }); + expect( + findTestSubject(controlEditor, 'create__optionsListControl').prop('aria-pressed') + ).toBe(false); + expect( + findTestSubject(controlEditor, 'create__rangeSliderControl').prop('aria-pressed') + ).toBe(true); + }); + }); + describe('selection options', () => { test('selects default', async () => { - await openOptionsListEditor(); + await openEditor('optionsList'); const radioGroup = findTestSubject( controlEditor, 'optionsListControl__selectionOptionsRadioGroup' @@ -216,7 +290,7 @@ describe('Data control editor', () => { }); test('selects given', async () => { - await openOptionsListEditor({ singleSelect: true }); + await openEditor('optionsList', { singleSelect: true }); const radioGroup = findTestSubject( controlEditor, 'optionsListControl__selectionOptionsRadioGroup' @@ -228,7 +302,7 @@ describe('Data control editor', () => { describe('search techniques', () => { test('selects default', async () => { - await openOptionsListEditor(); + await openEditor('optionsList'); const radioGroup = findTestSubject( controlEditor, 'optionsListControl__searchOptionsRadioGroup' @@ -238,7 +312,7 @@ describe('Data control editor', () => { }); test('selects given', async () => { - await openOptionsListEditor({ searchTechnique: 'wildcard' }); + await openEditor('optionsList', { searchTechnique: 'wildcard' }); const radioGroup = findTestSubject( controlEditor, 'optionsListControl__searchOptionsRadioGroup' diff --git a/src/plugins/controls/public/control_group/editor/control_editor.tsx b/src/plugins/controls/public/control_group/editor/control_editor.tsx index 0860f19e506e3..aa9f505075426 100644 --- a/src/plugins/controls/public/control_group/editor/control_editor.tsx +++ b/src/plugins/controls/public/control_group/editor/control_editor.tsx @@ -14,29 +14,31 @@ * Side Public License, v 1. */ +import deepEqual from 'fast-deep-equal'; import React, { useEffect, useMemo, useRef, useState } from 'react'; -import useMount from 'react-use/lib/useMount'; import useAsync from 'react-use/lib/useAsync'; -import deepEqual from 'fast-deep-equal'; +import useMount from 'react-use/lib/useMount'; import { - EuiFlyoutHeader, + EuiButton, + EuiButtonEmpty, EuiButtonGroup, - EuiFlyoutBody, + EuiDescribedFormGroup, + EuiFieldText, EuiFlexGroup, EuiFlexItem, - EuiTitle, - EuiFieldText, + EuiFlyoutBody, EuiFlyoutFooter, - EuiButton, - EuiFormRow, + EuiFlyoutHeader, EuiForm, - EuiButtonEmpty, - EuiSpacer, + EuiFormRow, EuiIcon, + EuiKeyPadMenu, + EuiKeyPadMenuItem, + EuiSpacer, EuiSwitch, - EuiTextColor, - EuiDescribedFormGroup, + EuiTitle, + EuiToolTip, } from '@elastic/eui'; import { DataViewField } from '@kbn/data-views-plugin/common'; import { @@ -45,7 +47,8 @@ import { withSuspense, } from '@kbn/presentation-util-plugin/public'; -import { ControlGroupStrings } from '../control_group_strings'; +import { TIME_SLIDER_CONTROL } from '../../../common'; +import { pluginServices } from '../../services'; import { ControlEmbeddable, ControlInput, @@ -54,10 +57,10 @@ import { DataControlInput, IEditableControlFactory, } from '../../types'; -import { CONTROL_WIDTH_OPTIONS } from './editor_constants'; -import { pluginServices } from '../../services'; -import { getDataControlFieldRegistry } from './data_control_editor_tools'; +import { ControlGroupStrings } from '../control_group_strings'; import { useControlGroupContainer } from '../embeddable/control_group_container'; +import { getDataControlFieldRegistry } from './data_control_editor_tools'; +import { CONTROL_WIDTH_OPTIONS } from './editor_constants'; export interface EditControlProps { embeddable?: ControlEmbeddable; @@ -87,7 +90,7 @@ export const ControlEditor = ({ }: EditControlProps) => { const { dataViews: { getIdsWithTitle, getDefaultId, get }, - controls: { getControlFactory }, + controls: { getControlFactory, getControlTypes }, } = pluginServices.getServices(); const controlGroup = useControlGroupContainer(); @@ -102,6 +105,9 @@ export const ControlEditor = ({ const [selectedField, setSelectedField] = useState( embeddable ? embeddable.getInput().fieldName : undefined ); + const [selectedControlType, setSelectedControlType] = useState( + embeddable ? embeddable.type : undefined + ); const [customSettings, setCustomSettings] = useState>(); const currentInput: Partial = useMemo( @@ -157,15 +163,93 @@ export const ControlEditor = ({ }, [selectedDataViewId]); useEffect( - () => setControlEditorValid(Boolean(selectedField) && Boolean(selectedDataView)), - [selectedField, setControlEditorValid, selectedDataView] + () => + setControlEditorValid( + Boolean(selectedField) && Boolean(selectedDataView) && Boolean(selectedControlType) + ), + [selectedField, setControlEditorValid, selectedDataView, selectedControlType] ); - const controlType = - selectedField && fieldRegistry && fieldRegistry[selectedField].compatibleControlTypes[0]; - const factory = controlType && getControlFactory(controlType); - const CustomSettings = - factory && (factory as IEditableControlFactory).controlEditorOptionsComponent; + const CompatibleControlTypesComponent = useMemo(() => { + const allDataControlTypes = getControlTypes().filter((type) => type !== TIME_SLIDER_CONTROL); + return ( + + {allDataControlTypes.map((controlType) => { + const factory = getControlFactory(controlType); + + const disabled = + fieldRegistry && selectedField + ? !fieldRegistry[selectedField].compatibleControlTypes.includes(controlType) + : true; + const keyPadMenuItem = ( + setSelectedControlType(controlType)} + label={factory.getDisplayName()} + > + + + ); + + return disabled ? ( + + {keyPadMenuItem} + + ) : ( + keyPadMenuItem + ); + })} + + ); + }, [selectedField, fieldRegistry, getControlFactory, getControlTypes, selectedControlType]); + + const CustomSettingsComponent = useMemo(() => { + if (!selectedControlType || !selectedField || !fieldRegistry) return; + + const controlFactory = getControlFactory(selectedControlType); + const CustomSettings = (controlFactory as IEditableControlFactory) + .controlEditorOptionsComponent; + + if (!CustomSettings) return; + + return ( + + {ControlGroupStrings.manageControl.controlTypeSettings.getFormGroupTitle( + controlFactory.getDisplayName() + )} + + } + description={ControlGroupStrings.manageControl.controlTypeSettings.getFormGroupDescription( + controlFactory.getDisplayName() + )} + data-test-subj="control-editor-custom-settings" + > + setCustomSettings(settings)} + initialInput={embeddable?.getInput()} + fieldType={fieldRegistry[selectedField].field.type} + /> + + ); + }, [selectedControlType, selectedField, getControlFactory, fieldRegistry, embeddable]); + return ( <> @@ -216,6 +300,9 @@ export const ControlEditor = ({ const newDefaultTitle = field.displayName ?? field.name; setDefaultTitle(newDefaultTitle); setSelectedField(field.name); + setSelectedControlType( + fieldRegistry?.[field.displayName].compatibleControlTypes[0] + ); if (!currentTitle || currentTitle === defaultTitle) { setCurrentTitle(newDefaultTitle); } @@ -224,20 +311,7 @@ export const ControlEditor = ({ /> - {factory ? ( - - - - - - {factory.getDisplayName()} - - - ) : ( - - {ControlGroupStrings.manageControl.dataSource.noControlTypeMessage()} - - )} + {CompatibleControlTypesComponent} )} - {!editorConfig?.hideAdditionalSettings && - CustomSettings && - (factory as IEditableControlFactory).controlEditorOptionsComponent && ( - - {ControlGroupStrings.manageControl.controlTypeSettings.getFormGroupTitle( - factory.getDisplayName() - )} - - } - description={ControlGroupStrings.manageControl.controlTypeSettings.getFormGroupDescription( - factory.getDisplayName() - )} - data-test-subj="control-editor-custom-settings" - > - setCustomSettings(settings)} - initialInput={embeddable?.getInput()} - fieldType={fieldRegistry?.[selectedField].field.type} - /> - - )} + {!editorConfig?.hideAdditionalSettings ? CustomSettingsComponent : null} {removeControl && ( <> @@ -350,7 +401,10 @@ export const ControlEditor = ({ color="primary" disabled={!controlEditorValid} onClick={() => - onSave({ input: currentInput, grow: currentGrow, width: currentWidth }, controlType) + onSave( + { input: currentInput, grow: currentGrow, width: currentWidth }, + selectedControlType + ) } > {ControlGroupStrings.manageControl.getSaveChangesTitle()} diff --git a/src/plugins/controls/public/control_group/editor/open_add_data_control_flyout.tsx b/src/plugins/controls/public/control_group/editor/open_add_data_control_flyout.tsx index aa2827f47703a..1ea7da60477b7 100644 --- a/src/plugins/controls/public/control_group/editor/open_add_data_control_flyout.tsx +++ b/src/plugins/controls/public/control_group/editor/open_add_data_control_flyout.tsx @@ -9,9 +9,18 @@ import React from 'react'; import { batch } from 'react-redux'; -import { toMountPoint } from '@kbn/kibana-react-plugin/public'; import { isErrorEmbeddable } from '@kbn/embeddable-plugin/public'; +import { toMountPoint } from '@kbn/react-kibana-mount'; +import { OPTIONS_LIST_CONTROL, RANGE_SLIDER_CONTROL } from '../..'; +import { + DEFAULT_CONTROL_GROW, + DEFAULT_CONTROL_WIDTH, +} from '../../../common/control_group/control_group_constants'; +import { ControlInputTransform } from '../../../common/types'; +import { pluginServices } from '../../services'; +import { DataControlEditorChanges, IEditableControlFactory } from '../../types'; +import { ControlGroupStrings } from '../control_group_strings'; import { ControlGroupContainer, ControlGroupContainerContext, @@ -22,16 +31,7 @@ import type { AddOptionsListControlProps, AddRangeSliderControlProps, } from '../external_api/control_group_input_builder'; -import { - DEFAULT_CONTROL_GROW, - DEFAULT_CONTROL_WIDTH, -} from '../../../common/control_group/control_group_constants'; -import { pluginServices } from '../../services'; import { ControlEditor } from './control_editor'; -import { DataControlEditorChanges, IEditableControlFactory } from '../../types'; -import { ControlInputTransform } from '../../../common/types'; -import { ControlGroupStrings } from '../control_group_strings'; -import { OPTIONS_LIST_CONTROL, RANGE_SLIDER_CONTROL } from '../..'; export function openAddDataControlFlyout( this: ControlGroupContainer, @@ -42,9 +42,9 @@ export function openAddDataControlFlyout( ) { const { controlInputTransform, onSave } = options || {}; const { + core: { theme, i18n }, overlays: { openFlyout, openConfirm }, controls: { getControlFactory }, - theme: { theme$ }, } = pluginServices.getServices(); const onCancel = (changes?: DataControlEditorChanges) => { @@ -125,7 +125,7 @@ export function openAddDataControlFlyout( onCancel={onCancel} /> , - { theme$ } + { theme, i18n } ), { 'aria-label': ControlGroupStrings.manageControl.getFlyoutCreateTitle(), diff --git a/src/plugins/controls/public/control_group/editor/open_edit_control_group_flyout.tsx b/src/plugins/controls/public/control_group/editor/open_edit_control_group_flyout.tsx index ccc0064a48cd3..847db2f1bb458 100644 --- a/src/plugins/controls/public/control_group/editor/open_edit_control_group_flyout.tsx +++ b/src/plugins/controls/public/control_group/editor/open_edit_control_group_flyout.tsx @@ -6,23 +6,23 @@ * Side Public License, v 1. */ -import React from 'react'; import { OverlayRef } from '@kbn/core-mount-utils-browser'; -import { toMountPoint } from '@kbn/kibana-react-plugin/public'; +import { toMountPoint } from '@kbn/react-kibana-mount'; +import React from 'react'; import { pluginServices } from '../../services'; -import { ControlGroupEditor } from './control_group_editor'; import { ControlGroupStrings } from '../control_group_strings'; import { ControlGroupContainer, ControlGroupContainerContext, setFlyoutRef, } from '../embeddable/control_group_container'; +import { ControlGroupEditor } from './control_group_editor'; export function openEditControlGroupFlyout(this: ControlGroupContainer) { const { + core: { theme, i18n }, overlays: { openFlyout, openConfirm }, - theme: { theme$ }, } = pluginServices.getServices(); const onDeleteAll = (ref: OverlayRef) => { @@ -49,7 +49,7 @@ export function openEditControlGroupFlyout(this: ControlGroupContainer) { onClose={() => flyoutInstance.close()} /> , - { theme$ } + { theme, i18n } ), { 'aria-label': ControlGroupStrings.manageControl.getFlyoutCreateTitle(), diff --git a/src/plugins/controls/public/control_group/embeddable/control_group_container.tsx b/src/plugins/controls/public/control_group/embeddable/control_group_container.tsx index 2bf18a70a97e7..f06067fd95ae2 100644 --- a/src/plugins/controls/public/control_group/embeddable/control_group_container.tsx +++ b/src/plugins/controls/public/control_group/embeddable/control_group_container.tsx @@ -5,19 +5,34 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ +import { compareFilters, COMPARE_ALL_OPTIONS, Filter, uniqFilters } from '@kbn/es-query'; import { isEqual } from 'lodash'; +import React, { createContext, useContext } from 'react'; import ReactDOM from 'react-dom'; import { Provider, TypedUseSelectorHook, useSelector } from 'react-redux'; -import React, { createContext, useContext } from 'react'; import { BehaviorSubject, merge, Subject, Subscription } from 'rxjs'; -import { skip, debounceTime, distinctUntilChanged } from 'rxjs/operators'; -import { compareFilters, COMPARE_ALL_OPTIONS, Filter, uniqFilters } from '@kbn/es-query'; +import { debounceTime, distinctUntilChanged, skip } from 'rxjs/operators'; import { OverlayRef } from '@kbn/core/public'; -import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import { Container, EmbeddableFactory } from '@kbn/embeddable-plugin/public'; -import { ReduxToolsPackage, ReduxEmbeddableTools } from '@kbn/presentation-util-plugin/public'; +import { ReduxEmbeddableTools, ReduxToolsPackage } from '@kbn/presentation-util-plugin/public'; +import { KibanaThemeProvider } from '@kbn/react-kibana-context-theme'; +import { pluginServices } from '../../services'; +import { ControlEmbeddable, ControlInput, ControlOutput } from '../../types'; +import { ControlGroup } from '../component/control_group_component'; +import { openAddDataControlFlyout } from '../editor/open_add_data_control_flyout'; +import { openEditControlGroupFlyout } from '../editor/open_edit_control_group_flyout'; +import { + getDataControlPanelState, + getOptionsListPanelState, + getRangeSliderPanelState, + getTimeSliderPanelState, + type AddDataControlProps, + type AddOptionsListControlProps, + type AddRangeSliderControlProps, +} from '../external_api/control_group_input_builder'; +import { controlGroupReducers } from '../state/control_group_reducers'; import { ControlGroupInput, ControlGroupOutput, @@ -33,22 +48,7 @@ import { ControlGroupChainingSystems, controlOrdersAreEqual, } from './control_group_chaining_system'; -import { - type AddDataControlProps, - type AddOptionsListControlProps, - type AddRangeSliderControlProps, - getDataControlPanelState, - getOptionsListPanelState, - getRangeSliderPanelState, - getTimeSliderPanelState, -} from '../external_api/control_group_input_builder'; -import { pluginServices } from '../../services'; import { getNextPanelOrder } from './control_group_helpers'; -import { ControlGroup } from '../component/control_group_component'; -import { controlGroupReducers } from '../state/control_group_reducers'; -import { ControlEmbeddable, ControlInput, ControlOutput } from '../../types'; -import { openAddDataControlFlyout } from '../editor/open_add_data_control_flyout'; -import { openEditControlGroupFlyout } from '../editor/open_edit_control_group_flyout'; let flyoutRef: OverlayRef | undefined; export const setFlyoutRef = (newRef: OverlayRef | undefined) => { @@ -382,7 +382,7 @@ export class ControlGroupContainer extends Container< } this.domNode = dom; ReactDOM.render( - + diff --git a/src/plugins/controls/public/options_list/components/options_list_editor_options.tsx b/src/plugins/controls/public/options_list/components/options_list_editor_options.tsx index 403a2b941a586..74e8d61ed218c 100644 --- a/src/plugins/controls/public/options_list/components/options_list_editor_options.tsx +++ b/src/plugins/controls/public/options_list/components/options_list_editor_options.tsx @@ -6,33 +6,33 @@ * Side Public License, v 1. */ +import React, { useEffect, useMemo, useState } from 'react'; import useAsync from 'react-use/lib/useAsync'; -import React, { useEffect, useState } from 'react'; import { + Direction, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiIconTip, - EuiSwitch, - Direction, - EuiRadioGroup, EuiLoadingSpinner, + EuiRadioGroup, + EuiSwitch, } from '@elastic/eui'; import { css } from '@emotion/react'; -import { pluginServices } from '../../services'; +import { ControlEditorProps, OptionsListEmbeddableInput } from '../..'; +import { + getCompatibleSearchTechniques, + OptionsListSearchTechnique, +} from '../../../common/options_list/suggestions_searching'; import { - OptionsListSortBy, getCompatibleSortingTypes, + OptionsListSortBy, OPTIONS_LIST_DEFAULT_SORT, } from '../../../common/options_list/suggestions_sorting'; +import { pluginServices } from '../../services'; import { OptionsListStrings } from './options_list_strings'; -import { ControlEditorProps, OptionsListEmbeddableInput } from '../..'; -import { - OptionsListSearchTechnique, - OPTIONS_LIST_DEFAULT_SEARCH_TECHNIQUE, -} from '../../../common/options_list/types'; const TooltipText = ({ label, tooltip }: { label: string; tooltip: string }) => ( @@ -61,7 +61,7 @@ const selectionOptions = [ }, ]; -const searchOptions = [ +const allSearchOptions = [ { id: 'prefix', label: ( @@ -82,6 +82,16 @@ const searchOptions = [ ), 'data-test-subj': 'optionsListControl__wildcardSearchOptionAdditionalSetting', }, + { + id: 'exact', + label: ( + + ), + 'data-test-subj': 'optionsListControl__exactSearchOptionAdditionalSetting', + }, ]; interface OptionsListEditorState { @@ -117,6 +127,17 @@ export const OptionsListEditorOptions = ({ return optionsListService.getAllowExpensiveQueries(); }, []); + const compatibleSearchTechniques = useMemo( + () => getCompatibleSearchTechniques(fieldType), + [fieldType] + ); + + const searchOptions = useMemo(() => { + return allSearchOptions.filter((searchOption) => { + return compatibleSearchTechniques.includes(searchOption.id as OptionsListSearchTechnique); + }); + }, [compatibleSearchTechniques]); + useEffect(() => { // when field type changes, ensure that the selected sort type is still valid if (!getCompatibleSortingTypes(fieldType).includes(state.sortBy)) { @@ -129,6 +150,21 @@ export const OptionsListEditorOptions = ({ } }, [fieldType, onChange, state.sortBy]); + useEffect(() => { + // when field type changes, ensure that the selected search technique is still valid; + // if the selected search technique **isn't** valid, reset to the default + const searchTechnique = + initialInput?.searchTechnique && + compatibleSearchTechniques.includes(initialInput.searchTechnique) + ? initialInput.searchTechnique + : compatibleSearchTechniques[0]; + onChange({ searchTechnique }); + setState((s) => ({ + ...s, + searchTechnique, + })); + }, [compatibleSearchTechniques, onChange, initialInput]); + return ( <> ) : ( allowExpensiveQueries && - !['ip', 'date'].includes(fieldType) && ( + compatibleSearchTechniques.length > 1 && ( { const searchTechnique = id as OptionsListSearchTechnique; onChange({ searchTechnique }); diff --git a/src/plugins/controls/public/options_list/components/options_list_popover.test.tsx b/src/plugins/controls/public/options_list/components/options_list_popover.test.tsx index 6263cdff192fc..59f1c9f2b058c 100644 --- a/src/plugins/controls/public/options_list/components/options_list_popover.test.tsx +++ b/src/plugins/controls/public/options_list/components/options_list_popover.test.tsx @@ -67,241 +67,269 @@ describe('Options list popover', () => { expect(noOptionsDiv.exists()).toBeTruthy(); }); - test('display error message when the show only selected toggle is true but there are no selections', async () => { - const popover = await mountComponent(); - clickShowOnlySelections(popover); - const availableOptionsDiv = findTestSubject(popover, 'optionsList-control-available-options'); - const noSelectionsDiv = findTestSubject( - availableOptionsDiv, - 'optionsList-control-selectionsEmptyMessage' - ); - expect(noSelectionsDiv.exists()).toBeTruthy(); - }); - - test('show only selected options', async () => { - const selections = ['woof', 'bark']; - const popover = await mountComponent({ - explicitInput: { selectedOptions: selections }, + describe('show only selected', () => { + test('display error message when the show only selected toggle is true but there are no selections', async () => { + const popover = await mountComponent(); + clickShowOnlySelections(popover); + const availableOptionsDiv = findTestSubject(popover, 'optionsList-control-available-options'); + const noSelectionsDiv = findTestSubject( + availableOptionsDiv, + 'optionsList-control-selectionsEmptyMessage' + ); + expect(noSelectionsDiv.exists()).toBeTruthy(); }); - clickShowOnlySelections(popover); - const availableOptions = popover.find( - '[data-test-subj="optionsList-control-available-options"] ul' - ); - availableOptions.children().forEach((child, i) => { - expect(child.text()).toBe(`${selections[i]}. Checked option.`); - }); - }); - test('disable search and sort when show only selected toggle is true', async () => { - const selections = ['woof', 'bark']; - const popover = await mountComponent({ - explicitInput: { selectedOptions: selections }, + test('show only selected options', async () => { + const selections = ['woof', 'bark']; + const popover = await mountComponent({ + explicitInput: { selectedOptions: selections }, + }); + clickShowOnlySelections(popover); + const availableOptions = popover.find( + '[data-test-subj="optionsList-control-available-options"] ul' + ); + availableOptions.children().forEach((child, i) => { + expect(child.text()).toBe(`${selections[i]}. Checked option.`); + }); }); - let searchBox = findTestSubject(popover, 'optionsList-control-search-input'); - let sortButton = findTestSubject(popover, 'optionsListControl__sortingOptionsButton'); - expect(searchBox.prop('disabled')).toBeFalsy(); - expect(sortButton.prop('disabled')).toBeFalsy(); - - clickShowOnlySelections(popover); - searchBox = findTestSubject(popover, 'optionsList-control-search-input'); - sortButton = findTestSubject(popover, 'optionsListControl__sortingOptionsButton'); - expect(searchBox.prop('disabled')).toBe(true); - expect(sortButton.prop('disabled')).toBe(true); - }); - test('test single invalid selection', async () => { - const popover = await mountComponent({ - explicitInput: { - selectedOptions: ['bark', 'woof'], - }, - componentState: { - availableOptions: [{ value: 'bark', docCount: 75 }], - validSelections: ['bark'], - invalidSelections: ['woof'], - }, + test('disable search and sort when show only selected toggle is true', async () => { + const selections = ['woof', 'bark']; + const popover = await mountComponent({ + explicitInput: { selectedOptions: selections }, + componentState: { field: { type: 'string' } as any as FieldSpec }, + }); + let searchBox = findTestSubject(popover, 'optionsList-control-search-input'); + let sortButton = findTestSubject(popover, 'optionsListControl__sortingOptionsButton'); + expect(searchBox.prop('disabled')).toBeFalsy(); + expect(sortButton.prop('disabled')).toBeFalsy(); + + clickShowOnlySelections(popover); + searchBox = findTestSubject(popover, 'optionsList-control-search-input'); + sortButton = findTestSubject(popover, 'optionsListControl__sortingOptionsButton'); + expect(searchBox.prop('disabled')).toBe(true); + expect(sortButton.prop('disabled')).toBe(true); }); - const validSelection = findTestSubject(popover, 'optionsList-control-selection-bark'); - expect(validSelection.find('.euiSelectableListItem__text').text()).toEqual( - 'bark. Checked option.' - ); - expect( - validSelection.find('div[data-test-subj="optionsList-document-count-badge"]').text().trim() - ).toEqual('75'); - const title = findTestSubject(popover, 'optionList__ignoredSelectionLabel').text(); - expect(title).toEqual('Ignored selection'); - const invalidSelection = findTestSubject(popover, 'optionsList-control-ignored-selection-woof'); - expect(invalidSelection.find('.euiSelectableListItem__text').text()).toEqual( - 'woof. Checked option.' - ); - expect(invalidSelection.hasClass('optionsList__selectionInvalid')).toBe(true); }); - test('test title when multiple invalid selections', async () => { - const popover = await mountComponent({ - explicitInput: { selectedOptions: ['bark', 'woof', 'meow'] }, - componentState: { - availableOptions: [{ value: 'bark', docCount: 75 }], - validSelections: ['bark'], - invalidSelections: ['woof', 'meow'], - }, + describe('invalid selections', () => { + test('test single invalid selection', async () => { + const popover = await mountComponent({ + explicitInput: { + selectedOptions: ['bark', 'woof'], + }, + componentState: { + availableOptions: [{ value: 'bark', docCount: 75 }], + validSelections: ['bark'], + invalidSelections: ['woof'], + }, + }); + const validSelection = findTestSubject(popover, 'optionsList-control-selection-bark'); + expect(validSelection.find('.euiSelectableListItem__text').text()).toEqual( + 'bark. Checked option.' + ); + expect( + validSelection.find('div[data-test-subj="optionsList-document-count-badge"]').text().trim() + ).toEqual('75'); + const title = findTestSubject(popover, 'optionList__ignoredSelectionLabel').text(); + expect(title).toEqual('Ignored selection'); + const invalidSelection = findTestSubject( + popover, + 'optionsList-control-ignored-selection-woof' + ); + expect(invalidSelection.find('.euiSelectableListItem__text').text()).toEqual( + 'woof. Checked option.' + ); + expect(invalidSelection.hasClass('optionsList__selectionInvalid')).toBe(true); }); - const title = findTestSubject(popover, 'optionList__ignoredSelectionLabel').text(); - expect(title).toEqual('Ignored selections'); - }); - test('should default to exclude = false', async () => { - const popover = await mountComponent(); - const includeButton = findTestSubject(popover, 'optionsList__includeResults'); - const excludeButton = findTestSubject(popover, 'optionsList__excludeResults'); - expect(includeButton.prop('checked')).toBe(true); - expect(excludeButton.prop('checked')).toBeFalsy(); - }); - - test('if exclude = true, select appropriate button in button group', async () => { - const popover = await mountComponent({ - explicitInput: { exclude: true }, + test('test title when multiple invalid selections', async () => { + const popover = await mountComponent({ + explicitInput: { selectedOptions: ['bark', 'woof', 'meow'] }, + componentState: { + availableOptions: [{ value: 'bark', docCount: 75 }], + validSelections: ['bark'], + invalidSelections: ['woof', 'meow'], + }, + }); + const title = findTestSubject(popover, 'optionList__ignoredSelectionLabel').text(); + expect(title).toEqual('Ignored selections'); }); - const includeButton = findTestSubject(popover, 'optionsList__includeResults'); - const excludeButton = findTestSubject(popover, 'optionsList__excludeResults'); - expect(includeButton.prop('checked')).toBeFalsy(); - expect(excludeButton.prop('checked')).toBe(true); }); - test('clicking another option unselects "Exists"', async () => { - const popover = await mountComponent({ - explicitInput: { existsSelected: true }, + describe('include/exclude toggle', () => { + test('should default to exclude = false', async () => { + const popover = await mountComponent(); + const includeButton = findTestSubject(popover, 'optionsList__includeResults'); + const excludeButton = findTestSubject(popover, 'optionsList__excludeResults'); + expect(includeButton.prop('aria-pressed')).toBe(true); + expect(excludeButton.prop('aria-pressed')).toBe(false); }); - const woofOption = findTestSubject(popover, 'optionsList-control-selection-woof'); - woofOption.simulate('click'); - const availableOptionsDiv = findTestSubject(popover, 'optionsList-control-available-options'); - availableOptionsDiv.children().forEach((child, i) => { - if (child.text() === 'woof') expect(child.prop('checked')).toBe('on'); - else expect(child.prop('checked')).toBeFalsy(); + test('if exclude = true, select appropriate button in button group', async () => { + const popover = await mountComponent({ + explicitInput: { exclude: true }, + }); + const includeButton = findTestSubject(popover, 'optionsList__includeResults'); + const excludeButton = findTestSubject(popover, 'optionsList__excludeResults'); + expect(includeButton.prop('aria-pressed')).toBe(false); + expect(excludeButton.prop('aria-pressed')).toBe(true); }); }); - test('clicking "Exists" unselects all other selections', async () => { - const selections = ['woof', 'bark']; - const popover = await mountComponent({ - explicitInput: { existsSelected: false, selectedOptions: selections }, - }); - const existsOption = findTestSubject(popover, 'optionsList-control-selection-exists'); - let availableOptionsDiv = findTestSubject(popover, 'optionsList-control-available-options'); - availableOptionsDiv.children().forEach((child, i) => { - if (selections.includes(child.text())) expect(child.prop('checked')).toBe('on'); - else expect(child.prop('checked')).toBeFalsy(); + describe('"Exists" option', () => { + test('clicking another option unselects "Exists"', async () => { + const popover = await mountComponent({ + explicitInput: { existsSelected: true }, + }); + const woofOption = findTestSubject(popover, 'optionsList-control-selection-woof'); + woofOption.simulate('click'); + + const availableOptionsDiv = findTestSubject(popover, 'optionsList-control-available-options'); + availableOptionsDiv.children().forEach((child, i) => { + if (child.text() === 'woof') expect(child.prop('aria-pressed')).toBe(true); + else expect(child.prop('aria-pressed')).toBeFalsy(); + }); }); - existsOption.simulate('click'); - availableOptionsDiv = findTestSubject(popover, 'optionsList-control-available-options'); - availableOptionsDiv.children().forEach((child, i) => { - if (child.text() === 'Exists (*)') expect(child.prop('checked')).toBe('on'); - else expect(child.prop('checked')).toBeFalsy(); + test('clicking "Exists" unselects all other selections', async () => { + const selections = ['woof', 'bark']; + const popover = await mountComponent({ + explicitInput: { existsSelected: false, selectedOptions: selections }, + }); + const existsOption = findTestSubject(popover, 'optionsList-control-selection-exists'); + let availableOptionsDiv = findTestSubject(popover, 'optionsList-control-available-options'); + availableOptionsDiv.children().forEach((child, i) => { + if (selections.includes(child.text())) expect(child.prop('aria-pressed')).toBe(true); + else expect(child.prop('aria-pressed')).toBeFalsy(); + }); + + existsOption.simulate('click'); + availableOptionsDiv = findTestSubject(popover, 'optionsList-control-available-options'); + availableOptionsDiv.children().forEach((child, i) => { + if (child.text() === 'Exists (*)') expect(child.prop('aria-pressed')).toBe(true); + else expect(child.prop('aria-pressed')).toBeFalsy(); + }); }); - }); - test('if existsSelected = false and no suggestions, then "Exists" does not show up', async () => { - const popover = await mountComponent({ - componentState: { availableOptions: [] }, - explicitInput: { existsSelected: false }, + test('if existsSelected = false and no suggestions, then "Exists" does not show up', async () => { + const popover = await mountComponent({ + componentState: { availableOptions: [] }, + explicitInput: { existsSelected: false }, + }); + const existsOption = findTestSubject(popover, 'optionsList-control-selection-exists'); + expect(existsOption.exists()).toBeFalsy(); }); - const existsOption = findTestSubject(popover, 'optionsList-control-selection-exists'); - expect(existsOption.exists()).toBeFalsy(); - }); - test('if existsSelected = true, "Exists" is the only option when "Show only selected options" is toggled', async () => { - const popover = await mountComponent({ - explicitInput: { existsSelected: true }, + test('if existsSelected = true, "Exists" is the only option when "Show only selected options" is toggled', async () => { + const popover = await mountComponent({ + explicitInput: { existsSelected: true }, + }); + clickShowOnlySelections(popover); + const availableOptions = popover.find( + '[data-test-subj="optionsList-control-available-options"] ul' + ); + expect(availableOptions.text()).toBe('Exists. Checked option.'); }); - clickShowOnlySelections(popover); - const availableOptions = popover.find( - '[data-test-subj="optionsList-control-available-options"] ul' - ); - expect(availableOptions.text()).toBe('Exists. Checked option.'); }); - test('when sorting suggestions, show both sorting types for keyword field', async () => { - const popover = await mountComponent({ - componentState: { - field: { name: 'Test keyword field', type: 'keyword' } as FieldSpec, - }, + describe('sorting suggestions', () => { + test('when sorting suggestions, show both sorting types for keyword field', async () => { + const popover = await mountComponent({ + componentState: { + field: { name: 'Test keyword field', type: 'keyword' } as FieldSpec, + }, + }); + const sortButton = findTestSubject(popover, 'optionsListControl__sortingOptionsButton'); + sortButton.simulate('click'); + + const sortingOptionsDiv = findTestSubject(popover, 'optionsListControl__sortingOptions'); + const optionsText = sortingOptionsDiv.find('ul li').map((element) => element.text().trim()); + expect(optionsText).toEqual(['By document count. Checked option.', 'Alphabetically']); }); - const sortButton = findTestSubject(popover, 'optionsListControl__sortingOptionsButton'); - sortButton.simulate('click'); - const sortingOptionsDiv = findTestSubject(popover, 'optionsListControl__sortingOptions'); - const optionsText = sortingOptionsDiv.find('ul li').map((element) => element.text().trim()); - expect(optionsText).toEqual(['By document count. Checked option.', 'Alphabetically']); - }); + test('sorting popover selects appropriate sorting type on load', async () => { + const popover = await mountComponent({ + explicitInput: { sort: { by: '_key', direction: 'asc' } }, + componentState: { + field: { name: 'Test keyword field', type: 'keyword' } as FieldSpec, + }, + }); + const sortButton = findTestSubject(popover, 'optionsListControl__sortingOptionsButton'); + sortButton.simulate('click'); - test('sorting popover selects appropriate sorting type on load', async () => { - const popover = await mountComponent({ - explicitInput: { sort: { by: '_key', direction: 'asc' } }, - componentState: { - field: { name: 'Test keyword field', type: 'keyword' } as FieldSpec, - }, - }); - const sortButton = findTestSubject(popover, 'optionsListControl__sortingOptionsButton'); - sortButton.simulate('click'); + const sortingOptionsDiv = findTestSubject(popover, 'optionsListControl__sortingOptions'); + const optionsText = sortingOptionsDiv.find('ul li').map((element) => element.text().trim()); + expect(optionsText).toEqual(['By document count', 'Alphabetically. Checked option.']); - const sortingOptionsDiv = findTestSubject(popover, 'optionsListControl__sortingOptions'); - const optionsText = sortingOptionsDiv.find('ul li').map((element) => element.text().trim()); - expect(optionsText).toEqual(['By document count', 'Alphabetically. Checked option.']); + const ascendingButton = findTestSubject(popover, 'optionsList__sortOrder_asc').instance(); + expect(ascendingButton).toHaveClass('euiButtonGroupButton-isSelected'); + const descendingButton = findTestSubject(popover, 'optionsList__sortOrder_desc').instance(); + expect(descendingButton).not.toHaveClass('euiButtonGroupButton-isSelected'); + }); - const ascendingButton = findTestSubject(popover, 'optionsList__sortOrder_asc').instance(); - expect(ascendingButton).toHaveClass('euiButtonGroupButton-isSelected'); - const descendingButton = findTestSubject(popover, 'optionsList__sortOrder_desc').instance(); - expect(descendingButton).not.toHaveClass('euiButtonGroupButton-isSelected'); - }); + test('when sorting suggestions, only show document count sorting for IP fields', async () => { + const popover = await mountComponent({ + componentState: { field: { name: 'Test IP field', type: 'ip' } as FieldSpec }, + }); + const sortButton = findTestSubject(popover, 'optionsListControl__sortingOptionsButton'); + sortButton.simulate('click'); - test('when sorting suggestions, only show document count sorting for IP fields', async () => { - const popover = await mountComponent({ - componentState: { field: { name: 'Test IP field', type: 'ip' } as FieldSpec }, + const sortingOptionsDiv = findTestSubject(popover, 'optionsListControl__sortingOptions'); + const optionsText = sortingOptionsDiv.find('ul li').map((element) => element.text().trim()); + expect(optionsText).toEqual(['By document count. Checked option.']); }); - const sortButton = findTestSubject(popover, 'optionsListControl__sortingOptionsButton'); - sortButton.simulate('click'); - const sortingOptionsDiv = findTestSubject(popover, 'optionsListControl__sortingOptions'); - const optionsText = sortingOptionsDiv.find('ul li').map((element) => element.text().trim()); - expect(optionsText).toEqual(['By document count. Checked option.']); - }); + test('when sorting suggestions, show "By date" sorting option for date fields', async () => { + const popover = await mountComponent({ + componentState: { field: { name: 'Test date field', type: 'date' } as FieldSpec }, + }); + const sortButton = findTestSubject(popover, 'optionsListControl__sortingOptionsButton'); + sortButton.simulate('click'); - test('when sorting suggestions, show "By date" sorting option for date fields', async () => { - const popover = await mountComponent({ - componentState: { field: { name: 'Test date field', type: 'date' } as FieldSpec }, + const sortingOptionsDiv = findTestSubject(popover, 'optionsListControl__sortingOptions'); + const optionsText = sortingOptionsDiv.find('ul li').map((element) => element.text().trim()); + expect(optionsText).toEqual(['By document count. Checked option.', 'By date']); }); - const sortButton = findTestSubject(popover, 'optionsListControl__sortingOptionsButton'); - sortButton.simulate('click'); - const sortingOptionsDiv = findTestSubject(popover, 'optionsListControl__sortingOptions'); - const optionsText = sortingOptionsDiv.find('ul li').map((element) => element.text().trim()); - expect(optionsText).toEqual(['By document count. Checked option.', 'By date']); - }); + test('when sorting suggestions, show "Numerically" sorting option for number fields', async () => { + const popover = await mountComponent({ + componentState: { field: { name: 'Test number field', type: 'number' } as FieldSpec }, + }); + const sortButton = findTestSubject(popover, 'optionsListControl__sortingOptionsButton'); + sortButton.simulate('click'); - test('ensure warning icon does not show up when testAllowExpensiveQueries = true/undefined', async () => { - const popover = await mountComponent({ - componentState: { field: { name: 'Test keyword field', type: 'keyword' } as FieldSpec }, + const sortingOptionsDiv = findTestSubject(popover, 'optionsListControl__sortingOptions'); + const optionsText = sortingOptionsDiv.find('ul li').map((element) => element.text().trim()); + expect(optionsText).toEqual(['By document count. Checked option.', 'Numerically']); }); - const warning = findTestSubject(popover, 'optionsList-allow-expensive-queries-warning'); - expect(warning).toEqual({}); }); - test('ensure warning icon shows up when testAllowExpensiveQueries = false', async () => { - pluginServices.getServices().optionsList.getAllowExpensiveQueries = jest.fn(() => - Promise.resolve(false) - ); - const popover = await mountComponent({ - componentState: { - field: { name: 'Test keyword field', type: 'keyword' } as FieldSpec, - allowExpensiveQueries: false, - }, + describe('allow expensive queries warning', () => { + test('ensure warning icon does not show up when testAllowExpensiveQueries = true/undefined', async () => { + const popover = await mountComponent({ + componentState: { field: { name: 'Test keyword field', type: 'keyword' } as FieldSpec }, + }); + const warning = findTestSubject(popover, 'optionsList-allow-expensive-queries-warning'); + expect(warning).toEqual({}); + }); + + test('ensure warning icon shows up when testAllowExpensiveQueries = false', async () => { + pluginServices.getServices().optionsList.getAllowExpensiveQueries = jest.fn(() => + Promise.resolve(false) + ); + const popover = await mountComponent({ + componentState: { + field: { name: 'Test keyword field', type: 'keyword' } as FieldSpec, + allowExpensiveQueries: false, + }, + }); + const warning = findTestSubject(popover, 'optionsList-allow-expensive-queries-warning'); + expect(warning.getDOMNode()).toBeInstanceOf(HTMLDivElement); }); - const warning = findTestSubject(popover, 'optionsList-allow-expensive-queries-warning'); - expect(warning.getDOMNode()).toBeInstanceOf(HTMLDivElement); }); - describe('Test advanced settings', () => { + describe('advanced settings', () => { const ensureComponentIsHidden = async ({ explicitInput, testSubject, diff --git a/src/plugins/controls/public/options_list/components/options_list_popover_action_bar.tsx b/src/plugins/controls/public/options_list/components/options_list_popover_action_bar.tsx index 8e890350f4d40..a6754a50ecaff 100644 --- a/src/plugins/controls/public/options_list/components/options_list_popover_action_bar.tsx +++ b/src/plugins/controls/public/options_list/components/options_list_popover_action_bar.tsx @@ -6,22 +6,22 @@ * Side Public License, v 1. */ -import React from 'react'; +import React, { useMemo } from 'react'; import { - EuiFieldSearch, EuiButtonIcon, + EuiFieldSearch, EuiFlexGroup, EuiFlexItem, EuiFormRow, - EuiToolTip, EuiText, + EuiToolTip, } from '@elastic/eui'; -import { OptionsListStrings } from './options_list_strings'; +import { getCompatibleSearchTechniques } from '../../../common/options_list/suggestions_searching'; import { useOptionsList } from '../embeddable/options_list_embeddable'; import { OptionsListPopoverSortingButton } from './options_list_popover_sorting_button'; -import { OPTIONS_LIST_DEFAULT_SEARCH_TECHNIQUE } from '../../../common/options_list/types'; +import { OptionsListStrings } from './options_list_strings'; interface OptionsListPopoverProps { showOnlySelected: boolean; @@ -38,20 +38,29 @@ export const OptionsListPopoverActionBar = ({ const totalCardinality = optionsList.select((state) => state.componentState.totalCardinality) ?? 0; - const searchString = optionsList.select((state) => state.componentState.searchString); const fieldSpec = optionsList.select((state) => state.componentState.field); + const searchString = optionsList.select((state) => state.componentState.searchString); const invalidSelections = optionsList.select((state) => state.componentState.invalidSelections); + const allowExpensiveQueries = optionsList.select( + (state) => state.componentState.allowExpensiveQueries + ); const hideSort = optionsList.select((state) => state.explicitInput.hideSort); const searchTechnique = optionsList.select((state) => state.explicitInput.searchTechnique); - const allowExpensiveQueries = optionsList.select( - (state) => state.componentState.allowExpensiveQueries + const compatibleSearchTechniques = useMemo(() => { + if (!fieldSpec) return []; + return getCompatibleSearchTechniques(fieldSpec.type); + }, [fieldSpec]); + + const defaultSearchTechnique = useMemo( + () => searchTechnique ?? compatibleSearchTechniques[0], + [searchTechnique, compatibleSearchTechniques] ); return (
- {fieldSpec?.type !== 'date' && ( + {compatibleSearchTechniques.length > 0 && ( updateSearchString(event.target.value)} value={searchString.value} data-test-subj="optionsList-control-search-input" - placeholder={OptionsListStrings.popover.searchPlaceholder[ - searchTechnique ?? OPTIONS_LIST_DEFAULT_SEARCH_TECHNIQUE - ].getPlaceholderText()} + placeholder={OptionsListStrings.popover.getSearchPlaceholder( + allowExpensiveQueries ? defaultSearchTechnique : 'exact' + )} /> )} diff --git a/src/plugins/controls/public/options_list/components/options_list_popover_empty_message.tsx b/src/plugins/controls/public/options_list/components/options_list_popover_empty_message.tsx index 379a8da7040b4..c4e4918f97714 100644 --- a/src/plugins/controls/public/options_list/components/options_list_popover_empty_message.tsx +++ b/src/plugins/controls/public/options_list/components/options_list_popover_empty_message.tsx @@ -6,9 +6,11 @@ * Side Public License, v 1. */ -import React from 'react'; -import { EuiSelectableMessage, EuiIcon, EuiSpacer } from '@elastic/eui'; +import React, { useMemo } from 'react'; +import { EuiIcon, EuiSelectableMessage, EuiSpacer } from '@elastic/eui'; + +import { useOptionsList } from '../embeddable/options_list_embeddable'; import { OptionsListStrings } from './options_list_strings'; export const OptionsListPopoverEmptyMessage = ({ @@ -16,17 +18,35 @@ export const OptionsListPopoverEmptyMessage = ({ }: { showOnlySelected: boolean; }) => { + const optionsList = useOptionsList(); + + const searchString = optionsList.select((state) => state.componentState.searchString); + const fieldSpec = optionsList.select((state) => state.componentState.field); + const searchTechnique = optionsList.select((state) => state.explicitInput.searchTechnique); + + const noResultsMessage = useMemo(() => { + if (showOnlySelected) { + return OptionsListStrings.popover.getSelectionsEmptyMessage(); + } + if (!searchString.valid && fieldSpec && searchTechnique) { + return OptionsListStrings.popover.getInvalidSearchMessage(fieldSpec.type); + } + return OptionsListStrings.popover.getEmptyMessage(); + }, [showOnlySelected, fieldSpec, searchString.valid, searchTechnique]); + return ( - + - {showOnlySelected - ? OptionsListStrings.popover.getSelectionsEmptyMessage() - : OptionsListStrings.popover.getEmptyMessage()} + {noResultsMessage} ); }; diff --git a/src/plugins/controls/public/options_list/components/options_list_popover_invalid_selections.tsx b/src/plugins/controls/public/options_list/components/options_list_popover_invalid_selections.tsx index 2b6dbdc3b9507..7ff64482b2c60 100644 --- a/src/plugins/controls/public/options_list/components/options_list_popover_invalid_selections.tsx +++ b/src/plugins/controls/public/options_list/components/options_list_popover_invalid_selections.tsx @@ -77,7 +77,7 @@ export const OptionsListPopoverInvalidSelections = () => { listProps={{ onFocusBadge: false, isVirtualized: false }} onChange={(newSuggestions, _, changedOption) => { setSelectableOptions(newSuggestions); - optionsList.dispatch.deselectOption(changedOption.label); + optionsList.dispatch.deselectOption(changedOption.key ?? changedOption.label); }} > {(list) => list} diff --git a/src/plugins/controls/public/options_list/components/options_list_popover_sorting_button.tsx b/src/plugins/controls/public/options_list/components/options_list_popover_sorting_button.tsx index d1cad0bb97c81..d246e631fba84 100644 --- a/src/plugins/controls/public/options_list/components/options_list_popover_sorting_button.tsx +++ b/src/plugins/controls/public/options_list/components/options_list_popover_sorting_button.tsx @@ -119,7 +119,6 @@ export const OptionsListPopoverSortingButton = ({ aria-labelledby="optionsList_sortingOptions" closePopover={() => setIsSortingPopoverOpen(false)} panelClassName={'optionsList--sortPopover'} - anchorClassName={'optionsList__sortButtonPopoverAnchor'} > diff --git a/src/plugins/controls/public/options_list/components/options_list_popover_suggestions.tsx b/src/plugins/controls/public/options_list/components/options_list_popover_suggestions.tsx index a7efc79825b86..097660a55a540 100644 --- a/src/plugins/controls/public/options_list/components/options_list_popover_suggestions.tsx +++ b/src/plugins/controls/public/options_list/components/options_list_popover_suggestions.tsx @@ -30,17 +30,21 @@ export const OptionsListPopoverSuggestions = ({ }: OptionsListPopoverSuggestionsProps) => { const optionsList = useOptionsList(); + const fieldSpec = optionsList.select((state) => state.componentState.field); const searchString = optionsList.select((state) => state.componentState.searchString); const availableOptions = optionsList.select((state) => state.componentState.availableOptions); const totalCardinality = optionsList.select((state) => state.componentState.totalCardinality); const invalidSelections = optionsList.select((state) => state.componentState.invalidSelections); - const fieldSpec = optionsList.select((state) => state.componentState.field); + const allowExpensiveQueries = optionsList.select( + (state) => state.componentState.allowExpensiveQueries + ); const sort = optionsList.select((state) => state.explicitInput.sort); const fieldName = optionsList.select((state) => state.explicitInput.fieldName); const hideExists = optionsList.select((state) => state.explicitInput.hideExists); const singleSelect = optionsList.select((state) => state.explicitInput.singleSelect); const existsSelected = optionsList.select((state) => state.explicitInput.existsSelected); + const searchTechnique = optionsList.select((state) => state.explicitInput.searchTechnique); const selectedOptions = optionsList.select((state) => state.explicitInput.selectedOptions); const dataViewId = optionsList.select((state) => state.output.dataViewId); @@ -52,11 +56,11 @@ export const OptionsListPopoverSuggestions = ({ const canLoadMoreSuggestions = useMemo( () => - totalCardinality + allowExpensiveQueries && searchString.valid && totalCardinality && !showOnlySelected ? (availableOptions ?? []).length < Math.min(totalCardinality, MAX_OPTIONS_LIST_REQUEST_SIZE) : false, - [availableOptions, totalCardinality] + [availableOptions, totalCardinality, searchString, showOnlySelected, allowExpensiveQueries] ); // track selectedOptions and invalidSelections in sets for more efficient lookup @@ -85,7 +89,7 @@ export const OptionsListPopoverSuggestions = ({ useEffect(() => { /* This useEffect makes selectableOptions responsive to search, show only selected, and clear selections */ const options: EuiSelectableOption[] = (suggestions ?? []).map((suggestion) => { - if (typeof suggestion === 'string') { + if (typeof suggestion !== 'object') { // this means that `showOnlySelected` is true, and doc count is not known when this is the case suggestion = { value: suggestion }; } @@ -145,6 +149,19 @@ export const OptionsListPopoverSuggestions = ({ } }, [loadMoreSuggestions, totalCardinality]); + const renderOption = useCallback( + (option, searchStringValue) => { + if (!allowExpensiveQueries || searchTechnique === 'exact') return option.label; + + return ( + + {option.label} + + ); + }, + [searchTechnique, allowExpensiveQueries] + ); + useEffect(() => { const container = listRef.current; if (!isLoading && canLoadMoreSuggestions) { @@ -166,13 +183,7 @@ export const OptionsListPopoverSuggestions = ({
{ - return ( - - {option.label} - - ); - }} + renderOption={(option) => renderOption(option, searchString.value)} listProps={{ onFocusBadge: false }} aria-label={OptionsListStrings.popover.getSuggestionsAriaLabel( fieldName, diff --git a/src/plugins/controls/public/options_list/components/options_list_strings.ts b/src/plugins/controls/public/options_list/components/options_list_strings.ts index 2977bb9f1cbb3..1e1401918f61d 100644 --- a/src/plugins/controls/public/options_list/components/options_list_strings.ts +++ b/src/plugins/controls/public/options_list/components/options_list_strings.ts @@ -7,11 +7,12 @@ */ import { i18n } from '@kbn/i18n'; +import { OptionsListSearchTechnique } from '../../../common/options_list/suggestions_searching'; export const OptionsListStrings = { control: { getSeparator: (type?: string) => { - if (type === 'date') { + if (['date', 'number'].includes(type ?? '')) { return i18n.translate('controls.optionsList.control.dateSeparator', { defaultMessage: '; ', }); @@ -78,6 +79,17 @@ export const OptionsListStrings = { 'Matches values that contain the given search string. Results might take longer to populate.', }), }, + exact: { + getLabel: () => + i18n.translate('controls.optionsList.editor.exactSearchLabel', { + defaultMessage: 'Exact', + }), + getTooltip: () => + i18n.translate('controls.optionsList.editor.exactSearchTooltip', { + defaultMessage: + 'Matches values that are equal to the given search string. Returns results quickly.', + }), + }, }, getAdditionalSettingsTitle: () => i18n.translate('controls.optionsList.editor.additionalSettingsTitle', { @@ -127,6 +139,26 @@ export const OptionsListStrings = { i18n.translate('controls.optionsList.popover.selectionsEmpty', { defaultMessage: 'You have no selections', }), + getInvalidSearchMessage: (fieldType: string) => { + switch (fieldType) { + case 'ip': { + return i18n.translate('controls.optionsList.popover.invalidSearch.ip', { + defaultMessage: 'Your search is not a valid IP address.', + }); + } + case 'number': { + return i18n.translate('controls.optionsList.popover.invalidSearch.number', { + defaultMessage: 'Your search is not a valid number.', + }); + } + default: { + // this shouldn't happen, but giving a fallback error message just in case + return i18n.translate('controls.optionsList.popover.invalidSearch.invalidCharacters', { + defaultMessage: 'Your search contains invalid characters.', + }); + } + } + }, getAllOptionsButtonTitle: () => i18n.translate('controls.optionsList.popover.allOptionsTitle', { defaultMessage: 'Show all options', @@ -135,19 +167,24 @@ export const OptionsListStrings = { i18n.translate('controls.optionsList.popover.selectedOptionsTitle', { defaultMessage: 'Show only selected options', }), - searchPlaceholder: { - prefix: { - getPlaceholderText: () => - i18n.translate('controls.optionsList.popover.prefixSearchPlaceholder', { + getSearchPlaceholder: (searchTechnique?: OptionsListSearchTechnique) => { + switch (searchTechnique) { + case 'prefix': { + return i18n.translate('controls.optionsList.popover.prefixSearchPlaceholder', { defaultMessage: 'Starts with...', - }), - }, - wildcard: { - getPlaceholderText: () => - i18n.translate('controls.optionsList.popover.wildcardSearchPlaceholder', { + }); + } + case 'wildcard': { + return i18n.translate('controls.optionsList.popover.wildcardSearchPlaceholder', { defaultMessage: 'Contains...', - }), - }, + }); + } + case 'exact': { + return i18n.translate('controls.optionsList.popover.exactSearchPlaceholder', { + defaultMessage: 'Equals...', + }); + } + } }, getCardinalityLabel: (totalOptions: number) => i18n.translate('controls.optionsList.popover.cardinalityLabel', { @@ -234,14 +271,22 @@ export const OptionsListStrings = { }), }, _key: { - getSortByLabel: (type?: string) => - type === 'date' - ? i18n.translate('controls.optionsList.popover.sortBy.date', { + getSortByLabel: (type?: string) => { + switch (type) { + case 'date': + return i18n.translate('controls.optionsList.popover.sortBy.date', { defaultMessage: 'By date', - }) - : i18n.translate('controls.optionsList.popover.sortBy.alphabetical', { + }); + case 'number': + return i18n.translate('controls.optionsList.popover.sortBy.numeric', { + defaultMessage: 'Numerically', + }); + default: + return i18n.translate('controls.optionsList.popover.sortBy.alphabetical', { defaultMessage: 'Alphabetically', - }), + }); + } + }, }, }, sortOrder: { diff --git a/src/plugins/controls/public/options_list/embeddable/options_list_embeddable.tsx b/src/plugins/controls/public/options_list/embeddable/options_list_embeddable.tsx index a56aded566c6b..0426523c09107 100644 --- a/src/plugins/controls/public/options_list/embeddable/options_list_embeddable.tsx +++ b/src/plugins/controls/public/options_list/embeddable/options_list_embeddable.tsx @@ -25,7 +25,7 @@ import { import { i18n } from '@kbn/i18n'; import { DataView, FieldSpec } from '@kbn/data-views-plugin/public'; import { Embeddable, IContainer } from '@kbn/embeddable-plugin/public'; -import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; +import { KibanaThemeProvider } from '@kbn/react-kibana-context-theme'; import { ReduxEmbeddableTools, ReduxToolsPackage } from '@kbn/presentation-util-plugin/public'; import { @@ -434,7 +434,7 @@ export class OptionsListEmbeddable this.node = node; ReactDOM.render( - + ({ popoverOpen: false, @@ -36,12 +36,13 @@ export const optionsListReducers = { }, setSearchString: (state: WritableDraft, action: PayloadAction) => { state.componentState.searchString.value = action.payload; - if ( - action.payload !== '' && // empty string search is never invalid - state.componentState.field?.type === 'ip' // only IP searches can currently be invalid - ) { - state.componentState.searchString.valid = getIpRangeQuery(action.payload).validSearch; - } + state.componentState.searchString.valid = isValidSearch({ + searchString: action.payload, + fieldType: state.componentState.field?.type, + searchTechnique: state.componentState.allowExpensiveQueries + ? state.explicitInput.searchTechnique + : 'exact', // only exact match searching is supported when allowExpensiveQueries is false + }); }, setAllowExpensiveQueries: ( state: WritableDraft, diff --git a/src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.tsx b/src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.tsx index a56cd4355bf6c..6ac09cbbaea5a 100644 --- a/src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.tsx +++ b/src/plugins/controls/public/range_slider/embeddable/range_slider_embeddable.tsx @@ -6,27 +6,26 @@ * Side Public License, v 1. */ +import deepEqual from 'fast-deep-equal'; +import { get, isEmpty, isEqual } from 'lodash'; import React, { createContext, useContext } from 'react'; import ReactDOM from 'react-dom'; -import { isEmpty } from 'lodash'; import { batch } from 'react-redux'; -import { get, isEqual } from 'lodash'; -import deepEqual from 'fast-deep-equal'; -import { Subscription, lastValueFrom, switchMap } from 'rxjs'; -import { distinctUntilChanged, skip, map } from 'rxjs/operators'; +import { lastValueFrom, Subscription, switchMap } from 'rxjs'; +import { distinctUntilChanged, map, skip } from 'rxjs/operators'; +import { DataView, DataViewField } from '@kbn/data-views-plugin/public'; +import { Embeddable, IContainer } from '@kbn/embeddable-plugin/public'; import { - compareFilters, buildRangeFilter, + compareFilters, COMPARE_ALL_OPTIONS, - RangeFilterParams, Filter, + RangeFilterParams, } from '@kbn/es-query'; import { i18n } from '@kbn/i18n'; -import { Embeddable, IContainer } from '@kbn/embeddable-plugin/public'; -import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; -import { DataView, DataViewField } from '@kbn/data-views-plugin/public'; import { ReduxEmbeddableTools, ReduxToolsPackage } from '@kbn/presentation-util-plugin/public'; +import { KibanaThemeProvider } from '@kbn/react-kibana-context-theme'; import { ControlInput, @@ -35,12 +34,12 @@ import { RANGE_SLIDER_CONTROL, } from '../..'; import { pluginServices } from '../../services'; -import { RangeSliderReduxState } from '../types'; -import { IClearableControl } from '../../types'; import { ControlsDataService } from '../../services/data/types'; -import { RangeSliderControl } from '../components/range_slider_control'; import { ControlsDataViewsService } from '../../services/data_views/types'; +import { IClearableControl } from '../../types'; +import { RangeSliderControl } from '../components/range_slider_control'; import { getDefaultComponentState, rangeSliderReducers } from '../range_slider_reducers'; +import { RangeSliderReduxState } from '../types'; const diffDataFetchProps = ( current?: RangeSliderDataFetchProps, @@ -431,7 +430,7 @@ export class RangeSliderEmbeddable this.node = node; const ControlsServicesProvider = pluginServices.getContextProvider(); ReactDOM.render( - + diff --git a/src/plugins/controls/public/services/theme/theme.story.ts b/src/plugins/controls/public/services/core/core.story.ts similarity index 51% rename from src/plugins/controls/public/services/theme/theme.story.ts rename to src/plugins/controls/public/services/core/core.story.ts index 9b35b263d2302..5549f15461553 100644 --- a/src/plugins/controls/public/services/theme/theme.story.ts +++ b/src/plugins/controls/public/services/core/core.story.ts @@ -6,12 +6,16 @@ * Side Public License, v 1. */ +import { coreMock, themeServiceMock } from '@kbn/core/public/mocks'; import { PluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { Observable } from 'rxjs'; -import { ControlsThemeService } from './types'; +import { ControlsCoreService } from './types'; -export type ThemeServiceFactory = PluginServiceFactory; +export type CoreServiceFactory = PluginServiceFactory; -export const themeServiceFactory: ThemeServiceFactory = () => ({ - theme$: new Observable((subscriber) => subscriber.next({ darkMode: false })), -}); +export const coreServiceFactory: CoreServiceFactory = () => { + const corePluginMock = coreMock.createStart(); + return { + theme: themeServiceMock.createSetupContract(), + i18n: corePluginMock.i18n, + }; +}; diff --git a/src/plugins/controls/public/services/theme/theme_service.ts b/src/plugins/controls/public/services/core/core_service.ts similarity index 66% rename from src/plugins/controls/public/services/theme/theme_service.ts rename to src/plugins/controls/public/services/core/core_service.ts index 197f17797131c..3a8ac7bb89098 100644 --- a/src/plugins/controls/public/services/theme/theme_service.ts +++ b/src/plugins/controls/public/services/core/core_service.ts @@ -7,20 +7,19 @@ */ import { KibanaPluginServiceFactory } from '@kbn/presentation-util-plugin/public'; -import { ControlsThemeService } from './types'; +import { ControlsCoreService } from './types'; import { ControlsPluginStartDeps } from '../../types'; -export type ThemeServiceFactory = KibanaPluginServiceFactory< - ControlsThemeService, +export type CoreServiceFactory = KibanaPluginServiceFactory< + ControlsCoreService, ControlsPluginStartDeps >; -export const themeServiceFactory: ThemeServiceFactory = ({ coreStart }) => { - const { - theme: { theme$ }, - } = coreStart; +export const coreServiceFactory: CoreServiceFactory = ({ coreStart }) => { + const { theme, i18n } = coreStart; return { - theme$, + theme, + i18n, }; }; diff --git a/src/plugins/controls/public/services/theme/types.ts b/src/plugins/controls/public/services/core/types.ts similarity index 69% rename from src/plugins/controls/public/services/theme/types.ts rename to src/plugins/controls/public/services/core/types.ts index f577c11389f27..f5a769c077163 100644 --- a/src/plugins/controls/public/services/theme/types.ts +++ b/src/plugins/controls/public/services/core/types.ts @@ -6,9 +6,9 @@ * Side Public License, v 1. */ -import { CoreTheme } from '@kbn/core/public'; -import { Observable } from 'rxjs'; +import { CoreStart } from '@kbn/core/public'; -export interface ControlsThemeService { - theme$: Observable; +export interface ControlsCoreService { + i18n: CoreStart['i18n']; + theme: CoreStart['theme']; } diff --git a/src/plugins/controls/public/services/plugin_services.story.ts b/src/plugins/controls/public/services/plugin_services.story.ts index 64affcbd9903b..e214349095695 100644 --- a/src/plugins/controls/public/services/plugin_services.story.ts +++ b/src/plugins/controls/public/services/plugin_services.story.ts @@ -7,23 +7,22 @@ */ import { - PluginServices, - PluginServiceProviders, PluginServiceProvider, + PluginServiceProviders, PluginServiceRegistry, + PluginServices, } from '@kbn/presentation-util-plugin/public'; -import { ControlsServices } from './types'; +import { controlsServiceFactory } from './controls/controls.story'; +import { coreServiceFactory } from './core/core.story'; import { dataServiceFactory } from './data/data.story'; -import { unifiedSearchServiceFactory } from './unified_search/unified_search.story'; -import { overlaysServiceFactory } from './overlays/overlays.story'; import { dataViewsServiceFactory } from './data_views/data_views.story'; +import { embeddableServiceFactory } from './embeddable/embeddable.story'; import { httpServiceFactory } from './http/http.stub'; -import { settingsServiceFactory } from './settings/settings.story'; -import { themeServiceFactory } from './theme/theme.story'; - import { optionsListServiceFactory } from './options_list/options_list.story'; -import { controlsServiceFactory } from './controls/controls.story'; -import { embeddableServiceFactory } from './embeddable/embeddable.story'; +import { overlaysServiceFactory } from './overlays/overlays.story'; +import { settingsServiceFactory } from './settings/settings.story'; +import { ControlsServices } from './types'; +import { unifiedSearchServiceFactory } from './unified_search/unified_search.story'; export const providers: PluginServiceProviders = { dataViews: new PluginServiceProvider(dataViewsServiceFactory), @@ -32,7 +31,7 @@ export const providers: PluginServiceProviders = { unifiedSearch: new PluginServiceProvider(unifiedSearchServiceFactory), overlays: new PluginServiceProvider(overlaysServiceFactory), settings: new PluginServiceProvider(settingsServiceFactory), - theme: new PluginServiceProvider(themeServiceFactory), + core: new PluginServiceProvider(coreServiceFactory), embeddable: new PluginServiceProvider(embeddableServiceFactory), controls: new PluginServiceProvider(controlsServiceFactory), diff --git a/src/plugins/controls/public/services/plugin_services.stub.ts b/src/plugins/controls/public/services/plugin_services.stub.ts index a55d68fa4550f..07c24d72ce630 100644 --- a/src/plugins/controls/public/services/plugin_services.stub.ts +++ b/src/plugins/controls/public/services/plugin_services.stub.ts @@ -7,25 +7,25 @@ */ import { - PluginServiceProviders, PluginServiceProvider, + PluginServiceProviders, PluginServices, } from '@kbn/presentation-util-plugin/public'; -import { ControlsServices } from './types'; import { ControlsPluginStart } from '../types'; +import { ControlsServices } from './types'; -import { httpServiceFactory } from './http/http.stub'; -import { overlaysServiceFactory } from './overlays/overlays.stub'; import { controlsServiceFactory } from './controls/controls.story'; +import { coreServiceFactory } from './core/core.story'; import { dataServiceFactory } from './data/data.story'; import { dataViewsServiceFactory } from './data_views/data_views.story'; +import { embeddableServiceFactory } from './embeddable/embeddable.story'; +import { httpServiceFactory } from './http/http.stub'; import { optionsListServiceFactory } from './options_list/options_list.story'; +import { overlaysServiceFactory } from './overlays/overlays.stub'; +import { registry as stubRegistry } from './plugin_services.story'; import { settingsServiceFactory } from './settings/settings.story'; import { unifiedSearchServiceFactory } from './unified_search/unified_search.story'; -import { themeServiceFactory } from './theme/theme.story'; -import { registry as stubRegistry } from './plugin_services.story'; -import { embeddableServiceFactory } from './embeddable/embeddable.story'; export const providers: PluginServiceProviders = { embeddable: new PluginServiceProvider(embeddableServiceFactory), @@ -36,7 +36,7 @@ export const providers: PluginServiceProviders = { optionsList: new PluginServiceProvider(optionsListServiceFactory), overlays: new PluginServiceProvider(overlaysServiceFactory), settings: new PluginServiceProvider(settingsServiceFactory), - theme: new PluginServiceProvider(themeServiceFactory), + core: new PluginServiceProvider(coreServiceFactory), unifiedSearch: new PluginServiceProvider(unifiedSearchServiceFactory), }; diff --git a/src/plugins/controls/public/services/plugin_services.ts b/src/plugins/controls/public/services/plugin_services.ts index 805382254130a..e6caa3565ed60 100644 --- a/src/plugins/controls/public/services/plugin_services.ts +++ b/src/plugins/controls/public/services/plugin_services.ts @@ -7,25 +7,25 @@ */ import { - PluginServiceProviders, KibanaPluginServiceParams, PluginServiceProvider, + PluginServiceProviders, PluginServiceRegistry, PluginServices, } from '@kbn/presentation-util-plugin/public'; import { ControlsPluginStartDeps } from '../types'; import { ControlsServices } from './types'; -import { dataViewsServiceFactory } from './data_views/data_views_service'; import { controlsServiceFactory } from './controls/controls_service'; -import { overlaysServiceFactory } from './overlays/overlays_service'; +import { coreServiceFactory } from './core/core_service'; import { dataServiceFactory } from './data/data_service'; +import { dataViewsServiceFactory } from './data_views/data_views_service'; +import { embeddableServiceFactory } from './embeddable/embeddable_service'; import { httpServiceFactory } from './http/http_service'; import { optionsListServiceFactory } from './options_list/options_list_service'; +import { overlaysServiceFactory } from './overlays/overlays_service'; import { settingsServiceFactory } from './settings/settings_service'; import { unifiedSearchServiceFactory } from './unified_search/unified_search_service'; -import { themeServiceFactory } from './theme/theme_service'; -import { embeddableServiceFactory } from './embeddable/embeddable_service'; export const providers: PluginServiceProviders< ControlsServices, @@ -39,7 +39,7 @@ export const providers: PluginServiceProviders< optionsList: new PluginServiceProvider(optionsListServiceFactory, ['data', 'http']), overlays: new PluginServiceProvider(overlaysServiceFactory), settings: new PluginServiceProvider(settingsServiceFactory), - theme: new PluginServiceProvider(themeServiceFactory), + core: new PluginServiceProvider(coreServiceFactory), unifiedSearch: new PluginServiceProvider(unifiedSearchServiceFactory), }; diff --git a/src/plugins/controls/public/services/types.ts b/src/plugins/controls/public/services/types.ts index f0785f9991bed..bb86855302989 100644 --- a/src/plugins/controls/public/services/types.ts +++ b/src/plugins/controls/public/services/types.ts @@ -6,16 +6,16 @@ * Side Public License, v 1. */ -import { ControlsDataViewsService } from './data_views/types'; -import { ControlsOverlaysService } from './overlays/types'; -import { ControlsDataService } from './data/types'; -import { ControlsUnifiedSearchService } from './unified_search/types'; import { ControlsServiceType } from './controls/types'; +import { ControlsCoreService } from './core/types'; +import { ControlsDataService } from './data/types'; +import { ControlsDataViewsService } from './data_views/types'; +import { ControlsEmbeddableService } from './embeddable/types'; import { ControlsHTTPService } from './http/types'; import { ControlsOptionsListService } from './options_list/types'; +import { ControlsOverlaysService } from './overlays/types'; import { ControlsSettingsService } from './settings/types'; -import { ControlsThemeService } from './theme/types'; -import { ControlsEmbeddableService } from './embeddable/types'; +import { ControlsUnifiedSearchService } from './unified_search/types'; export interface ControlsServices { // dependency services @@ -26,7 +26,7 @@ export interface ControlsServices { unifiedSearch: ControlsUnifiedSearchService; http: ControlsHTTPService; settings: ControlsSettingsService; - theme: ControlsThemeService; + core: ControlsCoreService; // controls plugin's own services controls: ControlsServiceType; diff --git a/src/plugins/controls/public/time_slider/embeddable/time_slider_embeddable.tsx b/src/plugins/controls/public/time_slider/embeddable/time_slider_embeddable.tsx index efd3515150cf8..cd073e5f94629 100644 --- a/src/plugins/controls/public/time_slider/embeddable/time_slider_embeddable.tsx +++ b/src/plugins/controls/public/time_slider/embeddable/time_slider_embeddable.tsx @@ -7,35 +7,37 @@ */ import _ from 'lodash'; -import { debounceTime, first, map } from 'rxjs/operators'; import moment from 'moment-timezone'; -import { Embeddable, IContainer } from '@kbn/embeddable-plugin/public'; -import { ReduxEmbeddableTools, ReduxToolsPackage } from '@kbn/presentation-util-plugin/public'; -import type { TimeRange } from '@kbn/es-query'; -import { KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; import React, { createContext, useContext } from 'react'; import ReactDOM from 'react-dom'; import { Subscription } from 'rxjs'; +import { debounceTime, first, map } from 'rxjs/operators'; + +import { Embeddable, IContainer } from '@kbn/embeddable-plugin/public'; +import type { TimeRange } from '@kbn/es-query'; +import { ReduxEmbeddableTools, ReduxToolsPackage } from '@kbn/presentation-util-plugin/public'; +import { KibanaThemeProvider } from '@kbn/react-kibana-context-theme'; + import { TIME_SLIDER_CONTROL } from '../..'; import { TimeSliderControlEmbeddableInput } from '../../../common/time_slider/types'; +import { ControlGroupContainer } from '../../control_group/embeddable/control_group_container'; import { pluginServices } from '../../services'; -import { ControlsSettingsService } from '../../services/settings/types'; import { ControlsDataService } from '../../services/data/types'; +import { ControlsSettingsService } from '../../services/settings/types'; import { ControlOutput, IClearableControl } from '../../types'; -import { ControlGroupContainer } from '../../control_group/embeddable/control_group_container'; import { TimeSlider, TimeSliderPrepend } from '../components'; import { timeSliderReducers } from '../time_slider_reducers'; -import { TimeSliderReduxState } from '../types'; +import { getIsAnchored, getRoundedTimeRangeBounds } from '../time_slider_selectors'; import { + FROM_INDEX, getMomentTimezone, getStepSize, getTicks, - FROM_INDEX, - TO_INDEX, roundDownToNextStepSizeFactor, roundUpToNextStepSizeFactor, + TO_INDEX, } from '../time_utils'; -import { getIsAnchored, getRoundedTimeRangeBounds } from '../time_slider_selectors'; +import { TimeSliderReduxState } from '../types'; export const TimeSliderControlContext = createContext(null); export const useTimeSlider = (): TimeSliderControlEmbeddable => { @@ -365,7 +367,7 @@ export class TimeSliderControlEmbeddable } this.node = node; ReactDOM.render( - + { - let rawSearchResponseMock: SearchResponse = {} as SearchResponse; - - beforeEach(() => { - rawSearchResponseMock = { - hits: { - total: 10, - max_score: 10, - hits: [], - }, - took: 10, - timed_out: false, - _shards: { - failed: 0, - successful: 1, - total: 1, - skipped: 0, - }, - aggregations: {}, - }; - }); - - describe('suggestion aggregation', () => { - describe('keyword or text+keyword field', () => { - test('without a search string, creates keyword aggregation', () => { - const optionsListRequestBodyMock: OptionsListRequestBody = { - size: 10, - allowExpensiveQueries: false, - fieldName: 'coolTestField.keyword', - sort: { by: '_count', direction: 'asc' }, - fieldSpec: { aggregatable: true } as unknown as FieldSpec, - }; - const suggestionAggBuilder = getCheapSuggestionAggregationBuilder( - optionsListRequestBodyMock - ); - expect(suggestionAggBuilder.buildAggregation(optionsListRequestBodyMock)) - .toMatchInlineSnapshot(` - Object { - "suggestions": Object { - "terms": Object { - "field": "coolTestField.keyword", - "order": Object { - "_count": "asc", - }, - "shard_size": 10, - }, - }, - } - `); - }); - - test('with a search string, creates case sensitive keyword aggregation', () => { - const optionsListRequestBodyMock: OptionsListRequestBody = { - size: 10, - searchString: 'cooool', - allowExpensiveQueries: false, - fieldName: 'coolTestField.keyword', - fieldSpec: { aggregatable: true } as unknown as FieldSpec, - }; - const suggestionAggBuilder = getCheapSuggestionAggregationBuilder( - optionsListRequestBodyMock - ); - expect(suggestionAggBuilder.buildAggregation(optionsListRequestBodyMock)) - .toMatchInlineSnapshot(` - Object { - "suggestions": Object { - "terms": Object { - "field": "coolTestField.keyword", - "include": "cooool.*", - "order": Object { - "_count": "desc", - }, - "shard_size": 10, - }, - }, - } - `); - }); - }); - - test('creates nested aggregation for nested field', () => { - const optionsListRequestBodyMock: OptionsListRequestBody = { - size: 10, - searchString: 'cooool', - allowExpensiveQueries: false, - fieldName: 'coolNestedField', - sort: { by: '_key', direction: 'asc' }, - fieldSpec: { subType: { nested: { path: 'path.to.nested' } } } as unknown as FieldSpec, - }; - const suggestionAggBuilder = getCheapSuggestionAggregationBuilder(optionsListRequestBodyMock); - expect(suggestionAggBuilder.buildAggregation(optionsListRequestBodyMock)) - .toMatchInlineSnapshot(` - Object { - "nestedSuggestions": Object { - "aggs": Object { - "suggestions": Object { - "terms": Object { - "field": "coolNestedField", - "include": "cooool.*", - "order": Object { - "_key": "asc", - }, - "shard_size": 10, - }, - }, - }, - "nested": Object { - "path": "path.to.nested", - }, - }, - } - `); - }); - - describe('boolean field', () => { - test('creates boolean aggregation for boolean field', () => { - const optionsListRequestBodyMock: OptionsListRequestBody = { - size: 10, - fieldName: 'coolean', - allowExpensiveQueries: false, - sort: { by: '_key', direction: 'desc' }, - fieldSpec: { type: 'boolean' } as unknown as FieldSpec, - }; - const suggestionAggBuilder = getCheapSuggestionAggregationBuilder( - optionsListRequestBodyMock - ); - expect(suggestionAggBuilder.buildAggregation(optionsListRequestBodyMock)) - .toMatchInlineSnapshot(` - Object { - "suggestions": Object { - "terms": Object { - "field": "coolean", - "order": Object { - "_key": "desc", - }, - "shard_size": 10, - }, - }, - } - `); - }); - }); - - describe('date field field', () => { - test('creates date aggregation for date field', () => { - const optionsListRequestBodyMock: OptionsListRequestBody = { - size: 10, - fieldName: '@timestamp', - allowExpensiveQueries: false, - sort: { by: '_key', direction: 'desc' }, - fieldSpec: { type: 'date' } as unknown as FieldSpec, - }; - const suggestionAggBuilder = getCheapSuggestionAggregationBuilder( - optionsListRequestBodyMock - ); - expect(suggestionAggBuilder.buildAggregation(optionsListRequestBodyMock)) - .toMatchInlineSnapshot(` - Object { - "suggestions": Object { - "terms": Object { - "field": "@timestamp", - "order": Object { - "_key": "desc", - }, - "shard_size": 10, - }, - }, - } - `); - }); - }); - - describe('IP field', () => { - test('without a search string, creates IP range aggregation with default range', () => { - const optionsListRequestBodyMock: OptionsListRequestBody = { - size: 10, - fieldName: 'clientip', - allowExpensiveQueries: false, - sort: { by: '_count', direction: 'asc' }, - fieldSpec: { type: 'ip' } as unknown as FieldSpec, - }; - const suggestionAggBuilder = getCheapSuggestionAggregationBuilder( - optionsListRequestBodyMock - ); - expect(suggestionAggBuilder.buildAggregation(optionsListRequestBodyMock)) - .toMatchInlineSnapshot(` - Object { - "suggestions": Object { - "aggs": Object { - "filteredSuggestions": Object { - "terms": Object { - "field": "clientip", - "order": Object { - "_count": "asc", - }, - "shard_size": 10, - }, - }, - }, - "ip_range": Object { - "field": "clientip", - "keyed": true, - "ranges": Array [ - Object { - "from": "::", - "key": "ipv6", - "to": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", - }, - ], - }, - }, - } - `); - }); - - test('full IPv4 in the search string, creates IP range aggregation with CIDR mask', () => { - const optionsListRequestBodyMock: OptionsListRequestBody = { - size: 10, - fieldName: 'clientip', - allowExpensiveQueries: false, - searchString: '41.77.243.255', - sort: { by: '_key', direction: 'desc' }, - fieldSpec: { type: 'ip' } as unknown as FieldSpec, - }; - const suggestionAggBuilder = getCheapSuggestionAggregationBuilder( - optionsListRequestBodyMock - ); - expect(suggestionAggBuilder.buildAggregation(optionsListRequestBodyMock)) - .toMatchInlineSnapshot(` - Object { - "suggestions": Object { - "aggs": Object { - "filteredSuggestions": Object { - "terms": Object { - "field": "clientip", - "order": Object { - "_key": "desc", - }, - "shard_size": 10, - }, - }, - }, - "ip_range": Object { - "field": "clientip", - "keyed": true, - "ranges": Array [ - Object { - "key": "ipv4", - "mask": "41.77.243.255/32", - }, - ], - }, - }, - } - `); - }); - - test('full IPv6 in the search string, creates IP range aggregation with CIDR mask', () => { - const optionsListRequestBodyMock: OptionsListRequestBody = { - size: 10, - fieldName: 'clientip', - allowExpensiveQueries: false, - sort: { by: '_key', direction: 'asc' }, - fieldSpec: { type: 'ip' } as unknown as FieldSpec, - searchString: 'f688:fb50:6433:bba2:604:f2c:194a:d3c5', - }; - const suggestionAggBuilder = getCheapSuggestionAggregationBuilder( - optionsListRequestBodyMock - ); - expect(suggestionAggBuilder.buildAggregation(optionsListRequestBodyMock)) - .toMatchInlineSnapshot(` - Object { - "suggestions": Object { - "aggs": Object { - "filteredSuggestions": Object { - "terms": Object { - "field": "clientip", - "order": Object { - "_key": "asc", - }, - "shard_size": 10, - }, - }, - }, - "ip_range": Object { - "field": "clientip", - "keyed": true, - "ranges": Array [ - Object { - "key": "ipv6", - "mask": "f688:fb50:6433:bba2:604:f2c:194a:d3c5/128", - }, - ], - }, - }, - } - `); - }); - - test('partial IPv4 in the search string, creates IP range aggregation with min and max', () => { - const optionsListRequestBodyMock: OptionsListRequestBody = { - size: 10, - fieldName: 'clientip', - searchString: '41.77', - allowExpensiveQueries: false, - fieldSpec: { type: 'ip' } as unknown as FieldSpec, - }; - const suggestionAggBuilder = getCheapSuggestionAggregationBuilder( - optionsListRequestBodyMock - ); - expect(suggestionAggBuilder.buildAggregation(optionsListRequestBodyMock)) - .toMatchInlineSnapshot(` - Object { - "suggestions": Object { - "aggs": Object { - "filteredSuggestions": Object { - "terms": Object { - "field": "clientip", - "order": Object { - "_count": "desc", - }, - "shard_size": 10, - }, - }, - }, - "ip_range": Object { - "field": "clientip", - "keyed": true, - "ranges": Array [ - Object { - "from": "41.77.0.0", - "key": "ipv4", - "to": "41.77.255.255", - }, - ], - }, - }, - } - `); - }); - - test('partial IPv46 in the search string, creates IP range aggregation with min and max', () => { - const optionsListRequestBodyMock: OptionsListRequestBody = { - size: 10, - fieldName: 'clientip', - searchString: 'cdb6:', - allowExpensiveQueries: false, - sort: { by: '_count', direction: 'desc' }, - fieldSpec: { type: 'ip' } as unknown as FieldSpec, - }; - const suggestionAggBuilder = getCheapSuggestionAggregationBuilder( - optionsListRequestBodyMock - ); - expect(suggestionAggBuilder.buildAggregation(optionsListRequestBodyMock)) - .toMatchInlineSnapshot(` - Object { - "suggestions": Object { - "aggs": Object { - "filteredSuggestions": Object { - "terms": Object { - "field": "clientip", - "order": Object { - "_count": "desc", - }, - "shard_size": 10, - }, - }, - }, - "ip_range": Object { - "field": "clientip", - "keyed": true, - "ranges": Array [ - Object { - "from": "cdb6::", - "key": "ipv6", - "to": "cdb6:ffff:ffff:ffff:ffff:ffff:ffff:ffff", - }, - ], - }, - }, - } - `); - }); - }); - }); - - describe('suggestion parsing', () => { - test('parses keyword / text result', () => { - const optionsListRequestBodyMock: OptionsListRequestBody = { - size: 10, - searchString: 'cooool', - allowExpensiveQueries: false, - fieldName: 'coolTestField.keyword', - fieldSpec: { aggregatable: true } as unknown as FieldSpec, - }; - const suggestionAggBuilder = getCheapSuggestionAggregationBuilder(optionsListRequestBodyMock); - rawSearchResponseMock.aggregations = { - suggestions: { - buckets: [ - { doc_count: 5, key: 'cool1' }, - { doc_count: 15, key: 'cool2' }, - { doc_count: 10, key: 'cool3' }, - ], - }, - }; - expect( - suggestionAggBuilder.parse(rawSearchResponseMock, optionsListRequestBodyMock).suggestions - ).toMatchInlineSnapshot(` - Array [ - Object { - "docCount": 5, - "value": "cool1", - }, - Object { - "docCount": 15, - "value": "cool2", - }, - Object { - "docCount": 10, - "value": "cool3", - }, - ] - `); - }); - - test('parses boolean result', () => { - const optionsListRequestBodyMock: OptionsListRequestBody = { - size: 10, - fieldName: 'coolean', - allowExpensiveQueries: false, - fieldSpec: { type: 'boolean' } as unknown as FieldSpec, - }; - const suggestionAggBuilder = getCheapSuggestionAggregationBuilder(optionsListRequestBodyMock); - rawSearchResponseMock.aggregations = { - suggestions: { - buckets: [ - { doc_count: 55, key_as_string: 'false' }, - { doc_count: 155, key_as_string: 'true' }, - ], - }, - }; - expect( - suggestionAggBuilder.parse(rawSearchResponseMock, optionsListRequestBodyMock).suggestions - ).toMatchInlineSnapshot(` - Array [ - Object { - "docCount": 55, - "value": "false", - }, - Object { - "docCount": 155, - "value": "true", - }, - ] - `); - }); - - test('parses nested result', () => { - const optionsListRequestBodyMock: OptionsListRequestBody = { - size: 10, - searchString: 'cooool', - fieldName: 'coolNestedField', - allowExpensiveQueries: false, - fieldSpec: { subType: { nested: { path: 'path.to.nested' } } } as unknown as FieldSpec, - }; - const suggestionAggBuilder = getCheapSuggestionAggregationBuilder(optionsListRequestBodyMock); - rawSearchResponseMock.aggregations = { - nestedSuggestions: { - suggestions: { - buckets: [ - { doc_count: 5, key: 'cool1' }, - { doc_count: 15, key: 'cool2' }, - { doc_count: 10, key: 'cool3' }, - ], - }, - }, - }; - expect( - suggestionAggBuilder.parse(rawSearchResponseMock, optionsListRequestBodyMock).suggestions - ).toMatchInlineSnapshot(` - Array [ - Object { - "docCount": 5, - "value": "cool1", - }, - Object { - "docCount": 15, - "value": "cool2", - }, - Object { - "docCount": 10, - "value": "cool3", - }, - ] - `); - }); - - test('parses keyword only result', () => { - const optionsListRequestBodyMock: OptionsListRequestBody = { - size: 10, - searchString: 'cooool', - allowExpensiveQueries: false, - fieldName: 'coolTestField.keyword', - fieldSpec: { aggregatable: true } as unknown as FieldSpec, - }; - const suggestionAggBuilder = getCheapSuggestionAggregationBuilder(optionsListRequestBodyMock); - rawSearchResponseMock.aggregations = { - suggestions: { - buckets: [ - { doc_count: 5, key: 'cool1' }, - { doc_count: 15, key: 'cool2' }, - { doc_count: 10, key: 'cool3' }, - ], - }, - }; - expect( - suggestionAggBuilder.parse(rawSearchResponseMock, optionsListRequestBodyMock).suggestions - ).toMatchInlineSnapshot(` - Array [ - Object { - "docCount": 5, - "value": "cool1", - }, - Object { - "docCount": 15, - "value": "cool2", - }, - Object { - "docCount": 10, - "value": "cool3", - }, - ] - `); - }); - - test('parses mixed IPv4 and IPv6 result', () => { - const optionsListRequestBodyMock: OptionsListRequestBody = { - size: 10, - fieldName: 'clientip', - allowExpensiveQueries: false, - fieldSpec: { type: 'ip' } as unknown as FieldSpec, - }; - const suggestionAggBuilder = getCheapSuggestionAggregationBuilder(optionsListRequestBodyMock); - rawSearchResponseMock.aggregations = { - suggestions: { - buckets: { - ipv4: { - from: '0.0.0.0', - to: '255.255.255.255', - filteredSuggestions: { - buckets: [ - { doc_count: 8, key: '21.35.91.62' }, - { doc_count: 8, key: '21.35.91.61' }, - { doc_count: 11, key: '111.52.174.2' }, - { doc_count: 1, key: '56.73.58.63' }, - { doc_count: 9, key: '23.216.241.120' }, - { doc_count: 10, key: '196.162.13.39' }, - { doc_count: 7, key: '203.88.33.151' }, - ], - }, - }, - ipv6: { - from: '::', - to: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', - filteredSuggestions: { - buckets: [ - { doc_count: 12, key: '52:ae76:5947:5e2a:551:fe6a:712a:c72' }, - { doc_count: 1, key: 'fd:4aa0:c27c:b04:997f:2de1:51b4:8418' }, - { doc_count: 9, key: '28c7:c9a4:42fd:16b0:4de5:e41e:28d9:9172' }, - { doc_count: 6, key: '1ec:aa98:b0a6:d07c:590:18a0:8a33:2eb8' }, - { doc_count: 10, key: 'f7a9:640b:b5a0:1219:8d75:ed94:3c3e:2e63' }, - ], - }, - }, - }, - }, - }; - - const parsed = suggestionAggBuilder.parse( - rawSearchResponseMock, - optionsListRequestBodyMock - ).suggestions; - - expect(parsed).toMatchInlineSnapshot(` - Array [ - Object { - "docCount": 12, - "value": "52:ae76:5947:5e2a:551:fe6a:712a:c72", - }, - Object { - "docCount": 11, - "value": "111.52.174.2", - }, - Object { - "docCount": 10, - "value": "196.162.13.39", - }, - Object { - "docCount": 10, - "value": "f7a9:640b:b5a0:1219:8d75:ed94:3c3e:2e63", - }, - Object { - "docCount": 9, - "value": "23.216.241.120", - }, - Object { - "docCount": 9, - "value": "28c7:c9a4:42fd:16b0:4de5:e41e:28d9:9172", - }, - Object { - "docCount": 8, - "value": "21.35.91.62", - }, - Object { - "docCount": 8, - "value": "21.35.91.61", - }, - Object { - "docCount": 7, - "value": "203.88.33.151", - }, - Object { - "docCount": 6, - "value": "1ec:aa98:b0a6:d07c:590:18a0:8a33:2eb8", - }, - ] - `); - }); - - test('parses date result', () => { - const optionsListRequestBodyMock: OptionsListRequestBody = { - size: 10, - fieldName: '@timestamp', - allowExpensiveQueries: false, - fieldSpec: { type: 'date' } as unknown as FieldSpec, - }; - const suggestionAggBuilder = getCheapSuggestionAggregationBuilder(optionsListRequestBodyMock); - rawSearchResponseMock.aggregations = { - suggestions: { - buckets: [ - { doc_count: 20, key: 1696824675 }, - { doc_count: 13, key: 1686086625 }, - { doc_count: 4, key: 1703684229 }, - { doc_count: 34, key: 1688603684 }, - ], - }, - }; - - const parsed = suggestionAggBuilder.parse( - rawSearchResponseMock, - optionsListRequestBodyMock - ).suggestions; - - expect(parsed).toMatchInlineSnapshot(` - Array [ - Object { - "docCount": 20, - "value": 1696824675, - }, - Object { - "docCount": 13, - "value": 1686086625, - }, - Object { - "docCount": 4, - "value": 1703684229, - }, - Object { - "docCount": 34, - "value": 1688603684, - }, - ] - `); - }); - }); -}); diff --git a/src/plugins/controls/server/options_list/options_list_cheap_suggestion_queries.ts b/src/plugins/controls/server/options_list/options_list_cheap_suggestion_queries.ts deleted file mode 100644 index cba08877607d7..0000000000000 --- a/src/plugins/controls/server/options_list/options_list_cheap_suggestion_queries.ts +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 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 { get } from 'lodash'; -import { getFieldSubtypeNested } from '@kbn/data-views-plugin/common'; - -import { OptionsListRequestBody, OptionsListSuggestions } from '../../common/options_list/types'; -import { getIpRangeQuery, type IpRangeQuery } from '../../common/options_list/ip_search'; -import { EsBucket, OptionsListSuggestionAggregationBuilder } from './types'; -import { - getEscapedRegexQuery, - getIpBuckets, - getSortType, -} from './options_list_suggestion_query_helpers'; - -/** - * Suggestion aggregations - */ -export const getCheapSuggestionAggregationBuilder = ({ fieldSpec }: OptionsListRequestBody) => { - if (fieldSpec?.type === 'boolean') { - return cheapSuggestionAggSubtypes.boolean; - } - if (fieldSpec?.type === 'ip') { - return cheapSuggestionAggSubtypes.ip; - } - if (fieldSpec && getFieldSubtypeNested(fieldSpec)) { - return cheapSuggestionAggSubtypes.subtypeNested; - } - return cheapSuggestionAggSubtypes.keywordOrText; -}; - -const cheapSuggestionAggSubtypes: { [key: string]: OptionsListSuggestionAggregationBuilder } = { - /** - * The "textOrKeyword" query / parser should be used whenever the field is built on some type non-nested string field - * (such as a keyword field or a keyword+text multi-field) - */ - keywordOrText: { - buildAggregation: ({ fieldName, fieldSpec, searchString, sort }: OptionsListRequestBody) => ({ - suggestions: { - terms: { - field: fieldName, - // disabling for date fields because applying a search string will return an error - ...(fieldSpec?.type !== 'date' && searchString && searchString.length > 0 - ? { include: `${getEscapedRegexQuery(searchString)}.*` } - : {}), - shard_size: 10, - order: getSortType(sort), - }, - }, - }), - parse: (rawEsResult) => ({ - suggestions: get(rawEsResult, 'aggregations.suggestions.buckets')?.reduce( - (acc: OptionsListSuggestions, suggestion: EsBucket) => { - acc.push({ value: suggestion.key, docCount: suggestion.doc_count }); - return acc; - }, - [] - ), - }), - }, - - /** - * the "Boolean" query / parser should be used when the options list is built on a field of type boolean. The query is slightly different than a keyword query. - */ - boolean: { - buildAggregation: ({ fieldName, sort }: OptionsListRequestBody) => ({ - suggestions: { - terms: { - field: fieldName, - shard_size: 10, - order: getSortType(sort), - }, - }, - }), - parse: (rawEsResult) => ({ - suggestions: get(rawEsResult, 'aggregations.suggestions.buckets')?.reduce( - (acc: OptionsListSuggestions, suggestion: EsBucket & { key_as_string: string }) => { - acc.push({ value: suggestion.key_as_string, docCount: suggestion.doc_count }); - return acc; - }, - [] - ), - }), - }, - - /** - * the "IP" query / parser should be used when the options list is built on a field of type IP. - */ - ip: { - buildAggregation: ({ fieldName, searchString, sort }: OptionsListRequestBody) => { - let ipRangeQuery: IpRangeQuery = { - validSearch: true, - rangeQuery: [ - { - key: 'ipv6', - from: '::', - to: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', - }, - ], - }; - - if (searchString && searchString.length > 0) { - ipRangeQuery = getIpRangeQuery(searchString); - if (!ipRangeQuery.validSearch) { - // ideally should be prevented on the client side but, if somehow an invalid search gets through to the server, - // simply don't return an aggregation query for the ES search request - return undefined; - } - } - - return { - suggestions: { - ip_range: { - field: fieldName, - ranges: ipRangeQuery.rangeQuery, - keyed: true, - }, - aggs: { - filteredSuggestions: { - terms: { - field: fieldName, - shard_size: 10, - order: getSortType(sort), - }, - }, - }, - }, - }; - }, - parse: (rawEsResult, { sort }) => { - if (!Boolean(rawEsResult.aggregations?.suggestions)) { - // if this is happens, that means there is an invalid search that snuck through to the server side code; - // so, might as well early return with no suggestions - return { suggestions: [] }; - } - - const buckets: EsBucket[] = []; - getIpBuckets(rawEsResult, buckets, 'ipv4'); // modifies buckets array directly, i.e. "by reference" - getIpBuckets(rawEsResult, buckets, 'ipv6'); - - const sortedSuggestions = - sort?.direction === 'asc' - ? buckets.sort( - (bucketA: EsBucket, bucketB: EsBucket) => bucketA.doc_count - bucketB.doc_count - ) - : buckets.sort( - (bucketA: EsBucket, bucketB: EsBucket) => bucketB.doc_count - bucketA.doc_count - ); - - return { - suggestions: sortedSuggestions - .slice(0, 10) // only return top 10 results - .reduce((acc: OptionsListSuggestions, suggestion: EsBucket) => { - acc.push({ value: suggestion.key, docCount: suggestion.doc_count }); - return acc; - }, []), - }; - }, - }, - - /** - * the "Subtype Nested" query / parser should be used when the options list is built on a field with subtype nested. - */ - subtypeNested: { - buildAggregation: (req: OptionsListRequestBody) => { - const { fieldSpec, fieldName, searchString, sort } = req; - const subTypeNested = fieldSpec && getFieldSubtypeNested(fieldSpec); - if (!subTypeNested) { - // if this field is not subtype nested, fall back to keywordOnly - return cheapSuggestionAggSubtypes.keywordOnly.buildAggregation(req); - } - return { - nestedSuggestions: { - nested: { - path: subTypeNested.nested.path, - }, - aggs: { - suggestions: { - terms: { - field: fieldName, - ...(searchString && searchString.length > 0 - ? { include: `${getEscapedRegexQuery(searchString)}.*` } - : {}), - shard_size: 10, - order: getSortType(sort), - }, - }, - }, - }, - }; - }, - parse: (rawEsResult) => ({ - suggestions: get(rawEsResult, 'aggregations.nestedSuggestions.suggestions.buckets')?.reduce( - (acc: OptionsListSuggestions, suggestion: EsBucket) => { - acc.push({ value: suggestion.key, docCount: suggestion.doc_count }); - return acc; - }, - [] - ), - }), - }, -}; diff --git a/src/plugins/controls/server/options_list/options_list_suggestions_route.ts b/src/plugins/controls/server/options_list/options_list_suggestions_route.ts index b2a42fa0b3b19..310a04874f7fb 100644 --- a/src/plugins/controls/server/options_list/options_list_suggestions_route.ts +++ b/src/plugins/controls/server/options_list/options_list_suggestions_route.ts @@ -8,17 +8,15 @@ import { Observable } from 'rxjs'; -import { PluginSetup as UnifiedSearchPluginSetup } from '@kbn/unified-search-plugin/server'; -import { getKbnServerError, reportServerError } from '@kbn/kibana-utils-plugin/server'; +import { schema } from '@kbn/config-schema'; import { CoreSetup, ElasticsearchClient } from '@kbn/core/server'; import { SearchRequest } from '@kbn/data-plugin/common'; -import { schema } from '@kbn/config-schema'; +import { getKbnServerError, reportServerError } from '@kbn/kibana-utils-plugin/server'; +import { PluginSetup as UnifiedSearchPluginSetup } from '@kbn/unified-search-plugin/server'; import { OptionsListRequestBody, OptionsListResponse } from '../../common/options_list/types'; import { getValidationAggregationBuilder } from './options_list_validation_queries'; -import { getExpensiveSuggestionAggregationBuilder } from './options_list_expensive_suggestion_queries'; -import { getCheapSuggestionAggregationBuilder } from './options_list_cheap_suggestion_queries'; -import { OptionsListSuggestionAggregationBuilder } from './types'; +import { getSuggestionAggregationBuilder } from './suggestion_queries'; export const setupOptionsListSuggestionsRoute = ( { http }: CoreSetup, @@ -51,6 +49,13 @@ export const setupOptionsListSuggestionsRoute = ( fieldSpec: schema.maybe(schema.any()), allowExpensiveQueries: schema.boolean(), searchString: schema.maybe(schema.string()), + searchTechnique: schema.maybe( + schema.oneOf([ + schema.literal('exact'), + schema.literal('prefix'), + schema.literal('wildcard'), + ]) + ), selectedOptions: schema.maybe( schema.oneOf([schema.arrayOf(schema.string()), schema.arrayOf(schema.number())]) ), @@ -97,18 +102,13 @@ export const setupOptionsListSuggestionsRoute = ( /** * Build ES Query */ - const { runPastTimeout, filters, runtimeFieldMap, allowExpensiveQueries } = request; + const { runPastTimeout, filters, runtimeFieldMap } = request; const { terminateAfter, timeout } = getAutocompleteSettings(); const timeoutSettings = runPastTimeout ? {} : { timeout: `${timeout}ms`, terminate_after: terminateAfter }; - let suggestionBuilder: OptionsListSuggestionAggregationBuilder; - if (allowExpensiveQueries) { - suggestionBuilder = getExpensiveSuggestionAggregationBuilder(request); - } else { - suggestionBuilder = getCheapSuggestionAggregationBuilder(request); - } + const suggestionBuilder = getSuggestionAggregationBuilder(request); const validationBuilder = getValidationAggregationBuilder(); const suggestionAggregation: any = suggestionBuilder.buildAggregation(request) ?? {}; @@ -134,6 +134,7 @@ export const setupOptionsListSuggestionsRoute = ( ...runtimeFieldMap, }, }; + /** * Run ES query */ diff --git a/src/plugins/controls/server/options_list/suggestion_queries/index.ts b/src/plugins/controls/server/options_list/suggestion_queries/index.ts new file mode 100644 index 0000000000000..5474873ed5385 --- /dev/null +++ b/src/plugins/controls/server/options_list/suggestion_queries/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 { getSuggestionAggregationBuilder } from './options_list_suggestion_queries'; diff --git a/src/plugins/controls/server/options_list/suggestion_queries/options_list_all_suggestions.test.ts b/src/plugins/controls/server/options_list/suggestion_queries/options_list_all_suggestions.test.ts new file mode 100644 index 0000000000000..6b785c8c0bdb3 --- /dev/null +++ b/src/plugins/controls/server/options_list/suggestion_queries/options_list_all_suggestions.test.ts @@ -0,0 +1,142 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { FieldSpec } from '@kbn/data-views-plugin/common'; +import { OptionsListRequestBody } from '../../../common/options_list/types'; +import { getAllSuggestionsAggregationBuilder } from './options_list_all_suggestions'; + +describe('options list fetch all suggestions query', () => { + describe('suggestion aggregation', () => { + test('number field', () => { + const optionsListRequestBodyMock: OptionsListRequestBody = { + size: 10, + fieldName: 'bytes', + allowExpensiveQueries: true, + fieldSpec: { + type: 'number', + } as unknown as FieldSpec, + sort: { + by: '_key', + direction: 'asc', + }, + }; + const aggregationBuilder = getAllSuggestionsAggregationBuilder(); + const aggregation = aggregationBuilder.buildAggregation(optionsListRequestBodyMock); + + expect(aggregation).toMatchObject({ + suggestions: { + terms: { + size: 10, + shard_size: 10, + field: 'bytes', + order: { + _key: 'asc', + }, + }, + }, + unique_terms: { + cardinality: { + field: 'bytes', + }, + }, + }); + }); + + test('nested string (keyword, text+keyword) field', () => { + const optionsListRequestBodyMock: OptionsListRequestBody = { + size: 10, + fieldName: 'testField', + allowExpensiveQueries: true, + fieldSpec: { + type: 'string', + subType: { nested: { path: 'path.to.nested' } }, + } as unknown as FieldSpec, + }; + const aggregationBuilder = getAllSuggestionsAggregationBuilder(); + const aggregation = aggregationBuilder.buildAggregation(optionsListRequestBodyMock); + + expect(aggregation).toMatchObject({ + nestedSuggestions: { + nested: { + path: 'path.to.nested', + }, + aggs: { + suggestions: { + terms: { + size: 10, + shard_size: 10, + field: 'testField', + order: { + _count: 'desc', + }, + }, + }, + unique_terms: { + cardinality: { + field: 'testField', + }, + }, + }, + }, + }); + }); + }); + + test('suggestion parsing', () => { + const optionsListRequestBodyMock: OptionsListRequestBody = { + size: 10, + fieldName: 'bytes', + allowExpensiveQueries: true, + fieldSpec: { + type: 'number', + } as unknown as FieldSpec, + sort: { + by: '_key', + direction: 'asc', + }, + }; + const aggregationBuilder = getAllSuggestionsAggregationBuilder(); + const searchResponseMock = { + hits: { + total: 10, + max_score: 10, + hits: [], + }, + took: 10, + timed_out: false, + _shards: { + failed: 0, + successful: 1, + total: 1, + skipped: 0, + }, + aggregations: { + suggestions: { + buckets: [ + { doc_count: 5, key: '1' }, + { doc_count: 4, key: '2' }, + { doc_count: 3, key: '3' }, + ], + }, + unique_terms: { + value: 3, + }, + }, + }; + + const parsed = aggregationBuilder.parse(searchResponseMock, optionsListRequestBodyMock); + expect(parsed).toMatchObject({ + suggestions: [ + { value: '1', docCount: 5 }, + { value: '2', docCount: 4 }, + { value: '3', docCount: 3 }, + ], + totalCardinality: 3, + }); + }); +}); diff --git a/src/plugins/controls/server/options_list/suggestion_queries/options_list_all_suggestions.ts b/src/plugins/controls/server/options_list/suggestion_queries/options_list_all_suggestions.ts new file mode 100644 index 0000000000000..e3f82bd4a9baf --- /dev/null +++ b/src/plugins/controls/server/options_list/suggestion_queries/options_list_all_suggestions.ts @@ -0,0 +1,90 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { getFieldSubtypeNested } from '@kbn/data-views-plugin/common'; +import { get } from 'lodash'; + +import { OptionsListRequestBody, OptionsListSuggestions } from '../../../common/options_list/types'; +import { EsBucket, OptionsListSuggestionAggregationBuilder } from '../types'; +import { getSortType } from './options_list_suggestion_query_helpers'; + +/** + * Fetch all suggestions without any additional searching/filtering. + * This query will be more-or-less the same for **all** field types, + */ +export const getAllSuggestionsAggregationBuilder: () => OptionsListSuggestionAggregationBuilder = + () => allSuggestionsAggregationBuilder; + +const allSuggestionsAggregationBuilder: OptionsListSuggestionAggregationBuilder = { + buildAggregation: ({ + fieldName, + fieldSpec, + sort, + size, + allowExpensiveQueries, + }: OptionsListRequestBody) => { + let suggestionsAgg: { suggestions: any; unique_terms?: any } = { + suggestions: { + terms: { + size, + field: fieldName, + shard_size: 10, + order: getSortType(sort), + }, + }, + }; + + if (allowExpensiveQueries) { + suggestionsAgg = { + ...suggestionsAgg, + unique_terms: { + cardinality: { + field: fieldName, + }, + }, + }; + } + + const subTypeNested = fieldSpec && getFieldSubtypeNested(fieldSpec); + if (subTypeNested) { + return { + nestedSuggestions: { + nested: { + path: subTypeNested.nested.path, + }, + aggs: { + ...suggestionsAgg, + }, + }, + }; + } + + return suggestionsAgg; + }, + parse: (rawEsResult, { fieldSpec, allowExpensiveQueries }: OptionsListRequestBody) => { + const subTypeNested = fieldSpec && getFieldSubtypeNested(fieldSpec); + const suggestions = get( + rawEsResult, + `aggregations.${subTypeNested ? 'nestedSuggestions.suggestions' : 'suggestions'}.buckets` + )?.reduce((acc: OptionsListSuggestions, suggestion: EsBucket) => { + acc.push({ value: suggestion.key, docCount: suggestion.doc_count }); + return acc; + }, []); + return { + suggestions, + totalCardinality: allowExpensiveQueries + ? get( + rawEsResult, + `aggregations.${ + subTypeNested ? 'nestedSuggestions.unique_terms' : 'unique_terms' + }.value` + ) + : undefined, + }; + }, +}; diff --git a/src/plugins/controls/server/options_list/suggestion_queries/options_list_exact_match.test.ts b/src/plugins/controls/server/options_list/suggestion_queries/options_list_exact_match.test.ts new file mode 100644 index 0000000000000..77ec137e0dbe7 --- /dev/null +++ b/src/plugins/controls/server/options_list/suggestion_queries/options_list_exact_match.test.ts @@ -0,0 +1,180 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { SearchResponse } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { FieldSpec } from '@kbn/data-views-plugin/common'; +import { OptionsListRequestBody } from '../../../common/options_list/types'; +import { getExactMatchAggregationBuilder } from './options_list_exact_match'; + +describe('options list exact match search query', () => { + test('returns empty result when given invalid search', () => { + const optionsListRequestBodyMock: OptionsListRequestBody = { + size: 10, + fieldName: 'bytes', + allowExpensiveQueries: true, + sort: { by: '_key', direction: 'desc' }, + searchString: '1a2b3c', + fieldSpec: { type: 'number' } as unknown as FieldSpec, + }; + const aggregationBuilder = getExactMatchAggregationBuilder(); + const aggregation = aggregationBuilder.buildAggregation(optionsListRequestBodyMock); + expect(aggregation).toEqual({}); + const parsed = aggregationBuilder.parse( + {} as any as SearchResponse, + optionsListRequestBodyMock + ); + expect(parsed).toEqual({ suggestions: [], totalCardinality: 0 }); + }); + + describe('suggestion aggregation', () => { + test('string (keyword, text+keyword) field', () => { + const optionsListRequestBodyMock: OptionsListRequestBody = { + size: 10, + fieldName: 'testField', + allowExpensiveQueries: true, + searchString: 'searchForMe', + fieldSpec: { type: 'string' } as unknown as FieldSpec, + }; + const aggregationBuilder = getExactMatchAggregationBuilder(); + const aggregation = aggregationBuilder.buildAggregation(optionsListRequestBodyMock); + expect(aggregation).toMatchObject({ + suggestions: { + filter: { + term: { + testField: { + value: 'searchForMe', + case_insensitive: true, + }, + }, + }, + aggs: { + filteredSuggestions: { + terms: { + field: 'testField', + shard_size: 10, + }, + }, + }, + }, + }); + }); + + test('nested string (keyword, text+keyword) field', () => { + const optionsListRequestBodyMock: OptionsListRequestBody = { + size: 10, + fieldName: 'testField', + allowExpensiveQueries: true, + searchString: 'searchForMe', + fieldSpec: { + type: 'string', + subType: { nested: { path: 'path.to.nested' } }, + } as unknown as FieldSpec, + }; + const aggregationBuilder = getExactMatchAggregationBuilder(); + const aggregation = aggregationBuilder.buildAggregation(optionsListRequestBodyMock); + + expect(aggregation).toMatchObject({ + nestedSuggestions: { + nested: { + path: 'path.to.nested', + }, + aggs: { + suggestions: { + filter: { + term: { + testField: { + value: 'searchForMe', + case_insensitive: true, + }, + }, + }, + aggs: { + filteredSuggestions: { + terms: { + field: 'testField', + shard_size: 10, + }, + }, + }, + }, + }, + }, + }); + }); + + test('number field', () => { + const optionsListRequestBodyMock: OptionsListRequestBody = { + size: 10, + fieldName: 'bytes', + allowExpensiveQueries: true, + searchString: '123', + fieldSpec: { type: 'number' } as unknown as FieldSpec, + }; + const aggregationBuilder = getExactMatchAggregationBuilder(); + const aggregation = aggregationBuilder.buildAggregation(optionsListRequestBodyMock); + expect(aggregation).toMatchObject({ + suggestions: { + filter: { + term: { + bytes: { + value: '123', + case_insensitive: false, // this is the only part that is dependent on field type + }, + }, + }, + aggs: { + filteredSuggestions: { + terms: { + field: 'bytes', + shard_size: 10, + }, + }, + }, + }, + }); + }); + }); + + test('suggestion parsing', () => { + const optionsListRequestBodyMock: OptionsListRequestBody = { + size: 10, + searchString: 'cool', + allowExpensiveQueries: true, + fieldName: 'coolTestField.keyword', + fieldSpec: { type: 'string' } as unknown as FieldSpec, + }; + const aggregationBuilder = getExactMatchAggregationBuilder(); + + const searchResponseMock = { + hits: { + total: 1, + max_score: 1, + hits: [], + }, + took: 10, + timed_out: false, + _shards: { + failed: 0, + successful: 1, + total: 1, + skipped: 0, + }, + aggregations: { + suggestions: { + filteredSuggestions: { + buckets: [{ doc_count: 5, key: 'cool1' }], + }, + }, + }, + }; + expect(aggregationBuilder.parse(searchResponseMock, optionsListRequestBodyMock)).toMatchObject({ + suggestions: [{ docCount: 5, value: 'cool1' }], + totalCardinality: 1, + }); + }); +}); diff --git a/src/plugins/controls/server/options_list/suggestion_queries/options_list_exact_match.ts b/src/plugins/controls/server/options_list/suggestion_queries/options_list_exact_match.ts new file mode 100644 index 0000000000000..e9da0c029418f --- /dev/null +++ b/src/plugins/controls/server/options_list/suggestion_queries/options_list_exact_match.ts @@ -0,0 +1,91 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { getFieldSubtypeNested } from '@kbn/data-views-plugin/common'; +import { get } from 'lodash'; + +import { isValidSearch } from '../../../common/options_list/is_valid_search'; +import { OptionsListRequestBody, OptionsListSuggestions } from '../../../common/options_list/types'; +import { EsBucket, OptionsListSuggestionAggregationBuilder } from '../types'; + +/** + * Search for an exact match based on the provided search string. + * This query will be more-or-less the same for **all** field types, and it should only ever return + * 0 (if no match) or 1 (if a match was found) results. + */ +export const getExactMatchAggregationBuilder: () => OptionsListSuggestionAggregationBuilder = () => + exactMatchAggregationBuilder; + +const exactMatchAggregationBuilder: OptionsListSuggestionAggregationBuilder = { + buildAggregation: ({ fieldName, fieldSpec, searchString }: OptionsListRequestBody) => { + if (!isValidSearch({ searchString, fieldType: fieldSpec?.type, searchTechnique: 'exact' })) { + return {}; + } + + const suggestionsAgg = { + suggestions: { + filter: { + term: { + [fieldName]: { + value: searchString, + case_insensitive: fieldSpec?.type === 'string', + }, + }, + }, + aggs: { + filteredSuggestions: { + terms: { + field: fieldName, + shard_size: 10, + }, + }, + }, + }, + }; + + const subTypeNested = fieldSpec && getFieldSubtypeNested(fieldSpec); + if (subTypeNested) { + return { + nestedSuggestions: { + nested: { + path: subTypeNested.nested.path, + }, + aggs: { + ...suggestionsAgg, + }, + }, + }; + } + + return suggestionsAgg; + }, + parse: (rawEsResult, { searchString, fieldSpec }) => { + if (!isValidSearch({ searchString, fieldType: fieldSpec?.type, searchTechnique: 'exact' })) { + // if this is happens, that means there is an invalid search that snuck through to the server side code; + // so, might as well early return with no suggestions + return { suggestions: [], totalCardinality: 0 }; + } + + const subTypeNested = fieldSpec && getFieldSubtypeNested(fieldSpec); + + const suggestions = get( + rawEsResult, + `aggregations.${ + subTypeNested ? 'nestedSuggestions.suggestions' : 'suggestions' + }.filteredSuggestions.buckets` + )?.reduce((acc: OptionsListSuggestions, suggestion: EsBucket) => { + acc.push({ value: suggestion.key, docCount: suggestion.doc_count }); + return acc; + }, []); + + return { + suggestions, + totalCardinality: suggestions.length, // should only be 0 or 1, so it's safe to use length here + }; + }, +}; diff --git a/src/plugins/controls/server/options_list/options_list_expensive_suggestion_queries.test.ts b/src/plugins/controls/server/options_list/suggestion_queries/options_list_search_suggestions.test.ts similarity index 66% rename from src/plugins/controls/server/options_list/options_list_expensive_suggestion_queries.test.ts rename to src/plugins/controls/server/options_list/suggestion_queries/options_list_search_suggestions.test.ts index a5d6f32f63377..b2ddb699ebaad 100644 --- a/src/plugins/controls/server/options_list/options_list_expensive_suggestion_queries.test.ts +++ b/src/plugins/controls/server/options_list/suggestion_queries/options_list_search_suggestions.test.ts @@ -6,13 +6,18 @@ * Side Public License, v 1. */ -import { FieldSpec } from '@kbn/data-views-plugin/common'; import { SearchResponse } from '@elastic/elasticsearch/lib/api/types'; +import { FieldSpec } from '@kbn/data-views-plugin/common'; + +import { OptionsListRequestBody } from '../../../common/options_list/types'; +import { getExactMatchAggregationBuilder } from './options_list_exact_match'; +import { getSearchSuggestionsAggregationBuilder } from './options_list_search_suggestions'; -import { getExpensiveSuggestionAggregationBuilder } from './options_list_expensive_suggestion_queries'; -import { OptionsListRequestBody } from '../../common/options_list/types'; +jest.mock('./options_list_exact_match', () => ({ + getExactMatchAggregationBuilder: jest.fn(), +})); -describe('options list expensive queries', () => { +describe('options list type-specific search queries', () => { let rawSearchResponseMock: SearchResponse = {} as SearchResponse; beforeEach(() => { @@ -35,40 +40,19 @@ describe('options list expensive queries', () => { }); describe('suggestion aggregation', () => { - describe('string (keyword, text+keyword, or nested) field', () => { - test('test keyword field, without a search string', () => { - const optionsListRequestBodyMock: OptionsListRequestBody = { - size: 10, - allowExpensiveQueries: true, - fieldName: 'coolTestField.keyword', - sort: { by: '_key', direction: 'asc' }, - fieldSpec: { aggregatable: true } as unknown as FieldSpec, - }; - const suggestionAggBuilder = getExpensiveSuggestionAggregationBuilder( - optionsListRequestBodyMock - ); - expect(suggestionAggBuilder.buildAggregation(optionsListRequestBodyMock)) - .toMatchInlineSnapshot(` - Object { - "suggestions": Object { - "terms": Object { - "field": "coolTestField.keyword", - "order": Object { - "_key": "asc", - }, - "shard_size": 10, - "size": 10, - }, - }, - "unique_terms": Object { - "cardinality": Object { - "field": "coolTestField.keyword", - }, - }, - } - `); - }); + test('for unsupported field types, return exact match search instead', () => { + const optionsListRequestBodyMock: OptionsListRequestBody = { + size: 10, + fieldName: 'success', + allowExpensiveQueries: true, + sort: { by: '_key', direction: 'desc' }, + fieldSpec: { type: 'boolean' } as unknown as FieldSpec, + }; + getSearchSuggestionsAggregationBuilder(optionsListRequestBodyMock); + expect(getExactMatchAggregationBuilder).toBeCalled(); + }); + describe('string (keyword, text+keyword, or nested) field', () => { test('test keyword field, with a search string', () => { const optionsListRequestBodyMock: OptionsListRequestBody = { size: 10, @@ -76,9 +60,9 @@ describe('options list expensive queries', () => { allowExpensiveQueries: true, fieldName: 'coolTestField.keyword', sort: { by: '_key', direction: 'desc' }, - fieldSpec: { aggregatable: true } as unknown as FieldSpec, + fieldSpec: { type: 'string' } as unknown as FieldSpec, }; - const suggestionAggBuilder = getExpensiveSuggestionAggregationBuilder( + const suggestionAggBuilder = getSearchSuggestionsAggregationBuilder( optionsListRequestBodyMock ); expect(suggestionAggBuilder.buildAggregation(optionsListRequestBodyMock)) @@ -123,9 +107,9 @@ describe('options list expensive queries', () => { allowExpensiveQueries: true, fieldName: 'coolTestField.keyword', sort: { by: '_key', direction: 'desc' }, - fieldSpec: { aggregatable: true } as unknown as FieldSpec, + fieldSpec: { type: 'string' } as unknown as FieldSpec, }; - const suggestionAggBuilder = getExpensiveSuggestionAggregationBuilder( + const suggestionAggBuilder = getSearchSuggestionsAggregationBuilder( optionsListRequestBodyMock ); expect(suggestionAggBuilder.buildAggregation(optionsListRequestBodyMock)) @@ -170,9 +154,9 @@ describe('options list expensive queries', () => { allowExpensiveQueries: true, fieldName: 'coolTestField.keyword', sort: { by: '_key', direction: 'desc' }, - fieldSpec: { aggregatable: true } as unknown as FieldSpec, + fieldSpec: { type: 'string' } as unknown as FieldSpec, }; - const suggestionAggBuilder = getExpensiveSuggestionAggregationBuilder( + const suggestionAggBuilder = getSearchSuggestionsAggregationBuilder( optionsListRequestBodyMock ); expect(suggestionAggBuilder.buildAggregation(optionsListRequestBodyMock)) @@ -216,9 +200,12 @@ describe('options list expensive queries', () => { allowExpensiveQueries: true, fieldName: 'coolNestedField', sort: { by: '_count', direction: 'asc' }, - fieldSpec: { subType: { nested: { path: 'path.to.nested' } } } as unknown as FieldSpec, + fieldSpec: { + type: 'string', + subType: { nested: { path: 'path.to.nested' } }, + } as unknown as FieldSpec, }; - const suggestionAggBuilder = getExpensiveSuggestionAggregationBuilder( + const suggestionAggBuilder = getSearchSuggestionsAggregationBuilder( optionsListRequestBodyMock ); expect(suggestionAggBuilder.buildAggregation(optionsListRequestBodyMock)) @@ -263,151 +250,20 @@ describe('options list expensive queries', () => { }); }); - describe('boolean field', () => { - test('creates boolean aggregation for boolean field', () => { - const optionsListRequestBodyMock: OptionsListRequestBody = { - size: 10, - fieldName: 'coolean', - allowExpensiveQueries: true, - sort: { by: '_key', direction: 'desc' }, - fieldSpec: { type: 'boolean' } as unknown as FieldSpec, - }; - const suggestionAggBuilder = getExpensiveSuggestionAggregationBuilder( - optionsListRequestBodyMock - ); - expect(suggestionAggBuilder.buildAggregation(optionsListRequestBodyMock)) - .toMatchInlineSnapshot(` - Object { - "suggestions": Object { - "terms": Object { - "field": "coolean", - "order": Object { - "_key": "desc", - }, - "shard_size": 10, - }, - }, - } - `); - }); - }); - - describe('date field field', () => { - test('creates date aggregation for date field', () => { - const optionsListRequestBodyMock: OptionsListRequestBody = { - size: 10, - fieldName: '@timestamp', - allowExpensiveQueries: true, - sort: { by: '_key', direction: 'desc' }, - fieldSpec: { type: 'date' } as unknown as FieldSpec, - }; - const suggestionAggBuilder = getExpensiveSuggestionAggregationBuilder( - optionsListRequestBodyMock - ); - expect(suggestionAggBuilder.buildAggregation(optionsListRequestBodyMock)) - .toMatchInlineSnapshot(` - Object { - "suggestions": Object { - "terms": Object { - "field": "@timestamp", - "order": Object { - "_key": "desc", - }, - "shard_size": 10, - "size": 10, - }, - }, - "unique_terms": Object { - "cardinality": Object { - "field": "@timestamp", - }, - }, - } - `); - }); - - test('does not throw error when receiving search string', () => { - const optionsListRequestBodyMock: OptionsListRequestBody = { - size: 10, - fieldName: '@timestamp', - allowExpensiveQueries: true, - sort: { by: '_key', direction: 'desc' }, - searchString: '2023', - fieldSpec: { type: 'date' } as unknown as FieldSpec, - }; - const suggestionAggBuilder = getExpensiveSuggestionAggregationBuilder( - optionsListRequestBodyMock - ); - expect(suggestionAggBuilder.buildAggregation(optionsListRequestBodyMock)) - .toMatchInlineSnapshot(` - Object { - "suggestions": Object { - "terms": Object { - "field": "@timestamp", - "order": Object { - "_key": "desc", - }, - "shard_size": 10, - "size": 10, - }, - }, - "unique_terms": Object { - "cardinality": Object { - "field": "@timestamp", - }, - }, - } - `); - }); - }); - describe('IP field', () => { - test('without a search string, creates IP range aggregation with default range', () => { + test('handles an invalid search', () => { const optionsListRequestBodyMock: OptionsListRequestBody = { size: 10, fieldName: 'clientip', allowExpensiveQueries: true, sort: { by: '_key', direction: 'asc' }, + searchString: '1.a.2.b.3.z', fieldSpec: { type: 'ip' } as unknown as FieldSpec, }; - const suggestionAggBuilder = getExpensiveSuggestionAggregationBuilder( + const suggestionAggBuilder = getSearchSuggestionsAggregationBuilder( optionsListRequestBodyMock ); - expect(suggestionAggBuilder.buildAggregation(optionsListRequestBodyMock)) - .toMatchInlineSnapshot(` - Object { - "suggestions": Object { - "aggs": Object { - "filteredSuggestions": Object { - "terms": Object { - "field": "clientip", - "order": Object { - "_key": "asc", - }, - "shard_size": 10, - "size": 10, - }, - }, - "unique_terms": Object { - "cardinality": Object { - "field": "clientip", - }, - }, - }, - "ip_range": Object { - "field": "clientip", - "keyed": true, - "ranges": Array [ - Object { - "from": "::", - "key": "ipv6", - "to": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", - }, - ], - }, - }, - } - `); + expect(suggestionAggBuilder.buildAggregation(optionsListRequestBodyMock)).toEqual({}); }); test('full IPv4 in the search string, creates IP range aggregation with CIDR mask', () => { @@ -419,7 +275,7 @@ describe('options list expensive queries', () => { sort: { by: '_count', direction: 'asc' }, fieldSpec: { type: 'ip' } as unknown as FieldSpec, }; - const suggestionAggBuilder = getExpensiveSuggestionAggregationBuilder( + const suggestionAggBuilder = getSearchSuggestionsAggregationBuilder( optionsListRequestBodyMock ); expect(suggestionAggBuilder.buildAggregation(optionsListRequestBodyMock)) @@ -467,7 +323,7 @@ describe('options list expensive queries', () => { fieldSpec: { type: 'ip' } as unknown as FieldSpec, searchString: 'f688:fb50:6433:bba2:604:f2c:194a:d3c5', }; - const suggestionAggBuilder = getExpensiveSuggestionAggregationBuilder( + const suggestionAggBuilder = getSearchSuggestionsAggregationBuilder( optionsListRequestBodyMock ); expect(suggestionAggBuilder.buildAggregation(optionsListRequestBodyMock)) @@ -514,7 +370,7 @@ describe('options list expensive queries', () => { allowExpensiveQueries: true, fieldSpec: { type: 'ip' } as unknown as FieldSpec, }; - const suggestionAggBuilder = getExpensiveSuggestionAggregationBuilder( + const suggestionAggBuilder = getSearchSuggestionsAggregationBuilder( optionsListRequestBodyMock ); expect(suggestionAggBuilder.buildAggregation(optionsListRequestBodyMock)) @@ -563,7 +419,7 @@ describe('options list expensive queries', () => { sort: { by: '_count', direction: 'desc' }, fieldSpec: { type: 'ip' } as unknown as FieldSpec, }; - const suggestionAggBuilder = getExpensiveSuggestionAggregationBuilder( + const suggestionAggBuilder = getSearchSuggestionsAggregationBuilder( optionsListRequestBodyMock ); expect(suggestionAggBuilder.buildAggregation(optionsListRequestBodyMock)) @@ -606,26 +462,29 @@ describe('options list expensive queries', () => { }); describe('suggestion parsing', () => { - test('parses string (keyword, text+keyword, or nested) result', () => { + test('parses string (keyword, text+keyword) result', () => { const optionsListRequestBodyMock: OptionsListRequestBody = { size: 10, + searchString: 'cool', allowExpensiveQueries: true, fieldName: 'coolTestField.keyword', - fieldSpec: { aggregatable: true } as unknown as FieldSpec, + fieldSpec: { type: 'string' } as unknown as FieldSpec, }; - const suggestionAggBuilder = getExpensiveSuggestionAggregationBuilder( + const suggestionAggBuilder = getSearchSuggestionsAggregationBuilder( optionsListRequestBodyMock ); rawSearchResponseMock.aggregations = { - suggestions: { - buckets: [ - { doc_count: 5, key: 'cool1' }, - { doc_count: 15, key: 'cool2' }, - { doc_count: 10, key: 'cool3' }, - ], - }, - unique_terms: { - value: 3, + filteredSuggestions: { + suggestions: { + buckets: [ + { doc_count: 5, key: 'cool1' }, + { doc_count: 15, key: 'cool2' }, + { doc_count: 10, key: 'cool3' }, + ], + }, + unique_terms: { + value: 3, + }, }, }; expect(suggestionAggBuilder.parse(rawSearchResponseMock, optionsListRequestBodyMock)) @@ -650,51 +509,18 @@ describe('options list expensive queries', () => { `); }); - test('parses boolean result', () => { - const optionsListRequestBodyMock: OptionsListRequestBody = { - size: 10, - fieldName: 'coolean', - allowExpensiveQueries: true, - fieldSpec: { type: 'boolean' } as unknown as FieldSpec, - }; - const suggestionAggBuilder = getExpensiveSuggestionAggregationBuilder( - optionsListRequestBodyMock - ); - rawSearchResponseMock.aggregations = { - suggestions: { - buckets: [ - { doc_count: 55, key_as_string: 'false' }, - { doc_count: 155, key_as_string: 'true' }, - ], - }, - }; - expect(suggestionAggBuilder.parse(rawSearchResponseMock, optionsListRequestBodyMock)) - .toMatchInlineSnapshot(` - Object { - "suggestions": Array [ - Object { - "docCount": 55, - "value": "false", - }, - Object { - "docCount": 155, - "value": "true", - }, - ], - "totalCardinality": 2, - } - `); - }); - - test('parses nested result', () => { + test('parses string nested result', () => { const optionsListRequestBodyMock: OptionsListRequestBody = { size: 10, searchString: 'co', fieldName: 'coolNestedField', allowExpensiveQueries: true, - fieldSpec: { subType: { nested: { path: 'path.to.nested' } } } as unknown as FieldSpec, + fieldSpec: { + type: 'string', + subType: { type: 'string', nested: { path: 'path.to.nested' } }, + } as unknown as FieldSpec, }; - const suggestionAggBuilder = getExpensiveSuggestionAggregationBuilder( + const suggestionAggBuilder = getSearchSuggestionsAggregationBuilder( optionsListRequestBodyMock ); rawSearchResponseMock.aggregations = { @@ -738,11 +564,12 @@ describe('options list expensive queries', () => { test('parses mixed IPv4 and IPv6 result', () => { const optionsListRequestBodyMock: OptionsListRequestBody = { size: 10, + searchString: '21', fieldName: 'clientip', allowExpensiveQueries: true, fieldSpec: { type: 'ip' } as unknown as FieldSpec, }; - const suggestionAggBuilder = getExpensiveSuggestionAggregationBuilder( + const suggestionAggBuilder = getSearchSuggestionsAggregationBuilder( optionsListRequestBodyMock ); rawSearchResponseMock.aggregations = { @@ -840,53 +667,5 @@ describe('options list expensive queries', () => { ] `); }); - - test('parses date result', () => { - const optionsListRequestBodyMock: OptionsListRequestBody = { - size: 10, - fieldName: '@timestamp', - allowExpensiveQueries: true, - fieldSpec: { type: 'date' } as unknown as FieldSpec, - }; - const suggestionAggBuilder = getExpensiveSuggestionAggregationBuilder( - optionsListRequestBodyMock - ); - rawSearchResponseMock.aggregations = { - suggestions: { - buckets: [ - { doc_count: 20, key: 1696824675 }, - { doc_count: 13, key: 1686086625 }, - { doc_count: 4, key: 1703684229 }, - { doc_count: 34, key: 1688603684 }, - ], - }, - }; - - const parsed = suggestionAggBuilder.parse( - rawSearchResponseMock, - optionsListRequestBodyMock - ).suggestions; - - expect(parsed).toMatchInlineSnapshot(` - Array [ - Object { - "docCount": 20, - "value": 1696824675, - }, - Object { - "docCount": 13, - "value": 1686086625, - }, - Object { - "docCount": 4, - "value": 1703684229, - }, - Object { - "docCount": 34, - "value": 1688603684, - }, - ] - `); - }); }); }); diff --git a/src/plugins/controls/server/options_list/options_list_expensive_suggestion_queries.ts b/src/plugins/controls/server/options_list/suggestion_queries/options_list_search_suggestions.ts similarity index 57% rename from src/plugins/controls/server/options_list/options_list_expensive_suggestion_queries.ts rename to src/plugins/controls/server/options_list/suggestion_queries/options_list_search_suggestions.ts index 156a966f6a482..47f1fe5f696dc 100644 --- a/src/plugins/controls/server/options_list/options_list_expensive_suggestion_queries.ts +++ b/src/plugins/controls/server/options_list/suggestion_queries/options_list_search_suggestions.ts @@ -6,16 +6,15 @@ * Side Public License, v 1. */ -import { get } from 'lodash'; import { getFieldSubtypeNested } from '@kbn/data-views-plugin/common'; +import { get } from 'lodash'; -import { - OptionsListRequestBody, - OptionsListSuggestions, - OPTIONS_LIST_DEFAULT_SEARCH_TECHNIQUE, -} from '../../common/options_list/types'; -import { getIpRangeQuery, type IpRangeQuery } from '../../common/options_list/ip_search'; -import { EsBucket, OptionsListSuggestionAggregationBuilder } from './types'; +import { getIpRangeQuery } from '../../../common/options_list/ip_search'; +import { isValidSearch } from '../../../common/options_list/is_valid_search'; +import { getDefaultSearchTechnique } from '../../../common/options_list/suggestions_searching'; +import { OptionsListRequestBody, OptionsListSuggestions } from '../../../common/options_list/types'; +import { EsBucket, OptionsListSuggestionAggregationBuilder } from '../types'; +import { getExactMatchAggregationBuilder } from './options_list_exact_match'; import { getEscapedWildcardQuery, getIpBuckets, @@ -23,19 +22,28 @@ import { } from './options_list_suggestion_query_helpers'; /** - * Suggestion aggregations + * Type-specific search suggestion aggregations. These queries are highly impacted by the field type. */ -export const getExpensiveSuggestionAggregationBuilder = ({ fieldSpec }: OptionsListRequestBody) => { - if (fieldSpec?.type === 'boolean') { - return expensiveSuggestionAggSubtypes.boolean; - } - if (fieldSpec?.type === 'ip') { - return expensiveSuggestionAggSubtypes.ip; +export const getSearchSuggestionsAggregationBuilder = (request: OptionsListRequestBody) => { + const { fieldSpec } = request; + + // note that date and boolean fields are non-searchable, so type-specific search aggs are not necessary; + // number fields, on the other hand, only support exact match searching - so, this also does not need a + // type-specific agg because it will be handled by `exactMatchSearchAggregation` + switch (fieldSpec?.type) { + case 'ip': { + return suggestionAggSubtypes.ip; + } + case 'string': { + return suggestionAggSubtypes.textOrKeywordOrNested; + } + default: + // safe guard just in case an invalid/unsupported field type somehow got through + return getExactMatchAggregationBuilder(); } - return expensiveSuggestionAggSubtypes.textOrKeywordOrNested; }; -const expensiveSuggestionAggSubtypes: { [key: string]: OptionsListSuggestionAggregationBuilder } = { +const suggestionAggSubtypes: { [key: string]: OptionsListSuggestionAggregationBuilder } = { /** * The "textOrKeywordOrNested" query / parser should be used whenever the field is built on some type of string field, * regardless of if it is keyword only, keyword+text, or some nested keyword/keyword+text field. @@ -49,28 +57,19 @@ const expensiveSuggestionAggSubtypes: { [key: string]: OptionsListSuggestionAggr sort, size, }: OptionsListRequestBody) => { + const hasSearchString = searchString && searchString.length > 0; + if (!hasSearchString || fieldSpec?.type === 'date') { + // we can assume that this is only ever called with a search string, and date fields are not + // currently searchable; so, if any of these things is true, this is invalid. + return undefined; + } + const subTypeNested = fieldSpec && getFieldSubtypeNested(fieldSpec); let textOrKeywordQuery: any = { - suggestions: { - terms: { - size, - field: fieldName, - shard_size: 10, - order: getSortType(sort), - }, - }, - unique_terms: { - cardinality: { - field: fieldName, - }, - }, - }; - // disabling for date fields because applying a search string will return an error - if (fieldSpec?.type !== 'date' && searchString && searchString.length > 0) { - textOrKeywordQuery = { - filteredSuggestions: { - filter: { - [(searchTechnique ?? OPTIONS_LIST_DEFAULT_SEARCH_TECHNIQUE) as string]: { + filteredSuggestions: { + filter: { + [(searchTechnique ?? getDefaultSearchTechnique(fieldSpec?.type ?? 'string')) as string]: + { [fieldName]: { value: searchTechnique === 'wildcard' @@ -79,11 +78,25 @@ const expensiveSuggestionAggSubtypes: { [key: string]: OptionsListSuggestionAggr case_insensitive: true, }, }, + }, + aggs: { + suggestions: { + terms: { + size, + field: fieldName, + shard_size: 10, + order: getSortType(sort), + }, + }, + unique_terms: { + cardinality: { + field: fieldName, + }, }, - aggs: { ...textOrKeywordQuery }, }, - }; - } + }, + }; + if (subTypeNested) { textOrKeywordQuery = { nestedSuggestions: { @@ -98,11 +111,10 @@ const expensiveSuggestionAggSubtypes: { [key: string]: OptionsListSuggestionAggr } return textOrKeywordQuery; }, - parse: (rawEsResult, request) => { + parse: (rawEsResult, { fieldSpec }) => { let basePath = 'aggregations'; - const isNested = request.fieldSpec && getFieldSubtypeNested(request.fieldSpec); - basePath += isNested ? '.nestedSuggestions' : ''; - basePath += request.searchString ? '.filteredSuggestions' : ''; + const isNested = fieldSpec && getFieldSubtypeNested(fieldSpec); + basePath += isNested ? '.nestedSuggestions.filteredSuggestions' : '.filteredSuggestions'; const suggestions = get(rawEsResult, `${basePath}.suggestions.buckets`)?.reduce( (acc: OptionsListSuggestions, suggestion: EsBucket) => { @@ -119,55 +131,23 @@ const expensiveSuggestionAggSubtypes: { [key: string]: OptionsListSuggestionAggr }, /** - * the "Boolean" query / parser should be used when the options list is built on a field of type boolean. The query is slightly different than a keyword query. + * the "IP" query / parser should be used when the options list is built on a field of type IP. */ - boolean: { - buildAggregation: ({ fieldName, sort }: OptionsListRequestBody) => ({ - suggestions: { + ip: { + buildAggregation: ({ fieldName, searchString, sort, size }: OptionsListRequestBody) => { + const filteredSuggestions = { terms: { + size, field: fieldName, shard_size: 10, order: getSortType(sort), }, - }, - }), - parse: (rawEsResult) => { - const suggestions = get(rawEsResult, 'aggregations.suggestions.buckets')?.reduce( - (acc: OptionsListSuggestions, suggestion: EsBucket & { key_as_string: string }) => { - acc.push({ value: suggestion.key_as_string, docCount: suggestion.doc_count }); - return acc; - }, - [] - ); - return { suggestions, totalCardinality: suggestions.length }; // cardinality is only ever 0, 1, or 2 so safe to use length here - }, - }, - - /** - * the "IP" query / parser should be used when the options list is built on a field of type IP. - */ - ip: { - buildAggregation: ({ fieldName, searchString, sort, size }: OptionsListRequestBody) => { - let ipRangeQuery: IpRangeQuery = { - validSearch: true, - rangeQuery: [ - { - key: 'ipv6', - from: '::', - to: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', - }, - ], }; - if (searchString && searchString.length > 0) { - ipRangeQuery = getIpRangeQuery(searchString); - if (!ipRangeQuery.validSearch) { - // ideally should be prevented on the client side but, if somehow an invalid search gets through to the server, - // simply don't return an aggregation query for the ES search request - return undefined; - } + const ipRangeQuery = getIpRangeQuery(searchString ?? ''); + if (!ipRangeQuery.validSearch) { + return {}; } - return { suggestions: { ip_range: { @@ -176,14 +156,7 @@ const expensiveSuggestionAggSubtypes: { [key: string]: OptionsListSuggestionAggr keyed: true, }, aggs: { - filteredSuggestions: { - terms: { - size, - field: fieldName, - shard_size: 10, - order: getSortType(sort), - }, - }, + filteredSuggestions, unique_terms: { cardinality: { field: fieldName, @@ -193,18 +166,22 @@ const expensiveSuggestionAggSubtypes: { [key: string]: OptionsListSuggestionAggr }, }; }, - parse: (rawEsResult, request) => { - if (!Boolean(rawEsResult.aggregations?.suggestions)) { + parse: (rawEsResult, { searchString, sort, fieldSpec, size, searchTechnique }) => { + if ( + !searchString || + !isValidSearch({ searchString, fieldType: fieldSpec?.type, searchTechnique }) + ) { // if this is happens, that means there is an invalid search that snuck through to the server side code; // so, might as well early return with no suggestions return { suggestions: [], totalCardinality: 0 }; } + const buckets: EsBucket[] = []; getIpBuckets(rawEsResult, buckets, 'ipv4'); // modifies buckets array directly, i.e. "by reference" getIpBuckets(rawEsResult, buckets, 'ipv6'); const sortedSuggestions = - request.sort?.direction === 'asc' + sort?.direction === 'asc' ? buckets.sort( (bucketA: EsBucket, bucketB: EsBucket) => bucketA.doc_count - bucketB.doc_count ) @@ -213,7 +190,7 @@ const expensiveSuggestionAggSubtypes: { [key: string]: OptionsListSuggestionAggr ); const suggestions = sortedSuggestions - .slice(0, request.size) + .slice(0, size) .reduce((acc: OptionsListSuggestions, suggestion: EsBucket) => { acc.push({ value: suggestion.key, docCount: suggestion.doc_count }); return acc; diff --git a/src/plugins/controls/server/options_list/suggestion_queries/options_list_suggestion_queries.test.ts b/src/plugins/controls/server/options_list/suggestion_queries/options_list_suggestion_queries.test.ts new file mode 100644 index 0000000000000..46f0af574f775 --- /dev/null +++ b/src/plugins/controls/server/options_list/suggestion_queries/options_list_suggestion_queries.test.ts @@ -0,0 +1,94 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { FieldSpec } from '@kbn/data-views-plugin/common'; +import { OptionsListRequestBody } from '../../../common/options_list/types'; +import { getAllSuggestionsAggregationBuilder } from './options_list_all_suggestions'; +import { getExactMatchAggregationBuilder } from './options_list_exact_match'; +import { getSearchSuggestionsAggregationBuilder } from './options_list_search_suggestions'; +import { getSuggestionAggregationBuilder } from './options_list_suggestion_queries'; + +jest.mock('./options_list_all_suggestions', () => ({ + getAllSuggestionsAggregationBuilder: jest.fn(), +})); + +jest.mock('./options_list_exact_match', () => ({ + getExactMatchAggregationBuilder: jest.fn(), +})); + +jest.mock('./options_list_search_suggestions', () => ({ + getSearchSuggestionsAggregationBuilder: jest.fn(), +})); + +describe('options list suggestion queries', () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + + test('returns generic fetch all aggregation when no search string is provided', () => { + const optionsListRequestBodyMock: OptionsListRequestBody = { + size: 10, + fieldName: '@timestamp', + allowExpensiveQueries: true, + sort: { by: '_key', direction: 'desc' }, + fieldSpec: { type: 'date' } as unknown as FieldSpec, + }; + getSuggestionAggregationBuilder(optionsListRequestBodyMock); + expect(getAllSuggestionsAggregationBuilder).toBeCalled(); + expect(getExactMatchAggregationBuilder).not.toBeCalled(); + expect(getSearchSuggestionsAggregationBuilder).not.toBeCalled(); + }); + + test('returns generic exact match search query when search technique is `exact`', () => { + const optionsListRequestBodyMock: OptionsListRequestBody = { + size: 10, + fieldName: 'bytes', + allowExpensiveQueries: true, + searchTechnique: 'exact', + searchString: 'searchForMe', + sort: { by: '_key', direction: 'asc' }, + fieldSpec: { type: 'number' } as unknown as FieldSpec, + }; + getSuggestionAggregationBuilder(optionsListRequestBodyMock); + expect(getAllSuggestionsAggregationBuilder).not.toBeCalled(); + expect(getExactMatchAggregationBuilder).toBeCalled(); + expect(getSearchSuggestionsAggregationBuilder).not.toBeCalled(); + }); + + test('returns generic exact match search query when allowExpensiveQueries is `false`', () => { + const optionsListRequestBodyMock: OptionsListRequestBody = { + size: 10, + fieldName: 'bytes', + allowExpensiveQueries: false, + searchTechnique: 'prefix', + searchString: 'searchForMe', + sort: { by: '_key', direction: 'asc' }, + fieldSpec: { type: 'number' } as unknown as FieldSpec, + }; + getSuggestionAggregationBuilder(optionsListRequestBodyMock); + expect(getAllSuggestionsAggregationBuilder).not.toBeCalled(); + expect(getExactMatchAggregationBuilder).toBeCalled(); + expect(getSearchSuggestionsAggregationBuilder).not.toBeCalled(); + }); + + test('returns type-specific search query only when absolutely necessary', () => { + const optionsListRequestBodyMock: OptionsListRequestBody = { + size: 10, + fieldName: 'bytes', + allowExpensiveQueries: true, + searchTechnique: 'prefix', + searchString: 'searchForMe', + sort: { by: '_key', direction: 'asc' }, + fieldSpec: { type: 'keyword' } as unknown as FieldSpec, + }; + getSuggestionAggregationBuilder(optionsListRequestBodyMock); + expect(getAllSuggestionsAggregationBuilder).not.toBeCalled(); + expect(getExactMatchAggregationBuilder).not.toBeCalled(); + expect(getSearchSuggestionsAggregationBuilder).toBeCalled(); + }); +}); diff --git a/src/plugins/controls/server/options_list/suggestion_queries/options_list_suggestion_queries.ts b/src/plugins/controls/server/options_list/suggestion_queries/options_list_suggestion_queries.ts new file mode 100644 index 0000000000000..56e7820c8260e --- /dev/null +++ b/src/plugins/controls/server/options_list/suggestion_queries/options_list_suggestion_queries.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { OptionsListRequestBody } from '../../../common/options_list/types'; +import { getAllSuggestionsAggregationBuilder } from './options_list_all_suggestions'; +import { getExactMatchAggregationBuilder } from './options_list_exact_match'; +import { getSearchSuggestionsAggregationBuilder } from './options_list_search_suggestions'; + +/** + * Suggestion aggregations + */ +export const getSuggestionAggregationBuilder = (request: OptionsListRequestBody) => { + const { searchString, searchTechnique, allowExpensiveQueries } = request; + const hasSearchString = searchString && searchString.length > 0; + if (!hasSearchString) { + // the field type only matters when there is a search string; so, if no search string, + // return generic "fetch all" aggregation builder + return getAllSuggestionsAggregationBuilder(); + } else if (!allowExpensiveQueries || searchTechnique === 'exact') { + // if `allowExpensiveQueries` is false, only support exact match searching; also, field type + // once again does not matter when building an exact match aggregation + return getExactMatchAggregationBuilder(); + } else { + // at this point, the type of the field matters - so, fetch the type-specific search agg + return getSearchSuggestionsAggregationBuilder(request); + } +}; diff --git a/src/plugins/controls/server/options_list/options_list_suggestion_query_helpers.ts b/src/plugins/controls/server/options_list/suggestion_queries/options_list_suggestion_query_helpers.ts similarity index 92% rename from src/plugins/controls/server/options_list/options_list_suggestion_query_helpers.ts rename to src/plugins/controls/server/options_list/suggestion_queries/options_list_suggestion_query_helpers.ts index 437450cc8ecf1..5986420f21f91 100644 --- a/src/plugins/controls/server/options_list/options_list_suggestion_query_helpers.ts +++ b/src/plugins/controls/server/options_list/suggestion_queries/options_list_suggestion_query_helpers.ts @@ -8,11 +8,11 @@ import { get } from 'lodash'; -import { EsBucket } from './types'; import { - OPTIONS_LIST_DEFAULT_SORT, OptionsListSortingType, -} from '../../common/options_list/suggestions_sorting'; + OPTIONS_LIST_DEFAULT_SORT, +} from '../../../common/options_list/suggestions_sorting'; +import { EsBucket } from '../types'; export const getSortType = (sort?: OptionsListSortingType) => { return sort diff --git a/src/plugins/controls/tsconfig.json b/src/plugins/controls/tsconfig.json index a386f5146e21b..2567c8e655dfd 100644 --- a/src/plugins/controls/tsconfig.json +++ b/src/plugins/controls/tsconfig.json @@ -36,6 +36,8 @@ "@kbn/safer-lodash-set", "@kbn/ui-actions-plugin", "@kbn/core-mount-utils-browser", + "@kbn/react-kibana-mount", + "@kbn/react-kibana-context-theme", ], "exclude": [ "target/**/*", diff --git a/src/plugins/data/common/search/expressions/__snapshots__/kibana.test.ts.snap b/src/plugins/data/common/search/expressions/__snapshots__/kibana.test.ts.snap index 2400f7a1f67d6..7bcf782fbbbc2 100644 --- a/src/plugins/data/common/search/expressions/__snapshots__/kibana.test.ts.snap +++ b/src/plugins/data/common/search/expressions/__snapshots__/kibana.test.ts.snap @@ -14,6 +14,7 @@ Object { }, }, ], + "now": 0, "query": Array [ Object { "language": "lucene", diff --git a/src/plugins/data/common/search/expressions/kibana.test.ts b/src/plugins/data/common/search/expressions/kibana.test.ts index c82bc0293cefe..4992a345bd0d2 100644 --- a/src/plugins/data/common/search/expressions/kibana.test.ts +++ b/src/plugins/data/common/search/expressions/kibana.test.ts @@ -20,6 +20,7 @@ describe('interpreter/functions#kibana', () => { beforeEach(() => { input = { timeRange: { from: '0', to: '1' } }; search = { + now: 0, type: 'kibana_context', query: { language: 'lucene', query: 'geo.src:US' }, filters: [ diff --git a/src/plugins/data/common/search/expressions/kibana.ts b/src/plugins/data/common/search/expressions/kibana.ts index aa83662e0b8d4..ad8405a51418c 100644 --- a/src/plugins/data/common/search/expressions/kibana.ts +++ b/src/plugins/data/common/search/expressions/kibana.ts @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n'; import { ExecutionContext, ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; import { Adapters } from '@kbn/inspector-plugin/common'; -import { ExpressionValueSearchContext, ExecutionContextSearch } from './kibana_context_type'; +import { ExpressionValueSearchContext } from './kibana_context_type'; const toArray = (query: undefined | T | T[]): T[] => !query ? [] : Array.isArray(query) ? query : [query]; @@ -20,7 +20,7 @@ export type ExpressionFunctionKibana = ExpressionFunctionDefinition< ExpressionValueSearchContext | null, object, ExpressionValueSearchContext, - ExecutionContext + ExecutionContext >; export const kibana: ExpressionFunctionKibana = { @@ -41,6 +41,7 @@ export const kibana: ExpressionFunctionKibana = { // TODO: But it shouldn't be need. ...input, type: 'kibana_context', + now: getSearchContext().now ?? Date.now(), query: [...toArray(getSearchContext().query), ...toArray((input || {}).query)], filters: [...(getSearchContext().filters || []), ...((input || {}).filters || [])], timeRange: getSearchContext().timeRange || (input ? input.timeRange : undefined), diff --git a/src/plugins/data/common/search/expressions/kibana_context.ts b/src/plugins/data/common/search/expressions/kibana_context.ts index 9f9a7f274ab5b..613a8ff2fede5 100644 --- a/src/plugins/data/common/search/expressions/kibana_context.ts +++ b/src/plugins/data/common/search/expressions/kibana_context.ts @@ -8,13 +8,7 @@ import { ExpressionFunctionDefinition, ExecutionContext } from '@kbn/expressions-plugin/common'; import { Adapters } from '@kbn/inspector-plugin/common'; -import { - KibanaTimerangeOutput, - ExecutionContextSearch, - KibanaContext, - KibanaFilter, - KibanaQueryOutput, -} from '../..'; +import { KibanaTimerangeOutput, KibanaContext, KibanaFilter, KibanaQueryOutput } from '../..'; interface Arguments { q?: KibanaQueryOutput[] | null; @@ -28,5 +22,5 @@ export type ExpressionFunctionKibanaContext = ExpressionFunctionDefinition< KibanaContext | null, Arguments, Promise, - ExecutionContext + ExecutionContext >; diff --git a/src/plugins/data/common/search/expressions/kibana_context_type.ts b/src/plugins/data/common/search/expressions/kibana_context_type.ts index d6947d2d46ce3..979fc65e4ac08 100644 --- a/src/plugins/data/common/search/expressions/kibana_context_type.ts +++ b/src/plugins/data/common/search/expressions/kibana_context_type.ts @@ -5,19 +5,11 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ -import { Filter } from '@kbn/es-query'; +import { Filter, ExecutionContextSearch } from '@kbn/es-query'; import { ExpressionValueBoxed } from '@kbn/expressions-plugin/common'; -import { Query, TimeRange } from '../../query'; +import { Query } from '../../query'; import { DataViewField } from '../..'; -// eslint-disable-next-line @typescript-eslint/consistent-type-definitions -export type ExecutionContextSearch = { - filters?: Filter[]; - query?: Query | Query[]; - timeRange?: TimeRange; - disableWarningToasts?: boolean; -}; - export type ExpressionValueSearchContext = ExpressionValueBoxed< 'kibana_context', ExecutionContextSearch diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index a851e28f60829..de2504dec2d99 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -138,7 +138,6 @@ export type { OptionedValueProp, ParsedInterval, // expressions - ExecutionContextSearch, ExpressionFunctionKql, ExpressionFunctionLucene, ExpressionFunctionKibana, @@ -165,8 +164,6 @@ export type { SearchRequest, SearchSourceFields, SerializedSearchSourceFields, - // errors - IEsError, WaitUntilNextSessionCompletesOptions, } from './search'; @@ -178,8 +175,6 @@ export { noSearchSessionStorageCapabilityMessage, SEARCH_SESSIONS_MANAGEMENT_ID, waitUntilNextSessionCompletes$, - isEsError, - getSearchErrorOverrideDisplay, SearchSource, SearchSessionState, SortDirection, diff --git a/src/plugins/data/public/search/errors/types.ts b/src/plugins/data/public/search/errors/types.ts deleted file mode 100644 index 14477327e83bb..0000000000000 --- a/src/plugins/data/public/search/errors/types.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 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 { KibanaServerError } from '@kbn/kibana-utils-plugin/common'; -import { IEsErrorAttributes } from '../../../common'; - -export type IEsError = KibanaServerError; - -/** - * Checks if a given errors originated from Elasticsearch. - * Those params are assigned to the attributes property of an error. - * - * @param e - */ -export function isEsError(e: any): e is IEsError { - return !!e.attributes; -} diff --git a/src/plugins/data/public/search/index.ts b/src/plugins/data/public/search/index.ts index 0921b0bc7bb74..54ac47680a2b6 100644 --- a/src/plugins/data/public/search/index.ts +++ b/src/plugins/data/public/search/index.ts @@ -56,7 +56,5 @@ export { getEsPreference } from './es_search'; export type { SearchInterceptorDeps } from './search_interceptor'; export { SearchInterceptor } from './search_interceptor'; -export { getSearchErrorOverrideDisplay } from './search_interceptor/utils'; -export * from './errors'; export { SearchService } from './search_service'; diff --git a/src/plugins/data/public/search/search_interceptor/create_request_hash.ts b/src/plugins/data/public/search/search_interceptor/create_request_hash.ts new file mode 100644 index 0000000000000..99927d7f84d80 --- /dev/null +++ b/src/plugins/data/public/search/search_interceptor/create_request_hash.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { Sha256 } from '@kbn/crypto-browser'; +import stringify from 'json-stable-stringify'; + +export async function createRequestHash(keys: Record) { + return new Sha256().update(stringify(keys), 'utf8').digest('hex'); +} diff --git a/src/plugins/data/public/search/search_interceptor/search_interceptor.test.ts b/src/plugins/data/public/search/search_interceptor/search_interceptor.test.ts index 09db89ee74a0e..1c6bc41e33216 100644 --- a/src/plugins/data/public/search/search_interceptor/search_interceptor.test.ts +++ b/src/plugins/data/public/search/search_interceptor/search_interceptor.test.ts @@ -12,7 +12,7 @@ import { coreMock, themeServiceMock } from '@kbn/core/public/mocks'; import { IEsSearchRequest } from '../../../common/search'; import { SearchInterceptor } from './search_interceptor'; import { AbortError } from '@kbn/kibana-utils-plugin/public'; -import { SearchTimeoutError, PainlessError, TimeoutErrorMode, EsError } from '../errors'; +import { PainlessError, EsError, type IEsError } from '@kbn/search-errors'; import { ISessionService, SearchSessionState } from '..'; import { bfetchPluginMock } from '@kbn/bfetch-plugin/public/mocks'; import { BfetchPublicSetup } from '@kbn/bfetch-plugin/public'; @@ -22,12 +22,12 @@ import * as resourceNotFoundException from '../../../common/search/test_data/res import { BehaviorSubject } from 'rxjs'; import { dataPluginMock } from '../../mocks'; import { UI_SETTINGS } from '../../../common'; -import type { IEsError } from '../errors'; import type { SearchServiceStartDependencies } from '../search_service'; import type { Start as InspectorStart } from '@kbn/inspector-plugin/public'; +import { SearchTimeoutError, TimeoutErrorMode } from './timeout_error'; -jest.mock('./utils', () => { - const originalModule = jest.requireActual('./utils'); +jest.mock('./create_request_hash', () => { + const originalModule = jest.requireActual('./create_request_hash'); return { ...originalModule, createRequestHash: jest.fn().mockImplementation((input) => { @@ -36,11 +36,11 @@ jest.mock('./utils', () => { }; }); -jest.mock('../errors/search_session_incomplete_warning', () => ({ +jest.mock('./search_session_incomplete_warning', () => ({ SearchSessionIncompleteWarning: jest.fn(), })); -import { SearchSessionIncompleteWarning } from '../errors/search_session_incomplete_warning'; +import { SearchSessionIncompleteWarning } from './search_session_incomplete_warning'; import { getMockSearchConfig } from '../../../config.mock'; let searchInterceptor: SearchInterceptor; diff --git a/src/plugins/data/public/search/search_interceptor/search_interceptor.ts b/src/plugins/data/public/search/search_interceptor/search_interceptor.ts index 68dca8f4d0304..8bae4b3fc7c12 100644 --- a/src/plugins/data/public/search/search_interceptor/search_interceptor.ts +++ b/src/plugins/data/public/search/search_interceptor/search_interceptor.ts @@ -34,7 +34,6 @@ import { estypes } from '@elastic/elasticsearch'; import { i18n } from '@kbn/i18n'; import { PublicMethodsOf } from '@kbn/utility-types'; import type { HttpSetup, IHttpFetchError } from '@kbn/core-http-browser'; -import { BfetchRequestError } from '@kbn/bfetch-plugin/public'; import { type Start as InspectorStart, RequestAdapter } from '@kbn/inspector-plugin/public'; import { @@ -50,6 +49,14 @@ import { import { BatchedFunc, BfetchPublicSetup, DISABLE_BFETCH } from '@kbn/bfetch-plugin/public'; import { toMountPoint } from '@kbn/kibana-react-plugin/public'; import { AbortError, KibanaServerError } from '@kbn/kibana-utils-plugin/public'; +import { BfetchRequestError } from '@kbn/bfetch-error'; +import { + EsError, + isEsError, + isPainlessError, + PainlessError, + renderSearchError, +} from '@kbn/search-errors'; import { ENHANCED_ES_SEARCH_STRATEGY, IAsyncSearchOptions, @@ -63,21 +70,14 @@ import { type SanitizedConnectionRequestParams, } from '../../../common'; import { SearchUsageCollector } from '../collectors'; -import { - EsError, - isEsError, - isPainlessError, - PainlessError, - SearchTimeoutError, - TimeoutErrorMode, - SearchSessionIncompleteWarning, -} from '../errors'; +import { SearchTimeoutError, TimeoutErrorMode } from './timeout_error'; +import { SearchSessionIncompleteWarning } from './search_session_incomplete_warning'; import { ISessionService, SearchSessionState } from '../session'; import { SearchResponseCache } from './search_response_cache'; -import { createRequestHash, getSearchErrorOverrideDisplay } from './utils'; import { SearchAbortController } from './search_abort_controller'; import { SearchConfigSchema } from '../../../config'; import type { SearchServiceStartDependencies } from '../search_service'; +import { createRequestHash } from './create_request_hash'; export interface SearchInterceptorDeps { bfetch: BfetchPublicSetup; @@ -585,15 +585,15 @@ export class SearchInterceptor { return; } - const overrideDisplay = getSearchErrorOverrideDisplay({ + const searchErrorDisplay = renderSearchError({ error: e, application: this.application, }); - if (overrideDisplay) { + if (searchErrorDisplay) { this.deps.toasts.addDanger({ - title: overrideDisplay.title, - text: toMountPoint(overrideDisplay.body, { theme$: this.deps.theme.theme$ }), + title: searchErrorDisplay.title, + text: toMountPoint(searchErrorDisplay.body, { theme$: this.deps.theme.theme$ }), }); } else { this.deps.toasts.addError(e, { diff --git a/src/plugins/data/public/search/errors/search_session_incomplete_warning.tsx b/src/plugins/data/public/search/search_interceptor/search_session_incomplete_warning.tsx similarity index 100% rename from src/plugins/data/public/search/errors/search_session_incomplete_warning.tsx rename to src/plugins/data/public/search/search_interceptor/search_session_incomplete_warning.tsx diff --git a/src/plugins/data/public/search/errors/timeout_error.test.tsx b/src/plugins/data/public/search/search_interceptor/timeout_error.test.tsx similarity index 100% rename from src/plugins/data/public/search/errors/timeout_error.test.tsx rename to src/plugins/data/public/search/search_interceptor/timeout_error.test.tsx diff --git a/src/plugins/data/public/search/errors/timeout_error.tsx b/src/plugins/data/public/search/search_interceptor/timeout_error.tsx similarity index 100% rename from src/plugins/data/public/search/errors/timeout_error.tsx rename to src/plugins/data/public/search/search_interceptor/timeout_error.tsx diff --git a/src/plugins/data/public/utils/table_inspector_view/components/__snapshots__/data_view.test.tsx.snap b/src/plugins/data/public/utils/table_inspector_view/components/__snapshots__/data_view.test.tsx.snap index 06a402fd47567..8bd049df006fc 100644 --- a/src/plugins/data/public/utils/table_inspector_view/components/__snapshots__/data_view.test.tsx.snap +++ b/src/plugins/data/public/utils/table_inspector_view/components/__snapshots__/data_view.test.tsx.snap @@ -176,27 +176,23 @@ Array [ class="euiFlexItem emotion-euiFlexItem-growZero" >
-
- -
+ color="inherit" + data-euiicon-type="arrowDown" + /> + +
, @@ -224,30 +220,26 @@ Array [ class="euiTableSortMobile" >
-
- -
+ + +
@@ -339,31 +331,27 @@ Array [ class="euiFlexItem emotion-euiFlexItem-growZero" >
-
- -
+ + +
-
- -
+ + +
@@ -515,27 +499,23 @@ Array [ class="euiFlexItem emotion-euiFlexItem-growZero" >
-
- -
+ color="inherit" + data-euiicon-type="arrowDown" + /> + +
, @@ -563,30 +543,26 @@ Array [ class="euiTableSortMobile" >
-
- -
+ + +
@@ -679,31 +655,27 @@ Array [ class="euiFlexItem emotion-euiFlexItem-growZero" >
-
- -
+ + +
{ 'Matching sources' ); - findTestSubject(component, 'allIndices').simulate('change', { - target: { - value: true, - }, - }); + findTestSubject(component, 'allIndices').simulate('click'); await component.update(); @@ -109,11 +105,7 @@ describe('DataViewEditor PreviewPanel', () => { expect(component.find('.euiButtonGroupButton-isSelected').first().text()).toBe('All sources'); - findTestSubject(component, 'onlyMatchingIndices').simulate('change', { - target: { - value: true, - }, - }); + findTestSubject(component, 'onlyMatchingIndices').simulate('click'); await component.update(); diff --git a/src/plugins/data_views/docs/openapi/bundled.json b/src/plugins/data_views/docs/openapi/bundled.json index e4f7e9856e2bd..52912da70f307 100644 --- a/src/plugins/data_views/docs/openapi/bundled.json +++ b/src/plugins/data_views/docs/openapi/bundled.json @@ -198,6 +198,9 @@ "data views" ], "parameters": [ + { + "$ref": "#/components/parameters/kbn_xsrf" + }, { "$ref": "#/components/parameters/view_id" } @@ -449,7 +452,10 @@ "type": "object" }, "fields": { - "type": "array" + "type": "array", + "items": { + "type": "object" + } } } }, @@ -2687,7 +2693,18 @@ }, "sourcefilters": { "type": "array", - "description": "The array of field names you want to filter out in Discover." + "description": "The array of field names you want to filter out in Discover.", + "items": { + "type": "object", + "required": [ + "value" + ], + "properties": { + "value": { + "type": "string" + } + } + } }, "timefieldname": { "type": "string", diff --git a/src/plugins/data_views/docs/openapi/bundled.yaml b/src/plugins/data_views/docs/openapi/bundled.yaml index 65ffd986ade7d..a0afe1e93a8ef 100644 --- a/src/plugins/data_views/docs/openapi/bundled.yaml +++ b/src/plugins/data_views/docs/openapi/bundled.yaml @@ -125,6 +125,7 @@ paths: tags: - data views parameters: + - $ref: '#/components/parameters/kbn_xsrf' - $ref: '#/components/parameters/view_id' responses: '204': @@ -290,6 +291,8 @@ paths: type: object fields: type: array + items: + type: object examples: createRuntimeFieldResponse: $ref: '#/components/examples/create_runtime_field_response' @@ -2017,6 +2020,13 @@ components: sourcefilters: type: array description: The array of field names you want to filter out in Discover. + items: + type: object + required: + - value + properties: + value: + type: string timefieldname: type: string description: The timestamp field name, which you use for time-based data views. diff --git a/src/plugins/data_views/docs/openapi/components/schemas/sourcefilters.yaml b/src/plugins/data_views/docs/openapi/components/schemas/sourcefilters.yaml index 4ba0980e43730..b2dda2e2e8579 100644 --- a/src/plugins/data_views/docs/openapi/components/schemas/sourcefilters.yaml +++ b/src/plugins/data_views/docs/openapi/components/schemas/sourcefilters.yaml @@ -1,2 +1,9 @@ type: array -description: The array of field names you want to filter out in Discover. \ No newline at end of file +description: The array of field names you want to filter out in Discover. +items: + type: object + required: + - value + properties: + value: + type: string 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/api@data_views@data_view@{viewid}.yaml index 73d206a6991f3..e239978f39f87 100644 --- a/src/plugins/data_views/docs/openapi/paths/api@data_views@data_view@{viewid}.yaml +++ b/src/plugins/data_views/docs/openapi/paths/api@data_views@data_view@{viewid}.yaml @@ -33,6 +33,7 @@ delete: tags: - data views parameters: + - $ref: '../components/headers/kbn_xsrf.yaml' - $ref: '../components/parameters/view_id.yaml' responses: '204': 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/api@data_views@data_view@{viewid}@runtime_field.yaml index bf8613992f411..04f836cae94fe 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/api@data_views@data_view@{viewid}@runtime_field.yaml @@ -79,6 +79,8 @@ put: type: object fields: type: array + items: + type: object examples: createRuntimeFieldResponse: $ref: '../components/examples/create_runtime_field_response.yaml' diff --git a/src/plugins/discover/common/locator.test.ts b/src/plugins/discover/common/locator.test.ts index 80bc4ceebed2b..93da54ad365e9 100644 --- a/src/plugins/discover/common/locator.test.ts +++ b/src/plugins/discover/common/locator.test.ts @@ -218,17 +218,22 @@ describe('Discover url generator', () => { expect(path).toContain('__test__'); }); - test('can specify columns, interval, sort and savedQuery', async () => { + test('can specify columns, grid, interval, sort and savedQuery', async () => { const { locator } = await setup(); const { path } = await locator.getLocation({ columns: ['_source'], + grid: { + columns: { + _source: { width: 150 }, + }, + }, interval: 'auto', sort: [['timestamp, asc']] as string[][] & SerializableRecord, savedQuery: '__savedQueryId__', }); expect(path).toMatchInlineSnapshot( - `"#/?_a=(columns:!(_source),interval:auto,savedQuery:__savedQueryId__,sort:!(!('timestamp,%20asc')))"` + `"#/?_a=(columns:!(_source),grid:(columns:(_source:(width:150))),interval:auto,savedQuery:__savedQueryId__,sort:!(!('timestamp,%20asc')))"` ); }); diff --git a/src/plugins/discover/common/locator.ts b/src/plugins/discover/common/locator.ts index 70e60f55b5fb1..9be9947e743dd 100644 --- a/src/plugins/discover/common/locator.ts +++ b/src/plugins/discover/common/locator.ts @@ -10,6 +10,7 @@ import type { SerializableRecord } from '@kbn/utility-types'; import type { Filter, TimeRange, Query, AggregateQuery } from '@kbn/es-query'; import type { GlobalQueryStateFromUrl, RefreshInterval } from '@kbn/data-plugin/public'; import type { LocatorDefinition, LocatorPublic } from '@kbn/share-plugin/public'; +import type { DiscoverGridSettings } from '@kbn/saved-search-plugin/common'; import { DataViewSpec } from '@kbn/data-views-plugin/common'; import { setStateToKbnUrl } from '@kbn/kibana-utils-plugin/common'; import { VIEW_MODE } from './constants'; @@ -70,6 +71,11 @@ export interface DiscoverAppLocatorParams extends SerializableRecord { */ columns?: string[]; + /** + * Data Grid related state + */ + grid?: DiscoverGridSettings; + /** * Used interval of the histogram */ @@ -139,6 +145,7 @@ export class DiscoverAppLocatorDefinition implements LocatorDefinition { }); }; -const props = { +const customizationContext: DiscoverCustomizationContext = { + displayMode: 'standalone', + showLogExplorerTabs: false, +}; + +const props: DiscoverRoutesProps = { isDev: false, customizationCallbacks: [], + customizationContext, }; describe('DiscoverRoutes', () => { @@ -147,12 +159,17 @@ describe('CustomDiscoverRoutes', () => { it('should show DiscoverRoutes for a valid profile', () => { mockProfile = 'test'; const component = shallow( - + ); expect(component.find(DiscoverRoutes).getElement()).toMatchObject( ); @@ -161,7 +178,11 @@ describe('CustomDiscoverRoutes', () => { it('should show NotFoundRoute for an invalid profile', () => { mockProfile = 'invalid'; const component = shallow( - + ); expect(component.find(NotFoundRoute).getElement()).toMatchObject(); }); @@ -178,6 +199,7 @@ describe('DiscoverRouter', () => { services={mockDiscoverServices} history={history} profileRegistry={profileRegistry} + customizationContext={customizationContext} isDev={props.isDev} /> ); @@ -186,13 +208,21 @@ describe('DiscoverRouter', () => { it('should show DiscoverRoutes component for / route', () => { expect(pathMap['/']).toMatchObject( - + ); }); it(`should show CustomDiscoverRoutes component for ${profilePath} route`, () => { expect(pathMap[profilePath]).toMatchObject( - + ); }); }); diff --git a/src/plugins/discover/public/application/discover_router.tsx b/src/plugins/discover/public/application/discover_router.tsx index 73c99d50b6fa6..5e146a768baa1 100644 --- a/src/plugins/discover/public/application/discover_router.tsx +++ b/src/plugins/discover/public/application/discover_router.tsx @@ -21,10 +21,12 @@ import { ViewAlertRoute } from './view_alert'; import type { CustomizationCallback } from '../customizations'; import type { DiscoverProfileRegistry } from '../customizations/profile_registry'; import { addProfile } from '../../common/customizations'; +import type { DiscoverCustomizationContext } from './types'; -interface DiscoverRoutesProps { +export interface DiscoverRoutesProps { prefix?: string; customizationCallbacks: CustomizationCallback[]; + customizationContext: DiscoverCustomizationContext; isDev: boolean; } @@ -66,6 +68,7 @@ export const DiscoverRoutes = ({ prefix, ...mainRouteProps }: DiscoverRoutesProp interface CustomDiscoverRoutesProps { profileRegistry: DiscoverProfileRegistry; + customizationContext: DiscoverCustomizationContext; isDev: boolean; } @@ -92,6 +95,7 @@ export const CustomDiscoverRoutes = ({ profileRegistry, ...props }: CustomDiscov export interface DiscoverRouterProps { services: DiscoverServices; profileRegistry: DiscoverProfileRegistry; + customizationContext: DiscoverCustomizationContext; history: History; isDev: boolean; } diff --git a/src/plugins/discover/public/application/index.tsx b/src/plugins/discover/public/application/index.tsx index a9571e08e21ad..2e26b32973210 100644 --- a/src/plugins/discover/public/application/index.tsx +++ b/src/plugins/discover/public/application/index.tsx @@ -12,15 +12,23 @@ import { toMountPoint } from '@kbn/react-kibana-mount'; import { DiscoverRouter } from './discover_router'; import { DiscoverServices } from '../build_services'; import type { DiscoverProfileRegistry } from '../customizations/profile_registry'; +import type { DiscoverCustomizationContext } from './types'; export interface RenderAppProps { element: HTMLElement; services: DiscoverServices; profileRegistry: DiscoverProfileRegistry; + customizationContext: DiscoverCustomizationContext; isDev: boolean; } -export const renderApp = ({ element, services, profileRegistry, isDev }: RenderAppProps) => { +export const renderApp = ({ + element, + services, + profileRegistry, + customizationContext, + isDev, +}: RenderAppProps) => { const { history: getHistory, capabilities, chrome, data, core } = services; const history = getHistory(); @@ -39,6 +47,7 @@ export const renderApp = ({ element, services, profileRegistry, isDev }: RenderA , diff --git a/src/plugins/discover/public/application/main/components/field_stats_table/field_stats_table.tsx b/src/plugins/discover/public/application/main/components/field_stats_table/field_stats_table.tsx index 0bb6947dc7674..bba3e23d3e70f 100644 --- a/src/plugins/discover/public/application/main/components/field_stats_table/field_stats_table.tsx +++ b/src/plugins/discover/public/application/main/components/field_stats_table/field_stats_table.tsx @@ -20,6 +20,8 @@ import { import type { SavedSearch } from '@kbn/saved-search-plugin/public'; import { EuiFlexItem } from '@elastic/eui'; import { css } from '@emotion/react'; +import useObservable from 'react-use/lib/useObservable'; +import { of } from 'rxjs'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; import { FIELD_STATISTICS_LOADED } from './constants'; import type { DiscoverStateContainer } from '../../services/discover_state'; @@ -120,7 +122,9 @@ export const FieldStatisticsTable = (props: FieldStatisticsTableProps) => { trackUiMetric, searchSessionId, } = props; - const totalHits$ = stateContainer?.dataState.data$.totalHits$; + const totalHits = useObservable(stateContainer?.dataState.data$.totalHits$ ?? of(undefined)); + const totalDocuments = useMemo(() => totalHits?.result, [totalHits]); + const services = useDiscoverServices(); const [embeddable, setEmbeddable] = useState< | ErrorEmbeddable @@ -173,7 +177,7 @@ export const FieldStatisticsTable = (props: FieldStatisticsTableProps) => { onAddFilter, sessionId: searchSessionId, fieldsToFetch: stateContainer?.dataState.data$.availableFields$?.getValue().fields, - totalDocuments: totalHits$ ? totalHits$.getValue()?.result : undefined, + totalDocuments, samplingOption: { mode: 'normal_sampling', shardSize: 5000, @@ -191,7 +195,7 @@ export const FieldStatisticsTable = (props: FieldStatisticsTableProps) => { filters, onAddFilter, searchSessionId, - totalHits$, + totalDocuments, stateContainer, ]); diff --git a/src/plugins/discover/public/application/main/components/layout/__stories__/get_layout_props.ts b/src/plugins/discover/public/application/main/components/layout/__stories__/get_layout_props.ts index 5a5c2bb039683..54d2dc5da0084 100644 --- a/src/plugins/discover/public/application/main/components/layout/__stories__/get_layout_props.ts +++ b/src/plugins/discover/public/application/main/components/layout/__stories__/get_layout_props.ts @@ -15,7 +15,7 @@ import { createHashHistory } from 'history'; import { SavedSearch } from '@kbn/saved-search-plugin/public'; import { buildDataTableRecordList } from '@kbn/discover-utils'; import { esHitsMock } from '@kbn/discover-utils/src/__mocks__'; -import { FetchStatus } from '../../../../types'; +import { DiscoverCustomizationContext, FetchStatus } from '../../../../types'; import { AvailableFields$, DataDocuments$, @@ -124,11 +124,17 @@ function getSavedSearch(dataView: DataView) { } as unknown as SavedSearch; } +const customizationContext: DiscoverCustomizationContext = { + displayMode: 'standalone', + showLogExplorerTabs: false, +}; + export function getDocumentsLayoutProps(dataView: DataView) { const stateContainer = getDiscoverStateContainer({ history: createHashHistory(), savedSearch: getSavedSearch(dataView), services, + customizationContext, }); stateContainer.appState.set({ columns: ['name', 'message', 'bytes'], @@ -153,6 +159,7 @@ export const getPlainRecordLayoutProps = (dataView: DataView) => { history: createHashHistory(), savedSearch: getSavedSearch(dataView), services, + customizationContext, }); stateContainer.appState.set({ columns: ['name', 'message', 'bytes'], diff --git a/src/plugins/discover/public/application/main/components/layout/discover_layout.scss b/src/plugins/discover/public/application/main/components/layout/discover_layout.scss index 55972b4f7f629..352a1f803bc35 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_layout.scss +++ b/src/plugins/discover/public/application/main/components/layout/discover_layout.scss @@ -11,6 +11,10 @@ discover-app { .dscPage { @include euiBreakpoint('m', 'l', 'xl') { @include kibanaFullBodyHeight(); + + &.dscPage--serverless { + @include kibanaFullBodyHeight($euiSize * 3); + } } flex-direction: column; diff --git a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx index b2d3a4b5058ee..622ef340ffa98 100644 --- a/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx +++ b/src/plugins/discover/public/application/main/components/layout/discover_layout.tsx @@ -37,7 +37,6 @@ import { DiscoverStateContainer } from '../../services/discover_state'; import { VIEW_MODE } from '../../../../../common/constants'; import { useInternalStateSelector } from '../../services/discover_internal_state_container'; import { useAppStateSelector } from '../../services/discover_app_state_container'; -import { useInspector } from '../../hooks/use_inspector'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; import { DiscoverNoResults } from '../no_results'; import { LoadingSpinner } from '../loading_spinner/loading_spinner'; @@ -73,8 +72,8 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) { filterManager, history, spaces, - inspector, docLinks, + serverless, } = useDiscoverServices(); const { euiTheme } = useEuiTheme(); const pageBackgroundColor = useEuiBackgroundColor('plain'); @@ -118,11 +117,6 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) { [dataState.fetchStatus, dataState.foundDocuments] ); - const onOpenInspector = useInspector({ - inspector, - stateContainer, - }); - const { columns: currentColumns, onAddColumn, @@ -243,7 +237,7 @@ export function DiscoverLayout({ stateContainer }: DiscoverLayoutProps) { return ( ({ ...jest.requireActual('@kbn/kibana-react-plugin/public'), - useKibana: () => ({ - services: mockDiscoverService, - }), + useKibana: jest.fn(), })); const MockCustomSearchBar: typeof mockDiscoverService.navigation.ui.AggregateQueryTopNavMenu = @@ -77,15 +75,14 @@ function getProps( return { stateContainer, - query: {} as Query, savedQuery: '', updateQuery: jest.fn(), - onOpenInspector: jest.fn(), onFieldEdited: jest.fn(), - isPlainRecord: false, }; } +const mockUseKibana = useKibana as jest.Mock; + describe('Discover topnav component', () => { beforeEach(() => { mockTopNavCustomization.defaultMenu = undefined; @@ -107,6 +104,10 @@ describe('Discover topnav component', () => { throw new Error(`Unknown customization id: ${id}`); } }); + + mockUseKibana.mockReturnValue({ + services: mockDiscoverService, + }); }); test('generated config of TopNavMenu config is correct when discover save permissions are assigned', () => { @@ -280,4 +281,38 @@ describe('Discover topnav component', () => { expect(topNav.prop('dataViewPickerComponentProps')).toBeUndefined(); }); }); + + describe('serverless', () => { + it('should render top nav when serverless plugin is not defined', () => { + const props = getProps(); + const component = mountWithIntl( + + + + ); + const searchBar = component.find(mockDiscoverService.navigation.ui.AggregateQueryTopNavMenu); + expect(searchBar.prop('badges')).toBeDefined(); + expect(searchBar.prop('config')).toBeDefined(); + expect(searchBar.prop('setMenuMountPoint')).toBeDefined(); + }); + + it('should not render top nav when serverless plugin is defined', () => { + mockUseKibana.mockReturnValue({ + services: { + ...mockDiscoverService, + serverless: true, + }, + }); + const props = getProps(); + const component = mountWithIntl( + + + + ); + const searchBar = component.find(mockDiscoverService.navigation.ui.AggregateQueryTopNavMenu); + expect(searchBar.prop('badges')).toBeUndefined(); + expect(searchBar.prop('config')).toBeUndefined(); + expect(searchBar.prop('setMenuMountPoint')).toBeUndefined(); + }); + }); }); diff --git a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx index 51484519ee7ac..3e8d58297783c 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav.tsx @@ -5,8 +5,8 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ + import React, { useCallback, useEffect, useMemo, useRef } from 'react'; -import useObservable from 'react-use/lib/useObservable'; import type { Query, TimeRange, AggregateQuery } from '@kbn/es-query'; import { DataViewType, type DataView } from '@kbn/data-views-plugin/public'; import type { DataViewPickerProps } from '@kbn/unified-search-plugin/public'; @@ -14,51 +14,48 @@ import { ENABLE_ESQL } from '@kbn/discover-utils'; import { useSavedSearchInitial } from '../../services/discover_state_provider'; import { useInternalStateSelector } from '../../services/discover_internal_state_container'; import { useDiscoverServices } from '../../../../hooks/use_discover_services'; -import { getTopNavLinks } from './get_top_nav_links'; -import { getTopNavBadges } from './get_top_nav_badges'; import { getHeaderActionMenuMounter } from '../../../../kibana_services'; import { DiscoverStateContainer } from '../../services/discover_state'; import { onSaveSearch } from './on_save_search'; import { useDiscoverCustomization } from '../../../../customizations'; import { addLog } from '../../../../utils/add_log'; +import { useAppStateSelector } from '../../services/discover_app_state_container'; +import { isTextBasedQuery } from '../../utils/is_text_based_query'; +import { useDiscoverTopNav } from './use_discover_topnav'; export interface DiscoverTopNavProps { - onOpenInspector: () => void; - query?: Query | AggregateQuery; savedQuery?: string; updateQuery: ( payload: { dateRange: TimeRange; query?: Query | AggregateQuery }, isUpdate?: boolean ) => void; stateContainer: DiscoverStateContainer; - isPlainRecord: boolean; textBasedLanguageModeErrors?: Error; textBasedLanguageModeWarning?: string; onFieldEdited: () => Promise; } export const DiscoverTopNav = ({ - onOpenInspector, - query, savedQuery, stateContainer, updateQuery, - isPlainRecord, textBasedLanguageModeErrors, textBasedLanguageModeWarning, onFieldEdited, }: DiscoverTopNavProps) => { + const query = useAppStateSelector((state) => state.query); const adHocDataViews = useInternalStateSelector((state) => state.adHocDataViews); const dataView = useInternalStateSelector((state) => state.dataView!); const savedDataViews = useInternalStateSelector((state) => state.savedDataViews); const savedSearch = useSavedSearchInitial(); + const isTextBased = useMemo(() => isTextBasedQuery(query), [query]); const showDatePicker = useMemo(() => { // always show the timepicker for text based languages return ( - isPlainRecord || - (!isPlainRecord && dataView.isTimeBased() && dataView.type !== DataViewType.ROLLUP) + isTextBased || + (!isTextBased && dataView.isTimeBased() && dataView.type !== DataViewType.ROLLUP) ); - }, [dataView, isPlainRecord]); + }, [dataView, isTextBased]); const services = useDiscoverServices(); const { dataViewEditor, navigation, dataViewFieldEditor, data, uiSettings, dataViews } = services; @@ -115,44 +112,6 @@ export const DiscoverTopNav = ({ }); }, [dataViewEditor, stateContainer]); - const topNavCustomization = useDiscoverCustomization('top_nav'); - - const hasSavedSearchChanges = useObservable(stateContainer.savedSearchState.getHasChanged$()); - const hasUnsavedChanges = - hasSavedSearchChanges && Boolean(stateContainer.savedSearchState.getId()); - const topNavBadges = useMemo( - () => - getTopNavBadges({ - stateContainer, - services, - hasUnsavedChanges, - topNavCustomization, - }), - [stateContainer, services, hasUnsavedChanges, topNavCustomization] - ); - - const topNavMenu = useMemo( - () => - getTopNavLinks({ - dataView, - services, - state: stateContainer, - onOpenInspector, - isPlainRecord, - adHocDataViews, - topNavCustomization, - }), - [ - adHocDataViews, - dataView, - isPlainRecord, - onOpenInspector, - services, - stateContainer, - topNavCustomization, - ] - ); - const onEditDataView = async (editedDataView: DataView) => { if (editedDataView.isPersisted()) { // Clear the current data view from the cache and create a new instance @@ -229,16 +188,21 @@ export const DiscoverTopNav = ({ [services, stateContainer] ); + const { topNavBadges, topNavMenu } = useDiscoverTopNav({ stateContainer }); + const topNavProps = !services.serverless && { + badges: topNavBadges, + config: topNavMenu, + setMenuMountPoint, + }; + return ( ({ + ...jest.requireActual('@kbn/kibana-react-plugin/public'), + useKibana: jest.fn(), +})); + +function getProps({ hideNavMenuItems }: { hideNavMenuItems?: boolean } = {}) { + const stateContainer = getDiscoverStateMock({ isTimeBased: true }); + stateContainer.internalState.transitions.setDataView(dataViewMock); + + return { + stateContainer, + hideNavMenuItems, + }; +} + +const mockUseKibana = useKibana as jest.Mock; + +describe('DiscoverTopNavServerless', () => { + beforeEach(() => { + jest.clearAllMocks(); + mockUseKibana.mockReturnValue({ + services: mockDiscoverService, + }); + }); + + it('should not render when serverless plugin is not defined', async () => { + const props = getProps(); + render( + + + + ); + const topNav = screen.queryByTestId('discoverTopNavServerless'); + expect(topNav).toBeNull(); + }); + + it('should render when serverless plugin is defined and displayMode is "standalone"', async () => { + mockUseKibana.mockReturnValue({ + services: { + ...mockDiscoverService, + serverless: true, + }, + }); + const props = getProps(); + render( + + + + ); + const topNav = screen.queryByTestId('discoverTopNavServerless'); + expect(topNav).not.toBeNull(); + }); + + it('should not render when serverless plugin is defined and displayMode is not "standalone"', async () => { + mockUseKibana.mockReturnValue({ + services: { + ...mockDiscoverService, + serverless: true, + }, + }); + const props = getProps(); + props.stateContainer.customizationContext.displayMode = 'embedded'; + render( + + + + ); + const topNav = screen.queryByTestId('discoverTopNavServerless'); + expect(topNav).toBeNull(); + }); + + describe('nav menu items', () => { + it('should show nav menu items when hideNavMenuItems is false', async () => { + mockUseKibana.mockReturnValue({ + services: { + ...mockDiscoverService, + serverless: true, + }, + }); + const props = getProps(); + render( + + + + ); + const topNav = screen.queryByTestId('discoverTopNavServerless'); + expect(topNav).not.toBeNull(); + await waitFor(() => { + const topNavMenuItems = screen.queryByTestId('topNavMenuItems'); + expect(topNavMenuItems).not.toBeNull(); + }); + }); + + it('should hide nav menu items when hideNavMenuItems is true', async () => { + mockUseKibana.mockReturnValue({ + services: { + ...mockDiscoverService, + serverless: true, + }, + }); + const props = getProps({ hideNavMenuItems: true }); + render( + + + + ); + const topNav = screen.queryByTestId('discoverTopNavServerless'); + expect(topNav).not.toBeNull(); + await waitFor(() => { + const topNavMenuItems = screen.queryByTestId('topNavMenuItems'); + expect(topNavMenuItems).toBeNull(); + }); + }); + }); + + describe('LogExplorerTabs', () => { + it('should render when showLogExplorerTabs is true', async () => { + mockUseKibana.mockReturnValue({ + services: { + ...mockDiscoverService, + serverless: true, + }, + }); + const props = getProps(); + props.stateContainer.customizationContext.showLogExplorerTabs = true; + render( + + + + ); + const topNav = screen.queryByTestId('discoverTopNavServerless'); + expect(topNav).not.toBeNull(); + await waitFor(() => { + const logExplorerTabs = screen.queryByTestId('logExplorerTabs'); + expect(logExplorerTabs).not.toBeNull(); + }); + }); + + it('should not render when showLogExplorerTabs is false', async () => { + mockUseKibana.mockReturnValue({ + services: { + ...mockDiscoverService, + serverless: true, + }, + }); + const props = getProps(); + render( + + + + ); + const topNav = screen.queryByTestId('discoverTopNavServerless'); + expect(topNav).not.toBeNull(); + await waitFor(() => { + const logExplorerTabs = screen.queryByTestId('logExplorerTabs'); + expect(logExplorerTabs).toBeNull(); + }); + }); + }); +}); diff --git a/src/plugins/discover/public/application/main/components/top_nav/discover_topnav_serverless.tsx b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav_serverless.tsx new file mode 100644 index 0000000000000..800ceeae76013 --- /dev/null +++ b/src/plugins/discover/public/application/main/components/top_nav/discover_topnav_serverless.tsx @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { EuiHeader, EuiHeaderSection, EuiHeaderSectionItem } from '@elastic/eui'; +import { TopNavMenuBadges, TopNavMenuItems } from '@kbn/navigation-plugin/public'; +import { LogExplorerTabs } from '../../../../components/log_explorer_tabs'; +import { useDiscoverServices } from '../../../../hooks/use_discover_services'; +import { useDiscoverTopNav } from './use_discover_topnav'; +import type { DiscoverStateContainer } from '../../services/discover_state'; + +export const DiscoverTopNavServerless = ({ + stateContainer, + hideNavMenuItems, +}: { + stateContainer: DiscoverStateContainer; + hideNavMenuItems?: boolean; +}) => { + const { customizationContext } = stateContainer; + const services = useDiscoverServices(); + const { topNavBadges, topNavMenu } = useDiscoverTopNav({ stateContainer }); + + if (!services.serverless || customizationContext.displayMode !== 'standalone') { + return null; + } + + return ( + + {customizationContext.showLogExplorerTabs && ( + + + + + + )} + {!hideNavMenuItems && ( + + + + + + + + + )} + + ); +}; diff --git a/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.test.ts b/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.test.ts index 4e3e7068f883d..e83f517242e37 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.test.ts +++ b/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.test.ts @@ -27,7 +27,7 @@ test('getTopNavLinks result', () => { onOpenInspector: jest.fn(), services, state, - isPlainRecord: false, + isTextBased: false, adHocDataViews: [], topNavCustomization: undefined, }); @@ -80,7 +80,7 @@ test('getTopNavLinks result for ES|QL mode', () => { onOpenInspector: jest.fn(), services, state, - isPlainRecord: true, + isTextBased: true, adHocDataViews: [], topNavCustomization: undefined, }); diff --git a/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.tsx b/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.tsx index ef2a6fb9ad28b..0f471ae1a837d 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/get_top_nav_links.tsx @@ -27,15 +27,15 @@ export const getTopNavLinks = ({ services, state, onOpenInspector, - isPlainRecord, + isTextBased, adHocDataViews, topNavCustomization, }: { - dataView: DataView; + dataView: DataView | undefined; services: DiscoverServices; state: DiscoverStateContainer; onOpenInspector: () => void; - isPlainRecord: boolean; + isTextBased: boolean; adHocDataViews: DataView[]; topNavCustomization: TopNavCustomization | undefined; }): TopNavMenuData[] => { @@ -53,7 +53,7 @@ export const getTopNavLinks = ({ services, stateContainer: state, adHocDataViews, - isPlainRecord, + isPlainRecord: isTextBased, }); }, testId: 'discoverAlertsButton', @@ -126,7 +126,7 @@ export const getTopNavLinks = ({ savedSearch.searchSource, state.appState.getState(), services, - isPlainRecord + isTextBased ); const { locator } = services; @@ -134,12 +134,11 @@ export const getTopNavLinks = ({ const { timefilter } = services.data.query.timefilter; const timeRange = timefilter.getTime(); const refreshInterval = timefilter.getRefreshInterval(); - const { grid, ...otherState } = appState; const filters = services.filterManager.getFilters(); // Share -> Get links -> Snapshot const params: DiscoverAppLocatorParams = { - ...otherState, + ...appState, ...(savedSearch.id ? { savedSearchId: savedSearch.id } : {}), ...(dataView?.isPersisted() ? { dataViewId: dataView?.id } diff --git a/src/plugins/discover/public/application/main/components/top_nav/on_save_search.test.tsx b/src/plugins/discover/public/application/main/components/top_nav/on_save_search.test.tsx index 5506010528e05..1d422baf238ff 100644 --- a/src/plugins/discover/public/application/main/components/top_nav/on_save_search.test.tsx +++ b/src/plugins/discover/public/application/main/components/top_nav/on_save_search.test.tsx @@ -25,6 +25,10 @@ function getStateContainer({ dataView }: { dataView?: DataView } = {}) { const stateContainer = getDiscoverStateContainer({ services: discoverServiceMock, history, + customizationContext: { + displayMode: 'standalone', + showLogExplorerTabs: false, + }, }); stateContainer.savedSearchState.set(savedSearch); stateContainer.appState.getState = jest.fn(() => ({ diff --git a/src/plugins/discover/public/application/main/components/top_nav/use_discover_topnav.ts b/src/plugins/discover/public/application/main/components/top_nav/use_discover_topnav.ts new file mode 100644 index 0000000000000..7304fd941c4a3 --- /dev/null +++ b/src/plugins/discover/public/application/main/components/top_nav/use_discover_topnav.ts @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { useMemo } from 'react'; +import useObservable from 'react-use/lib/useObservable'; +import { useDiscoverCustomization } from '../../../../customizations'; +import { useDiscoverServices } from '../../../../hooks/use_discover_services'; +import { useInspector } from '../../hooks/use_inspector'; +import { useAppStateSelector } from '../../services/discover_app_state_container'; +import { useInternalStateSelector } from '../../services/discover_internal_state_container'; +import type { DiscoverStateContainer } from '../../services/discover_state'; +import { isTextBasedQuery } from '../../utils/is_text_based_query'; +import { getTopNavBadges } from './get_top_nav_badges'; +import { getTopNavLinks } from './get_top_nav_links'; + +export const useDiscoverTopNav = ({ + stateContainer, +}: { + stateContainer: DiscoverStateContainer; +}) => { + const services = useDiscoverServices(); + const topNavCustomization = useDiscoverCustomization('top_nav'); + const hasSavedSearchChanges = useObservable(stateContainer.savedSearchState.getHasChanged$()); + const hasUnsavedChanges = Boolean( + hasSavedSearchChanges && stateContainer.savedSearchState.getId() + ); + + const topNavBadges = useMemo( + () => + getTopNavBadges({ + stateContainer, + services, + hasUnsavedChanges, + topNavCustomization, + }), + [stateContainer, services, hasUnsavedChanges, topNavCustomization] + ); + + const dataView = useInternalStateSelector((state) => state.dataView); + const adHocDataViews = useInternalStateSelector((state) => state.adHocDataViews); + const query = useAppStateSelector((state) => state.query); + const isTextBased = useMemo(() => isTextBasedQuery(query), [query]); + const onOpenInspector = useInspector({ + inspector: services.inspector, + stateContainer, + }); + + const topNavMenu = useMemo( + () => + getTopNavLinks({ + dataView, + services, + state: stateContainer, + onOpenInspector, + isTextBased, + adHocDataViews, + topNavCustomization, + }), + [ + adHocDataViews, + dataView, + isTextBased, + onOpenInspector, + services, + stateContainer, + topNavCustomization, + ] + ); + + return { + topNavMenu, + topNavBadges, + }; +}; diff --git a/src/plugins/discover/public/application/main/discover_main_app.tsx b/src/plugins/discover/public/application/main/discover_main_app.tsx index dbc1db449b030..d10e74a66523b 100644 --- a/src/plugins/discover/public/application/main/discover_main_app.tsx +++ b/src/plugins/discover/public/application/main/discover_main_app.tsx @@ -18,7 +18,6 @@ import { useSavedSearchAliasMatchRedirect } from '../../hooks/saved_search_alias import { useSavedSearchInitial } from './services/discover_state_provider'; import { useAdHocDataViews } from './hooks/use_adhoc_data_views'; import { useTextBasedQueryLanguage } from './hooks/use_text_based_query_language'; -import type { DiscoverDisplayMode } from '../types'; import { addLog } from '../../utils/add_log'; const DiscoverLayoutMemoized = React.memo(DiscoverLayout); @@ -28,11 +27,10 @@ export interface DiscoverMainProps { * Central state container */ stateContainer: DiscoverStateContainer; - mode?: DiscoverDisplayMode; } export function DiscoverMainApp(props: DiscoverMainProps) { - const { stateContainer, mode = 'standalone' } = props; + const { stateContainer } = props; const savedSearch = useSavedSearchInitial(); const services = useDiscoverServices(); const { chrome, docLinks, data, spaces, history } = services; @@ -65,12 +63,18 @@ export function DiscoverMainApp(props: DiscoverMainProps) { * SavedSearch dependent initializing */ useEffect(() => { - if (mode === 'standalone') { + if (stateContainer.customizationContext.displayMode === 'standalone') { const pageTitleSuffix = savedSearch.id && savedSearch.title ? `: ${savedSearch.title}` : ''; chrome.docTitle.change(`Discover${pageTitleSuffix}`); setBreadcrumbs({ titleBreadcrumbText: savedSearch.title, services }); } - }, [mode, chrome.docTitle, savedSearch.id, savedSearch.title, services]); + }, [ + chrome.docTitle, + savedSearch.id, + savedSearch.title, + services, + stateContainer.customizationContext.displayMode, + ]); useEffect(() => { addHelpMenuToAppChrome(chrome, docLinks); diff --git a/src/plugins/discover/public/application/main/discover_main_route.test.tsx b/src/plugins/discover/public/application/main/discover_main_route.test.tsx index 560f998b4bd94..63296bfb927e9 100644 --- a/src/plugins/discover/public/application/main/discover_main_route.test.tsx +++ b/src/plugins/discover/public/application/main/discover_main_route.test.tsx @@ -11,7 +11,7 @@ import { waitFor } from '@testing-library/react'; import { setHeaderActionMenuMounter, setScopedHistory } from '../../kibana_services'; import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { discoverServiceMock } from '../../__mocks__/services'; -import { DiscoverMainRoute } from './discover_main_route'; +import { DiscoverMainRoute, MainRouteProps } from './discover_main_route'; import { MemoryRouter } from 'react-router-dom'; import { DiscoverMainApp } from './discover_main_app'; import { findTestSubject } from '@elastic/eui/lib/test'; @@ -20,6 +20,7 @@ import { createCustomizationService, DiscoverCustomizationService, } from '../../customizations/customization_service'; +import { DiscoverTopNavServerless } from './components/top_nav/discover_topnav_serverless'; let mockCustomizationService: DiscoverCustomizationService | undefined; @@ -98,12 +99,26 @@ describe('DiscoverMainRoute', () => { expect(component.find(DiscoverMainApp).exists()).toBe(true); }); }); + + test('should pass hideNavMenuItems=true to DiscoverTopNavServerless while loading', async () => { + const component = mountComponent(true, true); + expect(component.find(DiscoverTopNavServerless).prop('hideNavMenuItems')).toBe(true); + await waitFor(() => { + expect(component.update().find(DiscoverTopNavServerless).prop('hideNavMenuItems')).toBe( + false + ); + }); + }); }); const mountComponent = (hasESData = true, hasUserDataView = true) => { - const props = { + const props: MainRouteProps = { isDev: false, customizationCallbacks: [], + customizationContext: { + displayMode: 'standalone', + showLogExplorerTabs: false, + }, }; return mountWithIntl( diff --git a/src/plugins/discover/public/application/main/discover_main_route.tsx b/src/plugins/discover/public/application/main/discover_main_route.tsx index 9e46f2134acd8..2a2575a773524 100644 --- a/src/plugins/discover/public/application/main/discover_main_route.tsx +++ b/src/plugins/discover/public/application/main/discover_main_route.tsx @@ -5,10 +5,15 @@ * in compliance with, at your election, the Elastic License 2.0 or the Server * Side Public License, v 1. */ + import React, { useEffect, useState, memo, useCallback, useMemo } from 'react'; import { useParams, useHistory } from 'react-router-dom'; import type { DataView } from '@kbn/data-views-plugin/public'; -import { redirectWhenMissing, SavedObjectNotFound } from '@kbn/kibana-utils-plugin/public'; +import { + type IKbnUrlStateStorage, + redirectWhenMissing, + SavedObjectNotFound, +} from '@kbn/kibana-utils-plugin/public'; import { useExecutionContext } from '@kbn/kibana-react-plugin/public'; import { AnalyticsNoDataPageKibanaProvider, @@ -34,7 +39,8 @@ import { DiscoverCustomizationProvider, useDiscoverCustomizationService, } from '../../customizations'; -import type { DiscoverDisplayMode } from '../types'; +import type { DiscoverCustomizationContext } from '../types'; +import { DiscoverTopNavServerless } from './components/top_nav/discover_topnav_serverless'; const DiscoverMainAppMemoized = memo(DiscoverMainApp); @@ -44,11 +50,16 @@ interface DiscoverLandingParams { export interface MainRouteProps { customizationCallbacks: CustomizationCallback[]; + stateStorageContainer?: IKbnUrlStateStorage; isDev: boolean; - mode?: DiscoverDisplayMode; + customizationContext: DiscoverCustomizationContext; } -export function DiscoverMainRoute({ customizationCallbacks, mode = 'standalone' }: MainRouteProps) { +export function DiscoverMainRoute({ + customizationCallbacks, + customizationContext, + stateStorageContainer, +}: MainRouteProps) { const history = useHistory(); const services = useDiscoverServices(); const { @@ -60,12 +71,12 @@ export function DiscoverMainRoute({ customizationCallbacks, mode = 'standalone' dataViewEditor, } = services; const { id: savedSearchId } = useParams(); - const stateContainer = useSingleton(() => getDiscoverStateContainer({ history, services, - mode, + customizationContext, + stateStorageContainer, }) ); const { customizationService, isInitialized: isCustomizationServiceInitialized } = @@ -150,7 +161,7 @@ export function DiscoverMainRoute({ customizationCallbacks, mode = 'standalone' dataView: nextDataView, dataViewSpec: historyLocationState?.dataViewSpec, }); - if (mode === 'standalone') { + if (customizationContext.displayMode === 'standalone') { if (currentSavedSearch?.id) { chrome.recentlyAccessed.add( getSavedSearchFullPathUrl(currentSavedSearch.id), @@ -195,32 +206,20 @@ export function DiscoverMainRoute({ customizationCallbacks, mode = 'standalone' }, [ checkData, - stateContainer, + stateContainer.actions, savedSearchId, historyLocationState?.dataViewSpec, - chrome, + customizationContext.displayMode, services, + chrome.recentlyAccessed, history, core.application.navigateToApp, core.theme, basePath, toastNotifications, - mode, ] ); - const onDataViewCreated = useCallback( - async (nextDataView: unknown) => { - if (nextDataView) { - setLoading(true); - setShowNoDataPage(false); - setError(undefined); - await loadSavedSearch(nextDataView as DataView); - } - }, - [loadSavedSearch] - ); - useEffect(() => { if (!isCustomizationServiceInitialized) return; @@ -244,8 +243,20 @@ export function DiscoverMainRoute({ customizationCallbacks, mode = 'standalone' }, }); - if (showNoDataPage) { - const analyticsServices = { + const onDataViewCreated = useCallback( + async (nextDataView: unknown) => { + if (nextDataView) { + setLoading(true); + setShowNoDataPage(false); + setError(undefined); + await loadSavedSearch(nextDataView as DataView); + } + }, + [loadSavedSearch] + ); + + const noDataDependencies = useMemo( + () => ({ coreStart: core, dataViews: { ...data.dataViews, @@ -260,27 +271,53 @@ export function DiscoverMainRoute({ customizationCallbacks, mode = 'standalone' }, dataViewEditor, noDataPage: services.noDataPage, - }; + }), + [core, data.dataViews, dataViewEditor, hasESData, hasUserDataView, services.noDataPage] + ); - return ( - - - - ); - } + const loadingIndicator = useMemo( + () => , + [hasCustomBranding] + ); + + const mainContent = useMemo(() => { + if (showNoDataPage) { + return ( + + + + ); + } + + if (loading) { + return loadingIndicator; + } + + return ; + }, [ + loading, + loadingIndicator, + noDataDependencies, + onDataViewCreated, + showNoDataPage, + stateContainer, + ]); if (error) { return ; } - if (loading || !customizationService) { - return ; + if (!customizationService) { + return loadingIndicator; } return ( - + <> + + {mainContent} + ); diff --git a/src/plugins/discover/public/application/main/services/discover_app_state_container.ts b/src/plugins/discover/public/application/main/services/discover_app_state_container.ts index 124c83beda236..e1614bf796391 100644 --- a/src/plugins/discover/public/application/main/services/discover_app_state_container.ts +++ b/src/plugins/discover/public/application/main/services/discover_app_state_container.ts @@ -16,6 +16,7 @@ import { COMPARE_ALL_OPTIONS, compareFilters, Filter, + FilterCompareOptions, FilterStateStore, Query, } from '@kbn/es-query'; @@ -23,7 +24,7 @@ import { SavedSearch, VIEW_MODE } from '@kbn/saved-search-plugin/public'; import { IKbnUrlStateStorage, ISyncStateRef, syncState } from '@kbn/kibana-utils-plugin/public'; import { isEqual } from 'lodash'; import { connectToQueryState, syncGlobalQueryStateWithUrl } from '@kbn/data-plugin/public'; -import type { UnifiedDataTableSettings } from '@kbn/unified-data-table'; +import type { DiscoverGridSettings } from '@kbn/saved-search-plugin/common'; import type { DiscoverServices } from '../../../build_services'; import { addLog } from '../../../utils/add_log'; import { cleanupUrlState } from '../utils/cleanup_url_state'; @@ -93,7 +94,7 @@ export interface DiscoverAppState { /** * Data Grid related state */ - grid?: UnifiedDataTableSettings; + grid?: DiscoverGridSettings; /** * Hide chart */ @@ -328,13 +329,17 @@ export function setState( /** * Helper function to compare 2 different filter states */ -export function isEqualFilters(filtersA?: Filter[] | Filter, filtersB?: Filter[] | Filter) { +export function isEqualFilters( + filtersA?: Filter[] | Filter, + filtersB?: Filter[] | Filter, + comparatorOptions: FilterCompareOptions = COMPARE_ALL_OPTIONS +) { if (!filtersA && !filtersB) { return true; } else if (!filtersA || !filtersB) { return false; } - return compareFilters(filtersA, filtersB, COMPARE_ALL_OPTIONS); + return compareFilters(filtersA, filtersB, comparatorOptions); } /** diff --git a/src/plugins/discover/public/application/main/services/discover_saved_search_container.ts b/src/plugins/discover/public/application/main/services/discover_saved_search_container.ts index 8948c954d039c..422fa787da849 100644 --- a/src/plugins/discover/public/application/main/services/discover_saved_search_container.ts +++ b/src/plugins/discover/public/application/main/services/discover_saved_search_container.ts @@ -8,19 +8,25 @@ import { SavedSearch } from '@kbn/saved-search-plugin/public'; import { BehaviorSubject } from 'rxjs'; +import { COMPARE_ALL_OPTIONS, FilterCompareOptions } from '@kbn/es-query'; import type { SearchSourceFields } from '@kbn/data-plugin/common'; import type { DataView } from '@kbn/data-views-plugin/common'; import { SavedObjectSaveOpts } from '@kbn/saved-objects-plugin/public'; -import { isEqual } from 'lodash'; +import { isEqual, isFunction } from 'lodash'; import { restoreStateFromSavedSearch } from '../../../services/saved_searches/restore_from_saved_search'; import { updateSavedSearch } from '../utils/update_saved_search'; import { addLog } from '../../../utils/add_log'; import { handleSourceColumnState } from '../../../utils/state_helpers'; -import { DiscoverAppState } from './discover_app_state_container'; +import { DiscoverAppState, isEqualFilters } from './discover_app_state_container'; import { DiscoverServices } from '../../../build_services'; import { getStateDefaults } from '../utils/get_state_defaults'; import type { DiscoverGlobalStateContainer } from './discover_global_state_container'; +const FILTERS_COMPARE_OPTIONS: FilterCompareOptions = { + ...COMPARE_ALL_OPTIONS, + state: false, // We don't compare filter types (global vs appState). +}; + export interface UpdateParams { /** * The next data view to be used @@ -279,11 +285,13 @@ export function isEqualSavedSearch(savedSearchPrev: SavedSearch, savedSearchNext const hasChangesInSearchSource = ( ['filter', 'query', 'index'] as Array ).some((key) => { - const prevValue = - key === 'index' ? prevSearchSource.getField(key)?.id : prevSearchSource.getField(key); - const nextValue = - key === 'index' ? nextSearchSource.getField(key)?.id : nextSearchSource.getField(key); - const isSame = isEqual(prevValue, nextValue); + const prevValue = getSearchSourceFieldValueForComparison(prevSearchSource, key); + const nextValue = getSearchSourceFieldValueForComparison(nextSearchSource, key); + + const isSame = + key === 'filter' + ? isEqualFilters(prevValue, nextValue, FILTERS_COMPARE_OPTIONS) // if a filter gets pinned and the order of filters does not change, we don't show the unsaved changes badge + : isEqual(prevValue, nextValue); if (!isSame) { addLog('[savedSearch] difference between initial and changed version', { @@ -304,3 +312,19 @@ export function isEqualSavedSearch(savedSearchPrev: SavedSearch, savedSearchNext return true; } + +function getSearchSourceFieldValueForComparison( + searchSource: SavedSearch['searchSource'], + searchSourceFieldName: keyof SearchSourceFields +) { + if (searchSourceFieldName === 'index') { + return searchSource.getField('index')?.id; + } + + if (searchSourceFieldName === 'filter') { + const filterField = searchSource.getField('filter'); + return isFunction(filterField) ? filterField() : filterField; + } + + return searchSource.getField(searchSourceFieldName); +} diff --git a/src/plugins/discover/public/application/main/services/discover_state.test.ts b/src/plugins/discover/public/application/main/services/discover_state.test.ts index c946ac70bbe62..2cf1e2c71880d 100644 --- a/src/plugins/discover/public/application/main/services/discover_state.test.ts +++ b/src/plugins/discover/public/application/main/services/discover_state.test.ts @@ -11,7 +11,7 @@ import { DiscoverStateContainer, createSearchSessionRestorationDataProvider, } from './discover_state'; -import { createBrowserHistory, History } from 'history'; +import { createBrowserHistory, createMemoryHistory, History } from 'history'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; import type { SavedSearch, SortOrder } from '@kbn/saved-search-plugin/public'; import { @@ -22,11 +22,12 @@ import { } from '../../../__mocks__/saved_search'; import { discoverServiceMock } from '../../../__mocks__/services'; import { dataViewMock } from '@kbn/discover-utils/src/__mocks__'; -import { DiscoverAppStateContainer } from './discover_app_state_container'; +import type { DiscoverAppStateContainer } from './discover_app_state_container'; import { waitFor } from '@testing-library/react'; -import { FetchStatus } from '../../types'; +import { DiscoverCustomizationContext, FetchStatus } from '../../types'; import { dataViewAdHoc, dataViewComplexMock } from '../../../__mocks__/data_view_complex'; import { copySavedSearch } from './discover_saved_search_container'; +import { createKbnUrlStateStorage, IKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; const startSync = (appState: DiscoverAppStateContainer) => { const { start, stop } = appState.syncState(); @@ -34,6 +35,11 @@ const startSync = (appState: DiscoverAppStateContainer) => { return stop; }; +const customizationContext: DiscoverCustomizationContext = { + displayMode: 'standalone', + showLogExplorerTabs: false, +}; + async function getState( url: string = '/', { savedSearch, isEmptyUrl }: { savedSearch?: SavedSearch; isEmptyUrl?: boolean } = {} @@ -51,6 +57,7 @@ async function getState( const nextState = getDiscoverStateContainer({ services: discoverServiceMock, history: nextHistory, + customizationContext, }); nextState.appState.isEmptyURL = jest.fn(() => isEmptyUrl ?? true); jest.spyOn(nextState.dataState, 'fetch'); @@ -87,9 +94,10 @@ describe('Test discover state', () => { state = getDiscoverStateContainer({ services: discoverServiceMock, history, + customizationContext, }); state.savedSearchState.set(savedSearchMock); - await state.appState.update({}, true); + state.appState.update({}, true); stopSync = startSync(state.appState); }); afterEach(() => { @@ -137,10 +145,75 @@ describe('Test discover state', () => { test('pauseAutoRefreshInterval sets refreshInterval.pause to true', async () => { history.push('/#?_g=(refreshInterval:(pause:!f,value:5000))'); expect(getCurrentUrl()).toBe('/#?_g=(refreshInterval:(pause:!f,value:5000))'); - await state.actions.setDataView(dataViewMock); + // TODO: state.actions.setDataView should be async because it calls pauseAutoRefreshInterval which is async. + // I found this bug while removing unnecessary awaits, but it will need to be fixed in a follow up PR. + state.actions.setDataView(dataViewMock); + await new Promise(process.nextTick); expect(getCurrentUrl()).toBe('/#?_g=(refreshInterval:(pause:!t,value:5000))'); }); }); + +describe('Test discover state with overridden state storage', () => { + let stopSync = () => {}; + let history: History; + let stateStorage: IKbnUrlStateStorage; + let state: DiscoverStateContainer; + + beforeEach(async () => { + jest.useFakeTimers(); + history = createMemoryHistory({ + initialEntries: [ + { + pathname: '/', + hash: `?_a=()`, + }, + ], + }); + stateStorage = createKbnUrlStateStorage({ + history, + useHash: false, + useHashQuery: true, + }); + state = getDiscoverStateContainer({ + services: discoverServiceMock, + history, + customizationContext, + stateStorageContainer: stateStorage, + }); + state.savedSearchState.set(savedSearchMock); + state.appState.update({}, true); + stopSync = startSync(state.appState); + }); + + afterEach(() => { + stopSync(); + stopSync = () => {}; + jest.useRealTimers(); + }); + + test('setting app state and syncing to URL', async () => { + state.appState.update({ index: 'modified' }); + + await jest.runAllTimersAsync(); + + expect(history.createHref(history.location)).toMatchInlineSnapshot( + `"/#?_a=(columns:!(default_column),index:modified,interval:auto,sort:!())"` + ); + }); + + test('changing URL to be propagated to appState', async () => { + history.push('/#?_a=(index:modified)'); + + await jest.runAllTimersAsync(); + + expect(state.appState.getState()).toMatchInlineSnapshot(` + Object { + "index": "modified", + } + `); + }); +}); + describe('Test discover initial state sort handling', () => { test('Non-empty sort in URL should not be overwritten by saved search sort', async () => { const savedSearch = { @@ -190,6 +263,7 @@ describe('Test createSearchSessionRestorationDataProvider', () => { const discoverStateContainer = getDiscoverStateContainer({ services: discoverServiceMock, history, + customizationContext, }); discoverStateContainer.appState.update({ index: savedSearchMock.searchSource.getField('index')!.id, @@ -661,9 +735,9 @@ describe('Test discover state actions', () => { const { state } = await getState('/', { savedSearch: savedSearchMock }); const unsubscribe = state.actions.initializeAndSync(); await state.actions.loadSavedSearch({ savedSearchId: savedSearchMock.id }); - await state.savedSearchState.update({ nextState: { hideChart: true } }); + state.savedSearchState.update({ nextState: { hideChart: true } }); expect(state.savedSearchState.getState().hideChart).toBe(true); - await state.actions.onOpenSavedSearch(savedSearchMock.id!); + state.actions.onOpenSavedSearch(savedSearchMock.id!); expect(state.savedSearchState.getState().hideChart).toBe(undefined); unsubscribe(); }); @@ -756,16 +830,21 @@ describe('Test discover state with embedded mode', () => { state = getDiscoverStateContainer({ services: discoverServiceMock, history, - mode: 'embedded', + customizationContext: { + ...customizationContext, + displayMode: 'embedded', + }, }); state.savedSearchState.set(savedSearchMock); - await state.appState.update({}, true); + state.appState.update({}, true); stopSync = startSync(state.appState); }); + afterEach(() => { stopSync(); stopSync = () => {}; }); + test('setting app state and syncing to URL', async () => { state.appState.update({ index: 'modified' }); await new Promise(process.nextTick); diff --git a/src/plugins/discover/public/application/main/services/discover_state.ts b/src/plugins/discover/public/application/main/services/discover_state.ts index b3abfed5e6ecc..8994afb8a5f96 100644 --- a/src/plugins/discover/public/application/main/services/discover_state.ts +++ b/src/plugins/discover/public/application/main/services/discover_state.ts @@ -26,7 +26,7 @@ import { merge } from 'rxjs'; import { AggregateQuery, Query, TimeRange } from '@kbn/es-query'; import { loadSavedSearch as loadSavedSearchFn } from './load_saved_search'; import { restoreStateFromSavedSearch } from '../../../services/saved_searches/restore_from_saved_search'; -import { DiscoverDisplayMode, FetchStatus } from '../../types'; +import { DiscoverCustomizationContext, FetchStatus } from '../../types'; import { changeDataView } from '../hooks/utils/change_data_view'; import { buildStateSubscribe } from '../hooks/utils/build_state_subscribe'; import { addLog } from '../../../utils/add_log'; @@ -67,11 +67,14 @@ interface DiscoverStateContainerParams { * core ui settings service */ services: DiscoverServices; - /* - * mode in which discover is running - * - * */ - mode?: DiscoverDisplayMode; + /** + * Context object for customization related properties + */ + customizationContext: DiscoverCustomizationContext; + /** + * a custom url state storage + */ + stateStorageContainer?: IKbnUrlStateStorage; } export interface LoadParams { @@ -94,7 +97,6 @@ export interface DiscoverStateContainer { * Global State, the _g part of the URL */ globalState: DiscoverGlobalStateContainer; - /** * App state, the _a part of the URL */ @@ -119,6 +121,10 @@ export interface DiscoverStateContainer { * Service for handling search sessions */ searchSessionManager: DiscoverSearchSessionManager; + /** + * Context object for customization related properties + */ + customizationContext: DiscoverCustomizationContext; /** * Complex functions to update multiple containers from UI */ @@ -190,7 +196,7 @@ export interface DiscoverStateContainer { * When saving a saved search with an ad hoc data view, a new id needs to be generated for the data view * This is to prevent duplicate ids messing with our system */ - updateAdHocDataViewId: () => void; + updateAdHocDataViewId: () => Promise; }; } @@ -201,7 +207,8 @@ export interface DiscoverStateContainer { export function getDiscoverStateContainer({ history, services, - mode = 'standalone', + customizationContext, + stateStorageContainer, }: DiscoverStateContainerParams): DiscoverStateContainer { const storeInSessionStorage = services.uiSettings.get('state:storeInSessionStorage'); const toasts = services.core.notifications.toasts; @@ -209,12 +216,14 @@ export function getDiscoverStateContainer({ /** * state storage for state in the URL */ - const stateStorage = createKbnUrlStateStorage({ - useHash: storeInSessionStorage, - history, - useHashQuery: mode !== 'embedded', - ...(toasts && withNotifyOnErrors(toasts)), - }); + const stateStorage = + stateStorageContainer ?? + createKbnUrlStateStorage({ + useHash: storeInSessionStorage, + history, + useHashQuery: customizationContext.displayMode !== 'embedded', + ...(toasts && withNotifyOnErrors(toasts)), + }); /** * Search session logic @@ -457,6 +466,16 @@ export function getDiscoverStateContainer({ timefilter: services.timefilter, }); const newAppState = getDefaultAppState(nextSavedSearch, services); + + // a saved search can't have global (pinned) filters so we can reset global filters state + const globalFilters = globalStateContainer.get()?.filters; + if (globalFilters) { + await globalStateContainer.set({ + ...globalStateContainer.get(), + filters: [], + }); + } + await appStateContainer.replaceUrlState(newAppState); return nextSavedSearch; }; @@ -475,6 +494,7 @@ export function getDiscoverStateContainer({ savedSearchState: savedSearchContainer, stateStorage, searchSessionManager, + customizationContext, actions: { initializeAndSync, fetchData, @@ -550,6 +570,7 @@ function createUrlGeneratorState({ : data.query.timefilter.timefilter.getTime(), searchSessionId: shouldRestoreSearchSession ? data.search.session.getSessionId() : undefined, columns: appState.columns, + grid: appState.grid, sort: appState.sort, savedQuery: appState.savedQuery, interval: appState.interval, diff --git a/src/plugins/discover/public/application/main/utils/update_saved_search.test.ts b/src/plugins/discover/public/application/main/utils/update_saved_search.test.ts index 8c8d48cd9105b..17fee93fc92af 100644 --- a/src/plugins/discover/public/application/main/utils/update_saved_search.test.ts +++ b/src/plugins/discover/public/application/main/utils/update_saved_search.test.ts @@ -70,7 +70,7 @@ describe('updateSavedSearch', () => { }, }); expect(savedSearch.searchSource.getField('query')).toEqual(query); - expect(savedSearch.searchSource.getField('filter')).toEqual([appFilter, globalFilter]); + expect(savedSearch.searchSource.getField('filter')).toEqual([globalFilter, appFilter]); }); it('should set query and filters from services', async () => { diff --git a/src/plugins/discover/public/application/main/utils/update_saved_search.ts b/src/plugins/discover/public/application/main/utils/update_saved_search.ts index fb6ffd6621825..ff9d5a70f39be 100644 --- a/src/plugins/discover/public/application/main/utils/update_saved_search.ts +++ b/src/plugins/discover/public/application/main/utils/update_saved_search.ts @@ -52,7 +52,7 @@ export function updateSavedSearch({ savedSearch.searchSource .setField('query', state.query ?? undefined) - .setField('filter', [...appFilters, ...globalFilters]); + .setField('filter', [...globalFilters, ...appFilters]); } if (state) { savedSearch.columns = state.columns || []; diff --git a/src/plugins/discover/public/application/types.ts b/src/plugins/discover/public/application/types.ts index 7e3413143ae51..70773d2db521f 100644 --- a/src/plugins/discover/public/application/types.ts +++ b/src/plugins/discover/public/application/types.ts @@ -21,6 +21,17 @@ export enum FetchStatus { export type DiscoverDisplayMode = 'embedded' | 'standalone'; +export interface DiscoverCustomizationContext { + /* + * Display mode in which discover is running + */ + displayMode: DiscoverDisplayMode; + /** + * Whether or not to show the Log Explorer tabs + */ + showLogExplorerTabs: boolean; +} + export interface RecordsFetchResponse { records: DataTableRecord[]; textBasedQueryColumns?: DatatableColumn[]; diff --git a/src/plugins/discover/public/build_services.ts b/src/plugins/discover/public/build_services.ts index da82df24e184f..8c69c53e317e3 100644 --- a/src/plugins/discover/public/build_services.ts +++ b/src/plugins/discover/public/build_services.ts @@ -53,6 +53,7 @@ import type { SettingsStart } from '@kbn/core-ui-settings-browser'; import type { ContentClient } from '@kbn/content-management-plugin/public'; import { memoize } from 'lodash'; import type { NoDataPagePluginStart } from '@kbn/no-data-page-plugin/public'; +import type { ServerlessPluginStart } from '@kbn/serverless/public'; import { getHistory } from './kibana_services'; import { DiscoverStartPlugins } from './plugin'; import { DiscoverContextAppLocator } from './application/context/services/locator'; @@ -111,6 +112,7 @@ export interface DiscoverServices { uiActions: UiActionsStart; contentClient: ContentClient; noDataPage?: NoDataPagePluginStart; + serverless?: ServerlessPluginStart; } export const buildServices = memoize(function ( @@ -171,5 +173,6 @@ export const buildServices = memoize(function ( uiActions: plugins.uiActions, contentClient: plugins.contentManagement.client, noDataPage: plugins.noDataPage, + serverless: plugins.serverless, }; }); diff --git a/src/plugins/discover/public/components/common/error_callout.test.tsx b/src/plugins/discover/public/components/common/error_callout.test.tsx index fd07af1bd548d..3094dbc8846aa 100644 --- a/src/plugins/discover/public/components/common/error_callout.test.tsx +++ b/src/plugins/discover/public/components/common/error_callout.test.tsx @@ -14,13 +14,13 @@ import React, { ReactNode } from 'react'; import { discoverServiceMock } from '../../__mocks__/services'; import { ErrorCallout } from './error_callout'; -const mockGetSearchErrorOverrideDisplay = jest.fn(); +const mockRenderSearchError = jest.fn(); -jest.mock('@kbn/data-plugin/public', () => { - const originalModule = jest.requireActual('@kbn/data-plugin/public'); +jest.mock('@kbn/search-errors', () => { + const originalModule = jest.requireActual('@kbn/search-errors'); return { ...originalModule, - getSearchErrorOverrideDisplay: () => mockGetSearchErrorOverrideDisplay(), + renderSearchError: () => mockRenderSearchError(), }; }); @@ -31,7 +31,7 @@ describe('ErrorCallout', () => { ); afterEach(() => { - mockGetSearchErrorOverrideDisplay.mockReset(); + mockRenderSearchError.mockReset(); }); it('should render', () => { @@ -54,7 +54,7 @@ describe('ErrorCallout', () => { const title = 'Override title'; const error = new Error('My error'); const overrideDisplay =
Override display
; - mockGetSearchErrorOverrideDisplay.mockReturnValue({ title, body: overrideDisplay }); + mockRenderSearchError.mockReturnValue({ title, body: overrideDisplay }); const wrapper = mountWithServices(); const prompt = wrapper.find(EuiEmptyPrompt); expect(prompt).toHaveLength(1); diff --git a/src/plugins/discover/public/components/common/error_callout.tsx b/src/plugins/discover/public/components/common/error_callout.tsx index 9b6aa341d5fdc..a2aa2b478a3dc 100644 --- a/src/plugins/discover/public/components/common/error_callout.tsx +++ b/src/plugins/discover/public/components/common/error_callout.tsx @@ -8,7 +8,7 @@ import { EuiButton, EuiEmptyPrompt, useEuiTheme } from '@elastic/eui'; import { css } from '@emotion/react'; -import { getSearchErrorOverrideDisplay } from '@kbn/data-plugin/public'; +import { renderSearchError } from '@kbn/search-errors'; import { i18n } from '@kbn/i18n'; import React from 'react'; import { useDiscoverServices } from '../../hooks/use_discover_services'; @@ -22,7 +22,7 @@ export const ErrorCallout = ({ title, error }: Props) => { const { core } = useDiscoverServices(); const { euiTheme } = useEuiTheme(); - const overrideDisplay = getSearchErrorOverrideDisplay({ + const searchErrorDisplay = renderSearchError({ error, application: core.application, }); @@ -31,15 +31,17 @@ export const ErrorCallout = ({ title, error }: Props) => { {overrideDisplay?.title ?? title}} - actions={overrideDisplay?.actions ?? []} + title={ +

{searchErrorDisplay?.title ?? title}

+ } + actions={searchErrorDisplay?.actions ?? []} body={
- {overrideDisplay?.body ?? ( + {searchErrorDisplay?.body ?? ( <>

Promise; scopedHistory: ScopedHistory; customizationCallbacks: CustomizationCallback[]; + stateStorageContainer?: IKbnUrlStateStorage; isDev: boolean; isLoading?: boolean; } @@ -43,12 +46,18 @@ const discoverContainerWrapperCss = css` } `; +const customizationContext: DiscoverCustomizationContext = { + displayMode: 'embedded', + showLogExplorerTabs: false, +}; + export const DiscoverContainerInternal = ({ overrideServices, scopedHistory, customizationCallbacks, isDev, getDiscoverServices, + stateStorageContainer, isLoading = false, }: DiscoverContainerInternalProps) => { const [discoverServices, setDiscoverServices] = useState(); @@ -90,7 +99,8 @@ export const DiscoverContainerInternal = ({ diff --git a/src/plugins/discover/public/components/discover_grid_flyout/discover_grid_flyout.test.tsx b/src/plugins/discover/public/components/discover_grid_flyout/discover_grid_flyout.test.tsx index 0f17aec0684a2..8d4e9bbee4ae1 100644 --- a/src/plugins/discover/public/components/discover_grid_flyout/discover_grid_flyout.test.tsx +++ b/src/plugins/discover/public/components/discover_grid_flyout/discover_grid_flyout.test.tsx @@ -315,11 +315,14 @@ describe('Discover flyout', function () { it('should provide an actions prop collection to optionally update the grid content', async () => { mockFlyoutCustomization.Content = ({ actions }) => ( <> - -

+ + +
diff --git a/src/plugins/kibana_react/public/util/index.tsx b/src/plugins/kibana_react/public/util/index.tsx index ad00b33c2f344..2fad7daee91e4 100644 --- a/src/plugins/kibana_react/public/util/index.tsx +++ b/src/plugins/kibana_react/public/util/index.tsx @@ -23,6 +23,7 @@ import { toMountPoint as _toMountPoint } from '@kbn/react-kibana-mount'; // and will be removed when the deprecated usages are removed. const themeStart: ThemeServiceStart = { theme$: new Observable((subscriber) => subscriber.next(defaultTheme)), + getTheme: () => defaultTheme, }; // The `i18n` start contract should always be included to ensure diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts index 537b7739bcc0d..1fa2407cdd287 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/schema.ts @@ -581,6 +581,10 @@ export const stackManagementSchema: MakeSchemaFrom = { type: 'boolean', _meta: { description: 'Non-default value of setting.' }, }, + 'observability:enableInfrastructureProfilingIntegration': { + type: 'boolean', + _meta: { description: 'Non-default value of setting.' }, + }, 'securitySolution:enableGroupedNav': { type: 'boolean', _meta: { description: 'Non-default value of setting.' }, diff --git a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts index 273864af2bb4a..33518b5389f57 100644 --- a/src/plugins/kibana_usage_collection/server/collectors/management/types.ts +++ b/src/plugins/kibana_usage_collection/server/collectors/management/types.ts @@ -46,6 +46,7 @@ export interface UsageStats { 'observability:apmAWSLambdaPriceFactor': string; 'observability:apmAWSLambdaRequestCostPerMillion': number; 'observability:enableInfrastructureHostsView': boolean; + 'observability:enableInfrastructureProfilingIntegration': boolean; 'observability:apmAgentExplorerView': boolean; 'visualization:heatmap:maxBuckets': number; 'visualization:colorMapping': string; diff --git a/src/plugins/kibana_utils/public/index.ts b/src/plugins/kibana_utils/public/index.ts index a33a1f5e7e64f..9a717953d60f8 100644 --- a/src/plugins/kibana_utils/public/index.ts +++ b/src/plugins/kibana_utils/public/index.ts @@ -112,7 +112,7 @@ export { } from './history'; export { applyDiff } from './state_management/utils/diff_object'; -export type { KibanaUtilsSetup } from './plugin'; +export type { KibanaUtilsPublicSetup as KibanaUtilsSetup, KibanaUtilsPublicStart } from './plugin'; export function plugin(initializerContext: PluginInitializerContext) { return new KibanaUtilsPublicPlugin(initializerContext); diff --git a/src/plugins/kibana_utils/public/mocks.ts b/src/plugins/kibana_utils/public/mocks.ts index a537c2fc74e90..cb872838af4b4 100644 --- a/src/plugins/kibana_utils/public/mocks.ts +++ b/src/plugins/kibana_utils/public/mocks.ts @@ -6,10 +6,10 @@ * Side Public License, v 1. */ -import { KibanaUtilsSetup, KibanaUtilsStart } from './plugin'; +import { KibanaUtilsPublicSetup, KibanaUtilsPublicStart } from './plugin'; -export type Setup = jest.Mocked; -export type Start = jest.Mocked; +export type Setup = jest.Mocked; +export type Start = jest.Mocked; const createSetupContract = (): Setup => { return { diff --git a/src/plugins/kibana_utils/public/plugin.ts b/src/plugins/kibana_utils/public/plugin.ts index d483754da5b22..05d8ede256498 100644 --- a/src/plugins/kibana_utils/public/plugin.ts +++ b/src/plugins/kibana_utils/public/plugin.ts @@ -10,26 +10,40 @@ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '@kbn/cor import { History } from 'history'; import { setVersion } from './set_version'; -export interface KibanaUtilsSetup { +export interface KibanaUtilsPublicSetup { setVersion: (history: Pick) => void; } -export type KibanaUtilsStart = undefined; +export type KibanaUtilsPublicStart = undefined; -export class KibanaUtilsPublicPlugin implements Plugin { +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface KibanaUtilsPublicSetupDependencies {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface KibanaUtilsPublicStartDependencies {} + +export class KibanaUtilsPublicPlugin + implements + Plugin< + KibanaUtilsPublicSetup, + KibanaUtilsPublicStart, + KibanaUtilsPublicSetupDependencies, + KibanaUtilsPublicStartDependencies + > +{ private readonly version: string; constructor(initializerContext: PluginInitializerContext) { this.version = initializerContext.env.packageInfo.version; } - public setup(core: CoreSetup): KibanaUtilsSetup { + public setup(_core: CoreSetup): KibanaUtilsPublicSetup { return { setVersion: this.setVersion, }; } - public start(core: CoreStart): KibanaUtilsStart { + public start(_core: CoreStart): KibanaUtilsPublicStart { return undefined; } diff --git a/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.test.ts b/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.test.ts index a7b72cb863f71..f3d7a0e0d1f15 100644 --- a/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.test.ts +++ b/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.test.ts @@ -202,7 +202,7 @@ describe('kbn_url_storage', () => { await Promise.all([pr1, pr2, pr3]); expect(getCurrentUrl()).toBe('/3'); - expect(urlControls.getPendingUrl()).toBeUndefined(); + expect(urlControls.getPendingUrl()).toEqual(getCurrentUrl()); }); }); diff --git a/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts b/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts index b81d3c1b81b63..9a4ed8e704b09 100644 --- a/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts +++ b/src/plugins/kibana_utils/public/state_management/url/kbn_url_storage.ts @@ -193,17 +193,17 @@ export const createKbnUrlControls = ( // runs scheduled url updates function flush(replace = shouldReplace) { - const nextUrl = getPendingUrl(); - - if (!nextUrl) return; + if (updateQueue.length === 0) { + return; + } + const nextUrl = getPendingUrl(); cleanUp(); const newUrl = updateUrl(nextUrl, replace); return newUrl; } function getPendingUrl() { - if (updateQueue.length === 0) return undefined; const resultUrl = updateQueue.reduce( (url, nextUpdate) => nextUpdate(url) ?? url, getCurrentUrl(history) diff --git a/src/plugins/kibana_utils/public/state_sync/state_sync_state_storage/create_kbn_url_state_storage.ts b/src/plugins/kibana_utils/public/state_sync/state_sync_state_storage/create_kbn_url_state_storage.ts index 6ad69238608c6..d823a95da59ce 100644 --- a/src/plugins/kibana_utils/public/state_sync/state_sync_state_storage/create_kbn_url_state_storage.ts +++ b/src/plugins/kibana_utils/public/state_sync/state_sync_state_storage/create_kbn_url_state_storage.ts @@ -116,7 +116,11 @@ export const createKbnUrlStateStorage = ( unlisten(); }; }).pipe( - map(() => getStateFromKbnUrl(key, undefined, { getFromHashQuery: useHashQuery })), + map(() => + getStateFromKbnUrl(key, history?.createHref(history.location), { + getFromHashQuery: useHashQuery, + }) + ), catchError((error) => { if (onGetErrorThrottled) onGetErrorThrottled(error); return of(null); diff --git a/src/plugins/navigation/public/index.ts b/src/plugins/navigation/public/index.ts index 7db4526995c04..a68f8f2bbcdbd 100644 --- a/src/plugins/navigation/public/index.ts +++ b/src/plugins/navigation/public/index.ts @@ -14,9 +14,12 @@ export function plugin(initializerContext: PluginInitializerContext) { } export type { TopNavMenuData, TopNavMenuProps, TopNavMenuBadgeProps } from './top_nav_menu'; -export { TopNavMenu } from './top_nav_menu'; +export { TopNavMenu, TopNavMenuItems, TopNavMenuBadges } from './top_nav_menu'; -export type { NavigationPublicPluginSetup, NavigationPublicPluginStart } from './types'; +export type { + NavigationPublicSetup as NavigationPublicPluginSetup, + NavigationPublicStart as NavigationPublicPluginStart, +} from './types'; // Export plugin after all other imports import { NavigationPublicPlugin } from './plugin'; diff --git a/src/plugins/navigation/public/plugin.ts b/src/plugins/navigation/public/plugin.ts index 3be546918cdc2..58e21e4a99f13 100644 --- a/src/plugins/navigation/public/plugin.ts +++ b/src/plugins/navigation/public/plugin.ts @@ -9,22 +9,29 @@ import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import { - NavigationPublicPluginSetup, - NavigationPublicPluginStart, - NavigationPluginStartDependencies, + NavigationPublicSetup, + NavigationPublicStart, + NavigationPublicSetupDependencies, + NavigationPublicStartDependencies, } from './types'; import { TopNavMenuExtensionsRegistry, createTopNav } from './top_nav_menu'; import { RegisteredTopNavMenuData } from './top_nav_menu/top_nav_menu_data'; export class NavigationPublicPlugin - implements Plugin + implements + Plugin< + NavigationPublicSetup, + NavigationPublicStart, + NavigationPublicSetupDependencies, + NavigationPublicStartDependencies + > { private readonly topNavMenuExtensionsRegistry: TopNavMenuExtensionsRegistry = new TopNavMenuExtensionsRegistry(); - constructor(initializerContext: PluginInitializerContext) {} + constructor(_initializerContext: PluginInitializerContext) {} - public setup(core: CoreSetup): NavigationPublicPluginSetup { + public setup(_core: CoreSetup): NavigationPublicSetup { return { registerMenuItem: this.topNavMenuExtensionsRegistry.register.bind( this.topNavMenuExtensionsRegistry @@ -33,9 +40,9 @@ export class NavigationPublicPlugin } public start( - core: CoreStart, - { unifiedSearch }: NavigationPluginStartDependencies - ): NavigationPublicPluginStart { + _core: CoreStart, + { unifiedSearch }: NavigationPublicStartDependencies + ): NavigationPublicStart { const extensions = this.topNavMenuExtensionsRegistry.getAll(); /* diff --git a/src/plugins/navigation/public/top_nav_menu/index.ts b/src/plugins/navigation/public/top_nav_menu/index.ts index e6705273c2a1b..24986f3acb3ba 100644 --- a/src/plugins/navigation/public/top_nav_menu/index.ts +++ b/src/plugins/navigation/public/top_nav_menu/index.ts @@ -7,8 +7,10 @@ */ export { createTopNav } from './create_top_nav_menu'; -export type { TopNavMenuProps, TopNavMenuBadgeProps } from './top_nav_menu'; +export type { TopNavMenuProps } from './top_nav_menu'; export { TopNavMenu } from './top_nav_menu'; export type { TopNavMenuData } from './top_nav_menu_data'; export type { TopNavMenuExtensionsRegistrySetup } from './top_nav_menu_extensions_registry'; export { TopNavMenuExtensionsRegistry } from './top_nav_menu_extensions_registry'; +export { TopNavMenuItems } from './top_nav_menu_items'; +export { TopNavMenuBadges, type TopNavMenuBadgeProps } from './top_nav_menu_badges'; diff --git a/src/plugins/navigation/public/top_nav_menu/top_nav_menu.test.tsx b/src/plugins/navigation/public/top_nav_menu/top_nav_menu.test.tsx index 218c620519cf2..376d0c4a7b442 100644 --- a/src/plugins/navigation/public/top_nav_menu/top_nav_menu.test.tsx +++ b/src/plugins/navigation/public/top_nav_menu/top_nav_menu.test.tsx @@ -10,11 +10,12 @@ import React from 'react'; import { ReactWrapper } from 'enzyme'; import { act } from 'react-dom/test-utils'; import { MountPoint } from '@kbn/core/public'; -import { TopNavMenu, TopNavMenuBadgeProps } from './top_nav_menu'; +import { TopNavMenu } from './top_nav_menu'; import { TopNavMenuData } from './top_nav_menu_data'; -import { shallowWithIntl, mountWithIntl } from '@kbn/test-jest-helpers'; +import { findTestSubject, mountWithIntl } from '@kbn/test-jest-helpers'; import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import { EuiToolTipProps } from '@elastic/eui'; +import type { TopNavMenuBadgeProps } from './top_nav_menu_badges'; const unifiedSearch = { ui: { @@ -66,35 +67,35 @@ describe('TopNavMenu', () => { ]; it('Should render nothing when no config is provided', () => { - const component = shallowWithIntl(); + const component = mountWithIntl(); expect(component.find(WRAPPER_SELECTOR).length).toBe(0); expect(component.find(TOP_NAV_ITEM_SELECTOR).length).toBe(0); expect(component.find(SEARCH_BAR_SELECTOR).length).toBe(0); }); it('Should not render menu items when config is empty', () => { - const component = shallowWithIntl(); + const component = mountWithIntl(); expect(component.find(WRAPPER_SELECTOR).length).toBe(0); expect(component.find(TOP_NAV_ITEM_SELECTOR).length).toBe(0); expect(component.find(SEARCH_BAR_SELECTOR).length).toBe(0); }); it('Should render 1 menu item', () => { - const component = shallowWithIntl(); + const component = mountWithIntl(); expect(component.find(WRAPPER_SELECTOR).length).toBe(1); expect(component.find(TOP_NAV_ITEM_SELECTOR).length).toBe(1); expect(component.find(SEARCH_BAR_SELECTOR).length).toBe(0); }); it('Should render multiple menu items', () => { - const component = shallowWithIntl(); + const component = mountWithIntl(); expect(component.find(WRAPPER_SELECTOR).length).toBe(1); expect(component.find(TOP_NAV_ITEM_SELECTOR).length).toBe(menuItems.length); expect(component.find(SEARCH_BAR_SELECTOR).length).toBe(0); }); it('Should render search bar', () => { - const component = shallowWithIntl( + const component = mountWithIntl( ); expect(component.find(WRAPPER_SELECTOR).length).toBe(1); @@ -103,7 +104,7 @@ describe('TopNavMenu', () => { }); it('Should render menu items and search bar', () => { - const component = shallowWithIntl( + const component = mountWithIntl( { }); it('Should render with a class name', () => { - const component = shallowWithIntl( + const component = mountWithIntl( { className={'myCoolClass'} /> ); - expect(component.find('.kbnTopNavMenu').length).toBe(1); + expect(findTestSubject(component, 'top-nav').hasClass('kbnTopNavMenu')).toBe(true); expect(component.find('.myCoolClass').length).toBeTruthy(); }); diff --git a/src/plugins/navigation/public/top_nav_menu/top_nav_menu.tsx b/src/plugins/navigation/public/top_nav_menu/top_nav_menu.tsx index 9c899cd9d7207..56daf31fb0703 100644 --- a/src/plugins/navigation/public/top_nav_menu/top_nav_menu.tsx +++ b/src/plugins/navigation/public/top_nav_menu/top_nav_menu.tsx @@ -6,15 +6,7 @@ * Side Public License, v 1. */ -import React, { ReactElement, Fragment } from 'react'; -import { - EuiBadge, - EuiBadgeGroup, - EuiBadgeProps, - EuiHeaderLinks, - EuiToolTip, - EuiToolTipProps, -} from '@elastic/eui'; +import React, { ReactElement } from 'react'; import classNames from 'classnames'; import { MountPoint } from '@kbn/core/public'; @@ -23,13 +15,8 @@ import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/publi import { StatefulSearchBarProps } from '@kbn/unified-search-plugin/public'; import { AggregateQuery, Query } from '@kbn/es-query'; import { TopNavMenuData } from './top_nav_menu_data'; -import { TopNavMenuItem } from './top_nav_menu_item'; - -export type TopNavMenuBadgeProps = EuiBadgeProps & { - badgeText: string; - toolTipProps?: Partial; - renderCustomBadge?: (props: { badgeText: string }) => ReactElement; -}; +import { TopNavMenuItems } from './top_nav_menu_items'; +import { TopNavMenuBadgeProps, TopNavMenuBadges } from './top_nav_menu_badges'; export type TopNavMenuProps = Omit< StatefulSearchBarProps, @@ -83,54 +70,12 @@ export function TopNavMenu( return null; } - function createBadge( - { badgeText, toolTipProps, renderCustomBadge, ...badgeProps }: TopNavMenuBadgeProps, - i: number - ): ReactElement { - const key = `nav-menu-badge-${i}`; - - const Badge = () => ( - - {badgeText} - - ); - - if (renderCustomBadge) { - return {renderCustomBadge({ badgeText })}; - } - - return toolTipProps ? ( - - - - ) : ( - - ); - } - function renderBadges(): ReactElement | null { - if (!badges || badges.length === 0) return null; - return ( - - {badges.map(createBadge)} - - ); - } - - function renderItems(): ReactElement[] | null { - if (!config || config.length === 0) return null; - return config.map((menuItem: TopNavMenuData, i: number) => { - return ; - }); + return ; } function renderMenu(className: string): ReactElement | null { - if (!config || config.length === 0) return null; - return ( - - {renderItems()} - - ); + return ; } function renderSearchBar(): ReactElement | null { diff --git a/src/plugins/navigation/public/top_nav_menu/top_nav_menu_badges.tsx b/src/plugins/navigation/public/top_nav_menu/top_nav_menu_badges.tsx new file mode 100644 index 0000000000000..f1c3a08e58418 --- /dev/null +++ b/src/plugins/navigation/public/top_nav_menu/top_nav_menu_badges.tsx @@ -0,0 +1,48 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { EuiBadge, EuiBadgeGroup, EuiToolTip, EuiBadgeProps, EuiToolTipProps } from '@elastic/eui'; +import React, { Fragment, ReactElement } from 'react'; + +export type TopNavMenuBadgeProps = EuiBadgeProps & { + badgeText: string; + toolTipProps?: Partial; + renderCustomBadge?: (props: { badgeText: string }) => ReactElement; +}; + +export const TopNavMenuBadges = ({ badges }: { badges: TopNavMenuBadgeProps[] | undefined }) => { + if (!badges || badges.length === 0) return null; + return ( + {badges.map(createBadge)} + ); +}; + +function createBadge( + { badgeText, toolTipProps, renderCustomBadge, ...badgeProps }: TopNavMenuBadgeProps, + i: number +): ReactElement { + const key = `nav-menu-badge-${i}`; + + const Badge = () => ( + + {badgeText} + + ); + + if (renderCustomBadge) { + return {renderCustomBadge({ badgeText })}; + } + + return toolTipProps ? ( + + + + ) : ( + + ); +} diff --git a/src/plugins/navigation/public/top_nav_menu/top_nav_menu_items.tsx b/src/plugins/navigation/public/top_nav_menu/top_nav_menu_items.tsx new file mode 100644 index 0000000000000..3113bdb4c1ce7 --- /dev/null +++ b/src/plugins/navigation/public/top_nav_menu/top_nav_menu_items.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 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 { EuiHeaderLinks } from '@elastic/eui'; +import React from 'react'; +import type { TopNavMenuData } from './top_nav_menu_data'; +import { TopNavMenuItem } from './top_nav_menu_item'; + +export const TopNavMenuItems = ({ + config, + className, +}: { + config: TopNavMenuData[] | undefined; + className?: string; +}) => { + if (!config || config.length === 0) return null; + return ( + + {config.map((menuItem: TopNavMenuData, i: number) => { + return ; + })} + + ); +}; diff --git a/src/plugins/navigation/public/types.ts b/src/plugins/navigation/public/types.ts index 8958aa91d5ff6..9dea8415ca874 100644 --- a/src/plugins/navigation/public/types.ts +++ b/src/plugins/navigation/public/types.ts @@ -11,11 +11,11 @@ import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/publi import { TopNavMenuProps, TopNavMenuExtensionsRegistrySetup, createTopNav } from './top_nav_menu'; import { RegisteredTopNavMenuData } from './top_nav_menu/top_nav_menu_data'; -export interface NavigationPublicPluginSetup { +export interface NavigationPublicSetup { registerMenuItem: TopNavMenuExtensionsRegistrySetup['register']; } -export interface NavigationPublicPluginStart { +export interface NavigationPublicStart { ui: { TopNavMenu: (props: TopNavMenuProps) => React.ReactElement; AggregateQueryTopNavMenu: (props: TopNavMenuProps) => React.ReactElement; @@ -26,6 +26,9 @@ export interface NavigationPublicPluginStart { }; } -export interface NavigationPluginStartDependencies { +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface NavigationPublicSetupDependencies {} + +export interface NavigationPublicStartDependencies { unifiedSearch: UnifiedSearchPublicPluginStart; } diff --git a/src/plugins/no_data_page/public/index.ts b/src/plugins/no_data_page/public/index.ts index 28dfcd6044403..4d6c72c34ef85 100644 --- a/src/plugins/no_data_page/public/index.ts +++ b/src/plugins/no_data_page/public/index.ts @@ -13,4 +13,7 @@ export function plugin(ctx: PluginInitializerContext) { return new NoDataPagePlugin(ctx); } -export type { NoDataPagePluginSetup, NoDataPagePluginStart } from './types'; +export type { + NoDataPagePublicSetup as NoDataPagePluginSetup, + NoDataPagePublicStart as NoDataPagePluginStart, +} from './types'; diff --git a/src/plugins/no_data_page/public/plugin.ts b/src/plugins/no_data_page/public/plugin.ts index 740f796f4f395..a9f5dd0fe1f95 100644 --- a/src/plugins/no_data_page/public/plugin.ts +++ b/src/plugins/no_data_page/public/plugin.ts @@ -7,13 +7,26 @@ */ import type { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from '@kbn/core/public'; -import type { NoDataPagePluginSetup, NoDataPagePluginStart } from './types'; +import type { + NoDataPagePublicSetup, + NoDataPagePublicSetupDependencies, + NoDataPagePublicStart, + NoDataPagePublicStartDependencies, +} from './types'; import type { NoDataPageConfig } from '../config'; -export class NoDataPagePlugin implements Plugin { +export class NoDataPagePlugin + implements + Plugin< + NoDataPagePublicSetup, + NoDataPagePublicStart, + NoDataPagePublicSetupDependencies, + NoDataPagePublicStartDependencies + > +{ constructor(private initializerContext: PluginInitializerContext) {} - public setup(core: CoreSetup): NoDataPagePluginSetup { + public setup(_core: CoreSetup): NoDataPagePublicSetup { return { getAnalyticsNoDataPageFlavor: () => { return this.initializerContext.config.get().analyticsNoDataPageFlavor; @@ -21,7 +34,7 @@ export class NoDataPagePlugin implements Plugin { }; } - public start(core: CoreStart): NoDataPagePluginStart { + public start(_core: CoreStart): NoDataPagePublicStart { return { getAnalyticsNoDataPageFlavor: () => { return this.initializerContext.config.get().analyticsNoDataPageFlavor; diff --git a/src/plugins/no_data_page/public/types.ts b/src/plugins/no_data_page/public/types.ts index 2e33170ec06bf..43761d946af87 100644 --- a/src/plugins/no_data_page/public/types.ts +++ b/src/plugins/no_data_page/public/types.ts @@ -6,8 +6,14 @@ * Side Public License, v 1. */ -export interface NoDataPagePluginSetup { +export interface NoDataPagePublicSetup { getAnalyticsNoDataPageFlavor: () => 'kibana' | 'serverless_search' | 'serverless_observability'; } -export type NoDataPagePluginStart = NoDataPagePluginSetup; +export type NoDataPagePublicStart = NoDataPagePublicSetup; + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface NoDataPagePublicSetupDependencies {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface NoDataPagePublicStartDependencies {} diff --git a/src/plugins/presentation_util/public/types.ts b/src/plugins/presentation_util/public/types.ts index 589d4101bdf80..dfc81ad4a0a27 100644 --- a/src/plugins/presentation_util/public/types.ts +++ b/src/plugins/presentation_util/public/types.ts @@ -8,7 +8,7 @@ import { ContentManagementPublicStart } from '@kbn/content-management-plugin/public'; import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; -import { UiActionsStart } from '@kbn/ui-actions-plugin/public/plugin'; +import { UiActionsStart } from '@kbn/ui-actions-plugin/public'; import { registerExpressionsLanguage } from '.'; import { PresentationLabsService } from './services/labs/types'; diff --git a/src/plugins/saved_objects_management/public/management_section/object_view/__snapshots__/saved_object_view.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/object_view/__snapshots__/saved_object_view.test.tsx.snap index 538755e8d677d..055d065982a77 100644 --- a/src/plugins/saved_objects_management/public/management_section/object_view/__snapshots__/saved_object_view.test.tsx.snap +++ b/src/plugins/saved_objects_management/public/management_section/object_view/__snapshots__/saved_object_view.test.tsx.snap @@ -35,6 +35,7 @@ exports[`SavedObjectEdition should render normally 1`] = ` }, }, "theme": Object { + "getTheme": [MockFunction], "theme$": Observable { "_subscribe": [Function], }, diff --git a/src/plugins/saved_search/common/types.ts b/src/plugins/saved_search/common/types.ts index c47548aebd8d4..acb98d26a0d14 100644 --- a/src/plugins/saved_search/common/types.ts +++ b/src/plugins/saved_search/common/types.ts @@ -9,13 +9,14 @@ import type { ISearchSource, RefreshInterval, TimeRange } from '@kbn/data-plugin/common'; import type { SavedObjectReference } from '@kbn/core-saved-objects-server'; import type { SavedObjectsResolveResponse } from '@kbn/core/server'; +import type { SerializableRecord } from '@kbn/utility-types'; import { VIEW_MODE } from '.'; -export interface DiscoverGridSettings { +export interface DiscoverGridSettings extends SerializableRecord { columns?: Record; } -export interface DiscoverGridSettingsColumn { +export interface DiscoverGridSettingsColumn extends SerializableRecord { width?: number; } @@ -25,9 +26,7 @@ export interface SavedSearchAttributes { sort: Array<[string, string]>; columns: string[]; description: string; - grid: { - columns?: Record; - }; + grid: DiscoverGridSettings; hideChart: boolean; isTextBasedQuery: boolean; usesAdHocDataView?: boolean; @@ -59,9 +58,7 @@ export interface SavedSearch { columns?: string[]; description?: string; tags?: string[] | undefined; - grid?: { - columns?: Record; - }; + grid?: DiscoverGridSettings; hideChart?: boolean; viewMode?: VIEW_MODE; hideAggregatedPreview?: boolean; diff --git a/src/plugins/saved_search/tsconfig.json b/src/plugins/saved_search/tsconfig.json index 7ed2cb4e82119..b1aa1679469ee 100644 --- a/src/plugins/saved_search/tsconfig.json +++ b/src/plugins/saved_search/tsconfig.json @@ -31,6 +31,7 @@ "@kbn/discover-utils", "@kbn/logging", "@kbn/core-plugins-server", + "@kbn/utility-types", ], "exclude": [ "target/**/*", diff --git a/src/plugins/screenshot_mode/public/index.ts b/src/plugins/screenshot_mode/public/index.ts index 591ddbfdf49c5..9a5b0203fdcdc 100644 --- a/src/plugins/screenshot_mode/public/index.ts +++ b/src/plugins/screenshot_mode/public/index.ts @@ -18,4 +18,7 @@ export { KBN_SCREENSHOT_MODE_ENABLED_KEY, } from '../common'; -export type { ScreenshotModePluginSetup, ScreenshotModePluginStart } from './types'; +export type { + ScreenshotModePublicSetup as ScreenshotModePluginSetup, + ScreenshotModePublicStart as ScreenshotModePluginStart, +} from './types'; diff --git a/src/plugins/screenshot_mode/public/mocks.ts b/src/plugins/screenshot_mode/public/mocks.ts index 1d3e226a83b6b..4f7157b8ae84e 100644 --- a/src/plugins/screenshot_mode/public/mocks.ts +++ b/src/plugins/screenshot_mode/public/mocks.ts @@ -7,14 +7,14 @@ */ import type { DeeplyMockedKeys } from '@kbn/utility-types-jest'; -import type { ScreenshotModePluginSetup, ScreenshotModePluginStart } from './types'; +import type { ScreenshotModePublicSetup, ScreenshotModePublicStart } from './types'; export const screenshotModePluginMock = { - createSetupContract: (): DeeplyMockedKeys => ({ + createSetupContract: (): DeeplyMockedKeys => ({ getScreenshotContext: jest.fn(), isScreenshotMode: jest.fn(() => false), }), - createStartContract: (): DeeplyMockedKeys => ({ + createStartContract: (): DeeplyMockedKeys => ({ getScreenshotContext: jest.fn(), isScreenshotMode: jest.fn(() => false), }), diff --git a/src/plugins/screenshot_mode/public/plugin.ts b/src/plugins/screenshot_mode/public/plugin.ts index 2008e46e9b2d6..aa55049128a05 100644 --- a/src/plugins/screenshot_mode/public/plugin.ts +++ b/src/plugins/screenshot_mode/public/plugin.ts @@ -8,19 +8,32 @@ import type { CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; import { getScreenshotContext, getScreenshotMode } from '../common'; -import type { ScreenshotModePluginSetup, ScreenshotModePluginStart } from './types'; +import type { + ScreenshotModePublicSetup, + ScreenshotModePublicSetupDependencies, + ScreenshotModePublicStart, + ScreenshotModePublicStartDependencies, +} from './types'; -export class ScreenshotModePlugin implements Plugin { +export class ScreenshotModePlugin + implements + Plugin< + ScreenshotModePublicSetup, + ScreenshotModePublicStart, + ScreenshotModePublicSetupDependencies, + ScreenshotModePublicStartDependencies + > +{ private publicContract = Object.freeze({ getScreenshotContext, isScreenshotMode: () => getScreenshotMode() === true, }); - public setup(core: CoreSetup): ScreenshotModePluginSetup { + public setup(_core: CoreSetup): ScreenshotModePublicSetup { return this.publicContract; } - public start(core: CoreStart): ScreenshotModePluginStart { + public start(_core: CoreStart): ScreenshotModePublicStart { return this.publicContract; } diff --git a/src/plugins/screenshot_mode/public/types.ts b/src/plugins/screenshot_mode/public/types.ts index 9de538bc9f8fb..b25a4a0f35dcb 100644 --- a/src/plugins/screenshot_mode/public/types.ts +++ b/src/plugins/screenshot_mode/public/types.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -export interface ScreenshotModePluginSetup { +export interface ScreenshotModePublicSetup { /** * Retrieves a value from the screenshotting context. * @param key Context key to get. @@ -22,4 +22,10 @@ export interface ScreenshotModePluginSetup { isScreenshotMode(): boolean; } -export type ScreenshotModePluginStart = ScreenshotModePluginSetup; +export type ScreenshotModePublicStart = ScreenshotModePublicSetup; + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ScreenshotModePublicSetupDependencies {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ScreenshotModePublicStartDependencies {} diff --git a/src/plugins/screenshot_mode/server/index.ts b/src/plugins/screenshot_mode/server/index.ts index 55dfd3967f4e4..cde13072badc3 100644 --- a/src/plugins/screenshot_mode/server/index.ts +++ b/src/plugins/screenshot_mode/server/index.ts @@ -14,8 +14,8 @@ export { export type { ScreenshotModeRequestHandlerContext, - ScreenshotModePluginSetup, - ScreenshotModePluginStart, + ScreenshotModeServerSetup as ScreenshotModePluginSetup, + ScreenshotModeServerStart as ScreenshotModePluginStart, } from './types'; export async function plugin() { diff --git a/src/plugins/screenshot_mode/server/plugin.ts b/src/plugins/screenshot_mode/server/plugin.ts index fc900170f7a83..4a2e05ff78740 100644 --- a/src/plugins/screenshot_mode/server/plugin.ts +++ b/src/plugins/screenshot_mode/server/plugin.ts @@ -9,15 +9,23 @@ import type { Plugin, CoreSetup } from '@kbn/core/server'; import type { ScreenshotModeRequestHandlerContext, - ScreenshotModePluginSetup, - ScreenshotModePluginStart, + ScreenshotModeServerSetup, + ScreenshotModeServerStart, + ScreenshotModeServerSetupDependencies, + ScreenshotModeServerStartDependencies, } from './types'; import { isScreenshotMode } from './is_screenshot_mode'; export class ScreenshotModePlugin - implements Plugin + implements + Plugin< + ScreenshotModeServerSetup, + ScreenshotModeServerStart, + ScreenshotModeServerSetupDependencies, + ScreenshotModeServerStartDependencies + > { - public setup(core: CoreSetup): ScreenshotModePluginSetup { + public setup(core: CoreSetup): ScreenshotModeServerSetup { core.http.registerRouteHandlerContext( 'screenshotMode', (ctx, req) => { @@ -39,7 +47,7 @@ export class ScreenshotModePlugin }; } - public start(): ScreenshotModePluginStart { + public start(): ScreenshotModeServerStart { return { isScreenshotMode, }; diff --git a/src/plugins/screenshot_mode/server/types.ts b/src/plugins/screenshot_mode/server/types.ts index 08655d2f0118d..15d4afb4ace3a 100644 --- a/src/plugins/screenshot_mode/server/types.ts +++ b/src/plugins/screenshot_mode/server/types.ts @@ -8,7 +8,7 @@ import type { CustomRequestHandlerContext, KibanaRequest } from '@kbn/core/server'; -export interface ScreenshotModePluginStart { +export interface ScreenshotModeServerStart { /** * Any context that requires access to the screenshot mode flag but does not have access * to request context {@link ScreenshotModeRequestHandlerContext}, for instance if they are pre-context, @@ -17,7 +17,7 @@ export interface ScreenshotModePluginStart { isScreenshotMode(request: KibanaRequest): boolean; } -export interface ScreenshotModePluginSetup extends ScreenshotModePluginStart { +export interface ScreenshotModeServerSetup extends ScreenshotModeServerStart { /** * Stores a value in the screenshotting context. * @param key Context key to set. @@ -32,6 +32,12 @@ export interface ScreenshotModePluginSetup extends ScreenshotModePluginStart { setScreenshotModeEnabled(): void; } +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ScreenshotModeServerSetupDependencies {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ScreenshotModeServerStartDependencies {} + export type ScreenshotModeRequestHandlerContext = CustomRequestHandlerContext<{ screenshotMode: { isScreenshot: boolean; diff --git a/src/plugins/share/public/index.ts b/src/plugins/share/public/index.ts index d33e4110ff3a4..d01e7df655f51 100644 --- a/src/plugins/share/public/index.ts +++ b/src/plugins/share/public/index.ts @@ -12,7 +12,10 @@ export { CSV_QUOTE_VALUES_SETTING, CSV_SEPARATOR_SETTING } from '../common/const export type { LocatorDefinition, LocatorPublic, KibanaLocation } from '../common/url_service'; -export type { SharePluginSetup, SharePluginStart } from './plugin'; +export type { + SharePublicSetup as SharePluginSetup, + SharePublicStart as SharePluginStart, +} from './plugin'; export type { ShareContext, diff --git a/src/plugins/share/public/mocks.ts b/src/plugins/share/public/mocks.ts index 7406dbb12f883..f7fd6b8aa0734 100644 --- a/src/plugins/share/public/mocks.ts +++ b/src/plugins/share/public/mocks.ts @@ -7,13 +7,13 @@ */ import { SerializableRecord } from '@kbn/utility-types'; -import { SharePluginSetup, SharePluginStart } from '.'; +import { SharePublicSetup, SharePublicStart } from './plugin'; import { LocatorPublic, UrlService } from '../common/url_service'; import { BrowserShortUrlClient } from './url_service/short_urls/short_url_client'; import type { BrowserShortUrlClientFactoryCreateParams } from './url_service/short_urls/short_url_client_factory'; -export type Setup = jest.Mocked; -export type Start = jest.Mocked; +export type Setup = jest.Mocked; +export type Start = jest.Mocked; const url = new UrlService({ navigate: async () => {}, diff --git a/src/plugins/share/public/plugin.ts b/src/plugins/share/public/plugin.ts index 6a04e4cd1b12c..41666a692cb90 100644 --- a/src/plugins/share/public/plugin.ts +++ b/src/plugins/share/public/plugin.ts @@ -26,7 +26,7 @@ import { registrations } from './lib/registrations'; import type { BrowserUrlService } from './types'; /** @public */ -export type SharePluginSetup = ShareMenuRegistrySetup & { +export type SharePublicSetup = ShareMenuRegistrySetup & { /** * Utilities to work with URL locators and short URLs. */ @@ -45,7 +45,7 @@ export type SharePluginSetup = ShareMenuRegistrySetup & { }; /** @public */ -export type SharePluginStart = ShareMenuManagerStart & { +export type SharePublicStart = ShareMenuManagerStart & { /** * Utilities to work with URL locators and short URLs. */ @@ -58,7 +58,21 @@ export type SharePluginStart = ShareMenuManagerStart & { navigate(options: RedirectOptions): void; }; -export class SharePlugin implements Plugin { +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface SharePublicSetupDependencies {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface SharePublicStartDependencies {} + +export class SharePlugin + implements + Plugin< + SharePublicSetup, + SharePublicStart, + SharePublicSetupDependencies, + SharePublicStartDependencies + > +{ private readonly shareMenuRegistry = new ShareMenuRegistry(); private readonly shareContextMenu = new ShareMenuManager(); @@ -68,7 +82,7 @@ export class SharePlugin implements Plugin { constructor(private readonly initializerContext: PluginInitializerContext) {} - public setup(core: CoreSetup): SharePluginSetup { + public setup(core: CoreSetup): SharePublicSetup { const { analytics, http } = core; const { basePath } = http; @@ -122,7 +136,7 @@ export class SharePlugin implements Plugin { }; } - public start(core: CoreStart): SharePluginStart { + public start(core: CoreStart): SharePublicStart { const disableEmbed = this.initializerContext.env.packageInfo.buildFlavor === 'serverless'; const sharingContextMenuStart = this.shareContextMenu.start( core, diff --git a/src/plugins/share/server/index.ts b/src/plugins/share/server/index.ts index d38575a3b7cd4..3105e5f57282f 100644 --- a/src/plugins/share/server/index.ts +++ b/src/plugins/share/server/index.ts @@ -8,7 +8,10 @@ import { PluginInitializerContext } from '@kbn/core/server'; -export type { SharePluginSetup, SharePluginStart } from './plugin'; +export type { + SharePublicSetup as SharePluginSetup, + SharePublicStart as SharePluginStart, +} from './plugin'; export { CSV_QUOTE_VALUES_SETTING, CSV_SEPARATOR_SETTING } from '../common/constants'; diff --git a/src/plugins/share/server/plugin.ts b/src/plugins/share/server/plugin.ts index 437257704501c..228a1fc09b1f9 100644 --- a/src/plugins/share/server/plugin.ts +++ b/src/plugins/share/server/plugin.ts @@ -21,16 +21,30 @@ import { LegacyShortUrlLocatorDefinition } from '../common/url_service/locators/ import { ShortUrlRedirectLocatorDefinition } from '../common/url_service/locators/short_url_redirect_locator'; /** @public */ -export interface SharePluginSetup { +export interface SharePublicSetup { url: ServerUrlService; } /** @public */ -export interface SharePluginStart { +export interface SharePublicStart { url: ServerUrlService; } -export class SharePlugin implements Plugin { +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface SharePublicSetupDependencies {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface SharePublicStartDependencies {} + +export class SharePlugin + implements + Plugin< + SharePublicSetup, + SharePublicStart, + SharePublicSetupDependencies, + SharePublicStartDependencies + > +{ private url?: ServerUrlService; private version: string; diff --git a/src/plugins/telemetry/schema/oss_plugins.json b/src/plugins/telemetry/schema/oss_plugins.json index 8c2a7b13d52ca..ec17c9b9d1a3b 100644 --- a/src/plugins/telemetry/schema/oss_plugins.json +++ b/src/plugins/telemetry/schema/oss_plugins.json @@ -10061,6 +10061,12 @@ "description": "Non-default value of setting." } }, + "observability:enableInfrastructureProfilingIntegration": { + "type": "boolean", + "_meta": { + "description": "Non-default value of setting." + } + }, "securitySolution:enableGroupedNav": { "type": "boolean", "_meta": { diff --git a/src/plugins/ui_actions/kibana.jsonc b/src/plugins/ui_actions/kibana.jsonc index 396f5b82faf01..66ccaa6917d0b 100644 --- a/src/plugins/ui_actions/kibana.jsonc +++ b/src/plugins/ui_actions/kibana.jsonc @@ -7,9 +7,7 @@ "id": "uiActions", "server": false, "browser": true, - "requiredPlugins": [ - "dataViews" - ], + "requiredPlugins": [], "requiredBundles": [ "kibanaUtils", "kibanaReact" diff --git a/src/plugins/ui_actions/public/index.ts b/src/plugins/ui_actions/public/index.ts index d996b6b4e2cdc..37f92d6962d48 100644 --- a/src/plugins/ui_actions/public/index.ts +++ b/src/plugins/ui_actions/public/index.ts @@ -13,7 +13,10 @@ export function plugin(initializerContext: PluginInitializerContext) { return new UiActionsPlugin(initializerContext); } -export type { UiActionsSetup, UiActionsStart } from './plugin'; +export type { + UiActionsPublicSetup as UiActionsSetup, + UiActionsPublicStart as UiActionsStart, +} from './plugin'; export type { UiActionsServiceParams } from './service'; export { UiActionsService } from './service'; export type { Action, ActionDefinition as UiActionsActionDefinition } from './actions'; diff --git a/src/plugins/ui_actions/public/mocks.ts b/src/plugins/ui_actions/public/mocks.ts index 57474bbf6ff3b..c184e6e021122 100644 --- a/src/plugins/ui_actions/public/mocks.ts +++ b/src/plugins/ui_actions/public/mocks.ts @@ -8,11 +8,12 @@ import { CoreSetup, CoreStart } from '@kbn/core/public'; import { coreMock } from '@kbn/core/public/mocks'; -import { Action, UiActionsSetup, UiActionsStart } from '.'; +import { Action } from '.'; +import { UiActionsPublicSetup, UiActionsPublicStart } from './plugin'; import { plugin as pluginInitializer } from '.'; -export type Setup = jest.Mocked; -export type Start = jest.Mocked; +export type Setup = jest.Mocked; +export type Start = jest.Mocked; const createSetupContract = (): Setup => { const setupContract: Setup = { diff --git a/src/plugins/ui_actions/public/plugin.ts b/src/plugins/ui_actions/public/plugin.ts index 16060d4bf3435..1a0e45612c831 100644 --- a/src/plugins/ui_actions/public/plugin.ts +++ b/src/plugins/ui_actions/public/plugin.ts @@ -16,7 +16,7 @@ import { import { UiActionsService } from './service'; import { setTheme } from './services'; -export type UiActionsSetup = Pick< +export type UiActionsPublicSetup = Pick< UiActionsService, | 'addTriggerAction' | 'attachAction' @@ -26,14 +26,28 @@ export type UiActionsSetup = Pick< | 'unregisterAction' >; -export type UiActionsStart = PublicMethodsOf; +export type UiActionsPublicStart = PublicMethodsOf; -export class UiActionsPlugin implements Plugin { +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface UiActionsPublicSetupDependencies {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface UiActionsPublicStartDependencies {} + +export class UiActionsPlugin + implements + Plugin< + UiActionsPublicSetup, + UiActionsPublicStart, + UiActionsPublicSetupDependencies, + UiActionsPublicStartDependencies + > +{ private readonly service = new UiActionsService(); - constructor(initializerContext: PluginInitializerContext) {} + constructor(_initializerContext: PluginInitializerContext) {} - public setup(core: CoreSetup): UiActionsSetup { + public setup(core: CoreSetup): UiActionsPublicSetup { setTheme(core.theme); this.service.registerTrigger(rowClickTrigger); this.service.registerTrigger(visualizeFieldTrigger); @@ -41,7 +55,7 @@ export class UiActionsPlugin implements Plugin { return this.service; } - public start(core: CoreStart): UiActionsStart { + public start(_core: CoreStart): UiActionsPublicStart { return this.service; } diff --git a/src/plugins/ui_actions_enhanced/server/index.ts b/src/plugins/ui_actions_enhanced/server/index.ts index bb62e2b86a19c..51076054e2eb0 100644 --- a/src/plugins/ui_actions_enhanced/server/index.ts +++ b/src/plugins/ui_actions_enhanced/server/index.ts @@ -13,8 +13,8 @@ export async function plugin() { export type { AdvancedUiActionsServerPlugin as Plugin, - SetupContract as AdvancedUiActionsSetup, - StartContract as AdvancedUiActionsStart, + UiActionsEnhancedServerSetup as AdvancedUiActionsSetup, + UiActionsEnhancedServerStart as AdvancedUiActionsStart, } from './plugin'; export type { diff --git a/src/plugins/ui_actions_enhanced/server/plugin.ts b/src/plugins/ui_actions_enhanced/server/plugin.ts index 272657ec84f0b..8d5c12c4312c4 100644 --- a/src/plugins/ui_actions_enhanced/server/plugin.ts +++ b/src/plugins/ui_actions_enhanced/server/plugin.ts @@ -12,24 +12,33 @@ import { EmbeddableSetup } from '@kbn/embeddable-plugin/server'; import { dynamicActionEnhancement } from './dynamic_action_enhancement'; import { ActionFactoryRegistry, SerializedEvent, ActionFactoryDefinition } from './types'; -export interface SetupContract { +export interface UiActionsEnhancedServerSetup { registerActionFactory: (definition: ActionFactoryDefinition) => void; } -export type StartContract = void; +export type UiActionsEnhancedServerStart = void; -interface SetupDependencies { +interface UiActionsEnhancedServerSetupDependencies { embeddable: EmbeddableSetup; // Embeddable are needed because they register basic triggers/actions. } +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface UiActionsEnhancedServerStartDependencies {} + export class AdvancedUiActionsServerPlugin - implements Plugin + implements + Plugin< + UiActionsEnhancedServerSetup, + UiActionsEnhancedServerStart, + UiActionsEnhancedServerSetupDependencies, + UiActionsEnhancedServerStartDependencies + > { protected readonly actionFactories: ActionFactoryRegistry = new Map(); constructor() {} - public setup(core: CoreSetup, { embeddable }: SetupDependencies) { + public setup(_core: CoreSetup, { embeddable }: UiActionsEnhancedServerSetupDependencies) { const getActionFactory = (actionFactoryId: string) => this.actionFactories.get(actionFactoryId); embeddable.registerEnhancement(dynamicActionEnhancement(getActionFactory)); diff --git a/src/plugins/unified_doc_viewer/public/__mocks__/services.ts b/src/plugins/unified_doc_viewer/public/__mocks__/services.ts index c43b8daa86727..a101104e2283d 100644 --- a/src/plugins/unified_doc_viewer/public/__mocks__/services.ts +++ b/src/plugins/unified_doc_viewer/public/__mocks__/services.ts @@ -12,9 +12,10 @@ import { fieldFormatsMock } from '@kbn/field-formats-plugin/common/mocks'; import { uiSettingsServiceMock } from '@kbn/core-ui-settings-browser-mocks'; import type { UnifiedDocViewerServices, UnifiedDocViewerStart } from '../types'; import { Storage } from '@kbn/kibana-utils-plugin/public'; +import { DocViewsRegistry } from '@kbn/unified-doc-viewer'; export const mockUnifiedDocViewer: jest.Mocked = { - getDocViews: jest.fn().mockReturnValue([]), + registry: new DocViewsRegistry(), }; export const mockUnifiedDocViewerServices: jest.Mocked = { diff --git a/src/plugins/unified_doc_viewer/public/components/doc_viewer/doc_viewer.tsx b/src/plugins/unified_doc_viewer/public/components/doc_viewer/doc_viewer.tsx index 929ef2aa1b0c4..5358808beec39 100644 --- a/src/plugins/unified_doc_viewer/public/components/doc_viewer/doc_viewer.tsx +++ b/src/plugins/unified_doc_viewer/public/components/doc_viewer/doc_viewer.tsx @@ -6,12 +6,22 @@ * Side Public License, v 1. */ -import React from 'react'; +import React, { useMemo } from 'react'; import type { DocViewRenderProps } from '@kbn/unified-doc-viewer/types'; import { DocViewer } from '@kbn/unified-doc-viewer'; import { getUnifiedDocViewerServices } from '../../plugin'; -export function UnifiedDocViewer(props: DocViewRenderProps) { +export function UnifiedDocViewer({ docViewsRegistry, ...props }: DocViewRenderProps) { const { unifiedDocViewer } = getUnifiedDocViewerServices(); - return ; + + const registry = useMemo(() => { + if (docViewsRegistry) { + return typeof docViewsRegistry === 'function' + ? docViewsRegistry(unifiedDocViewer.registry.clone()) + : docViewsRegistry; + } + return unifiedDocViewer.registry; + }, [docViewsRegistry, unifiedDocViewer.registry]); + + return ; } diff --git a/src/plugins/unified_doc_viewer/public/plugin.tsx b/src/plugins/unified_doc_viewer/public/plugin.tsx index a79ae9e44d0ca..4b3793872b454 100644 --- a/src/plugins/unified_doc_viewer/public/plugin.tsx +++ b/src/plugins/unified_doc_viewer/public/plugin.tsx @@ -26,11 +26,11 @@ const DocViewerTable = React.lazy(() => import('./components/doc_viewer_table')) const SourceViewer = React.lazy(() => import('./components/doc_viewer_source')); export interface UnifiedDocViewerSetup { - addDocView: DocViewsRegistry['addDocView']; + registry: DocViewsRegistry; } export interface UnifiedDocViewerStart { - getDocViews: DocViewsRegistry['getDocViewsSorted']; + registry: DocViewsRegistry; } export interface UnifiedDocViewerStartDeps { @@ -44,7 +44,8 @@ export class UnifiedDocViewerPublicPlugin private docViewsRegistry = new DocViewsRegistry(); public setup(core: CoreSetup) { - this.docViewsRegistry.addDocView({ + this.docViewsRegistry.add({ + id: 'doc_view_table', title: i18n.translate('unifiedDocViewer.docViews.table.tableTitle', { defaultMessage: 'Table', }), @@ -67,12 +68,13 @@ export class UnifiedDocViewerPublicPlugin }, }); - this.docViewsRegistry.addDocView({ + this.docViewsRegistry.add({ + id: 'doc_view_source', title: i18n.translate('unifiedDocViewer.docViews.json.jsonTitle', { defaultMessage: 'JSON', }), order: 20, - component: ({ hit, dataView, query, textBasedHits }) => { + component: ({ hit, dataView, textBasedHits }) => { return ( , diff --git a/src/plugins/unified_search/public/query_string_input/no_data_popover.tsx b/src/plugins/unified_search/public/query_string_input/no_data_popover.tsx index 7e9760486bb93..8fde7a52e6bd5 100644 --- a/src/plugins/unified_search/public/query_string_input/no_data_popover.tsx +++ b/src/plugins/unified_search/public/query_string_input/no_data_popover.tsx @@ -67,7 +67,6 @@ export function NoDataPopover({ } minWidth={300} anchorPosition="downCenter" - anchorClassName="eui-displayBlock" step={1} stepsTotal={1} isStepOpen={noDataPopoverVisible} diff --git a/src/plugins/unified_search/public/query_string_input/query_bar.scss b/src/plugins/unified_search/public/query_string_input/query_bar.scss index 89cca695ee154..2ecf27416a907 100644 --- a/src/plugins/unified_search/public/query_string_input/query_bar.scss +++ b/src/plugins/unified_search/public/query_string_input/query_bar.scss @@ -1,13 +1,9 @@ .kbnQueryBar__datePickerWrapper { .euiDatePopoverButton-isInvalid { background-image: euiFormControlGradient($euiColorDanger); - - // @todo Remove when EUI issue is resolved. - // @see https://github.com/elastic/eui/issues/4612 - &:focus { - color: $euiTextColor; - background-color: $euiFormBackgroundColor; - background-image: euiFormControlGradient($euiColorPrimary); - } } } +// increase the section height to avoid vertical scrolling +.euiQuickSelectPopover__section { + max-height: 142px; +} diff --git a/src/plugins/vis_types/table/public/components/table_vis_basic.tsx b/src/plugins/vis_types/table/public/components/table_vis_basic.tsx index f787365dfcdae..105dc8791976e 100644 --- a/src/plugins/vis_types/table/public/components/table_vis_basic.tsx +++ b/src/plugins/vis_types/table/public/components/table_vis_basic.tsx @@ -15,14 +15,13 @@ import { EuiTitle, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { orderBy } from 'lodash'; - import { IInterpreterRenderHandlers } from '@kbn/expressions-plugin/common'; import { createTableVisCell } from './table_vis_cell'; import { TableContext, TableVisConfig, TableVisUseUiStateProps } from '../types'; import { usePagination } from '../utils'; import { TableVisControls } from './table_vis_controls'; import { createGridColumns } from './table_vis_columns'; +import { sortNullsLast } from './utils'; interface TableVisBasicProps { fireEvent: IInterpreterRenderHandlers['event']; @@ -45,13 +44,14 @@ export const TableVisBasic = memo( const { columns, rows, formattedColumns } = table; // custom sorting is in place until the EuiDataGrid sorting gets rid of flaws -> https://github.com/elastic/eui/issues/4108 - const sortedRows = useMemo( - () => - sort.columnIndex !== null && sort.direction - ? orderBy(rows, columns[sort.columnIndex]?.id, sort.direction) - : rows, - [columns, rows, sort] - ); + const sortedRows = useMemo(() => { + if (sort.columnIndex !== null && sort.direction) { + const id = columns[sort.columnIndex]?.id; + return sortNullsLast(rows, sort.direction, id); + } + + return rows; + }, [columns, rows, sort.columnIndex, sort.direction]); // renderCellValue is a component which renders a cell based on column and row indexes const renderCellValue = useMemo( diff --git a/src/plugins/vis_types/table/public/components/utils.test.ts b/src/plugins/vis_types/table/public/components/utils.test.ts new file mode 100644 index 0000000000000..0b81f70a5fe41 --- /dev/null +++ b/src/plugins/vis_types/table/public/components/utils.test.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 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 { sortNullsLast } from './utils'; + +describe('sortNullsLast', () => { + const rows = [ + { + col1: null, + }, + { + col1: 'meow', + }, + { + col1: 'woof', + }, + ]; + test('should sort correctly in ascending order', async () => { + const sortedRows = sortNullsLast(rows, 'asc', 'col1'); + expect(sortedRows).toStrictEqual([ + { + col1: 'meow', + }, + { + col1: 'woof', + }, + { + col1: null, + }, + ]); + }); + + test('should sort correctly in descending order', async () => { + const sortedRows = sortNullsLast(rows, 'desc', 'col1'); + expect(sortedRows).toStrictEqual([ + { + col1: 'woof', + }, + { + col1: 'meow', + }, + { + col1: null, + }, + ]); + }); +}); diff --git a/src/plugins/vis_types/table/public/components/utils.ts b/src/plugins/vis_types/table/public/components/utils.ts index 4bba926099626..6dc89d483d2d4 100644 --- a/src/plugins/vis_types/table/public/components/utils.ts +++ b/src/plugins/vis_types/table/public/components/utils.ts @@ -7,6 +7,7 @@ */ import { i18n } from '@kbn/i18n'; +import type { DatatableRow } from '@kbn/expressions-plugin/common'; import { AggTypes } from '../../common'; const totalAggregations = [ @@ -42,4 +43,31 @@ const totalAggregations = [ }, ]; -export { totalAggregations }; +const sortNullsLast = ( + rows: DatatableRow[], + direction: 'asc' | 'desc', + id: string +): DatatableRow[] => { + return rows.sort((row1, row2) => { + const rowA = row1[id]; + const rowB = row2[id]; + + if (rowA === null) { + return 1; + } + if (rowB === null) { + return -1; + } + if (rowA === rowB) { + return 0; + } + + if (direction === 'desc') { + return rowA < rowB ? 1 : -1; + } else { + return rowA < rowB ? -1 : 1; + } + }); +}; + +export { totalAggregations, sortNullsLast }; diff --git a/src/plugins/vis_types/timelion/public/components/timelion_vis_component.tsx b/src/plugins/vis_types/timelion/public/components/timelion_vis_component.tsx index 9eb41c09394d9..983a2ec74c12a 100644 --- a/src/plugins/vis_types/timelion/public/components/timelion_vis_component.tsx +++ b/src/plugins/vis_types/timelion/public/components/timelion_vis_component.tsx @@ -113,8 +113,6 @@ export const TimelionVisComponent = ({ const chartRef = useRef(null); const chart = seriesList.list; const chartsService = getCharts(); - - const chartTheme = chartsService.theme.useChartsTheme(); const chartBaseTheme = chartsService.theme.useChartsBaseTheme(); const handleCursorUpdate = useActiveCursor(chartsService.activeCursor, chartRef, { @@ -216,7 +214,6 @@ export const TimelionVisComponent = ({ externalPointerEvents={{ tooltip: { visible: syncTooltips, placement: Placement.Right }, }} - theme={chartTheme} baseTheme={chartBaseTheme} ariaLabel={ariaLabel} ariaUseDefaultSummary={!ariaLabel} diff --git a/src/plugins/vis_types/timelion/public/helpers/get_timezone.ts b/src/plugins/vis_types/timelion/public/helpers/get_timezone.ts deleted file mode 100644 index 44fc531e77b78..0000000000000 --- a/src/plugins/vis_types/timelion/public/helpers/get_timezone.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 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 moment from 'moment-timezone'; -import { IUiSettingsClient } from '@kbn/core/public'; - -export function getTimezone(config: IUiSettingsClient) { - if (config.isDefault('dateFormat:tz')) { - const detectedTimezone = moment.tz.guess(); - if (detectedTimezone) return detectedTimezone; - else return moment().format('Z'); - } else { - return config.get('dateFormat:tz', 'Browser'); - } -} diff --git a/src/plugins/vis_types/timelion/public/helpers/timelion_request_handler.ts b/src/plugins/vis_types/timelion/public/helpers/timelion_request_handler.ts index 283998460111f..499758a5c6cf7 100644 --- a/src/plugins/vis_types/timelion/public/helpers/timelion_request_handler.ts +++ b/src/plugins/vis_types/timelion/public/helpers/timelion_request_handler.ts @@ -11,8 +11,8 @@ import type { KibanaExecutionContext } from '@kbn/core/public'; import { DataView } from '@kbn/data-plugin/common'; import { Filter, buildEsQuery, TimeRange, Query } from '@kbn/es-query'; import { KibanaContext, getEsQueryConfig } from '@kbn/data-plugin/public'; +import { getTimeZone } from '@kbn/visualization-utils'; import { TimelionVisDependencies } from '../plugin'; -import { getTimezone } from './get_timezone'; import { TimelionVisParams } from '../timelion_vis_fn'; import { getDataSearch, getIndexPatterns } from './plugin_services'; import { VisSeries } from '../../common/vis_data'; @@ -58,7 +58,7 @@ export function getTimelionRequestHandler({ }: TimelionVisDependencies & { expressionAbortSignal: AbortSignal; }) { - const timezone = getTimezone(uiSettings); + const timezone = getTimeZone(uiSettings); return async function ({ timeRange, diff --git a/src/plugins/vis_types/timelion/tsconfig.json b/src/plugins/vis_types/timelion/tsconfig.json index 7121a7d7078be..b6581ddddb159 100644 --- a/src/plugins/vis_types/timelion/tsconfig.json +++ b/src/plugins/vis_types/timelion/tsconfig.json @@ -31,6 +31,7 @@ "@kbn/expect", "@kbn/std", "@kbn/timelion-grammar", + "@kbn/visualization-utils", ], "exclude": [ "target/**/*", diff --git a/src/plugins/vis_types/timeseries/public/application/components/vis_editor_visualization.js b/src/plugins/vis_types/timeseries/public/application/components/vis_editor_visualization.js index 326ba3734f45d..7bde7abd961da 100644 --- a/src/plugins/vis_types/timeseries/public/application/components/vis_editor_visualization.js +++ b/src/plugins/vis_types/timeseries/public/application/components/vis_editor_visualization.js @@ -8,7 +8,15 @@ import PropTypes from 'prop-types'; import React, { Component } from 'react'; -import { keys, EuiFlexGroup, EuiFlexItem, EuiButton, EuiText, EuiSwitch } from '@elastic/eui'; +import { + keys, + EuiFlexGroup, + EuiIcon, + EuiFlexItem, + EuiButton, + EuiText, + EuiSwitch, +} from '@elastic/eui'; import { FormattedMessage, injectI18n } from '@kbn/i18n-react'; import { pluck } from 'rxjs/operators'; @@ -200,7 +208,7 @@ class VisEditorVisualizationUI extends Component { defaultMessage: 'Press up/down to adjust the chart size', })} > - + diff --git a/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.test.js b/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.test.js index fe6f73ce66f79..d2c343d676ed9 100644 --- a/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.test.js +++ b/src/plugins/vis_types/timeseries/public/application/components/vis_types/timeseries/vis.test.js @@ -38,15 +38,6 @@ describe('TimeseriesVisualization', () => { setCharts({ theme: { - useChartsTheme: () => ({ - axes: { - tickLabel: { - padding: { - inner: 0, - }, - }, - }, - }), useChartsBaseTheme: () => ({ axes: { tickLabel: { diff --git a/src/plugins/vis_types/timeseries/public/application/lib/get_timezone.ts b/src/plugins/vis_types/timeseries/public/application/lib/get_timezone.ts deleted file mode 100644 index 44fc531e77b78..0000000000000 --- a/src/plugins/vis_types/timeseries/public/application/lib/get_timezone.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0 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 moment from 'moment-timezone'; -import { IUiSettingsClient } from '@kbn/core/public'; - -export function getTimezone(config: IUiSettingsClient) { - if (config.isDefault('dateFormat:tz')) { - const detectedTimezone = moment.tz.guess(); - if (detectedTimezone) return detectedTimezone; - else return moment().format('Z'); - } else { - return config.get('dateFormat:tz', 'Browser'); - } -} diff --git a/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/index.js b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/index.js index 418d1c0c19969..0874ef8593258 100644 --- a/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/index.js +++ b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/index.js @@ -28,7 +28,7 @@ import { Tooltip, } from '@elastic/charts'; import { EuiIcon } from '@elastic/eui'; -import { getTimezone } from '../../../lib/get_timezone'; +import { getTimeZone } from '@kbn/visualization-utils'; import { getUISettings, getCharts } from '../../../../services'; import { GRID_LINE_CONFIG, ICON_TYPES_MAP, STACKED_OPTIONS } from '../../constants'; import { AreaSeriesDecorator } from './decorators/area_decorator'; @@ -89,7 +89,7 @@ export const TimeSeries = ({ const { theme: themeService, activeCursor: activeCursorService } = getCharts(); const chartRef = useRef(); - const chartTheme = themeService.useChartsTheme(); + const chartBaseTheme = getBaseTheme(themeService.useChartsBaseTheme(), backgroundColor); const handleCursorUpdate = useActiveCursor(activeCursorService, chartRef, { isDateHistogram: true, @@ -121,14 +121,12 @@ export const TimeSeries = ({ } const uiSettings = getUISettings(); - const timeZone = getTimezone(uiSettings); + const timeZone = getTimeZone(uiSettings); const hasBarChart = series.some(({ bars }) => bars?.show); // apply legend style change if bgColor is configured const classes = classNames(getChartClasses(backgroundColor)); - const baseTheme = getBaseTheme(themeService.useChartsBaseTheme(), backgroundColor); - const onBrushEndListener = ({ x }) => { if (!x) { return; @@ -196,15 +194,12 @@ export const TimeSeries = ({ pointerUpdateDebounce={0} theme={[ { - crosshair: { - ...chartTheme.crosshair, - }, axes: { tickLabel: { padding: { inner: hasVisibleAnnotations ? TICK_LABEL_WITH_ANNOTATIONS_PADDING - : chartTheme.axes.tickLabel.padding.inner, + : chartBaseTheme.axes.tickLabel.padding.inner, }, }, }, @@ -226,9 +221,8 @@ export const TimeSeries = ({ labelOptions: { maxLines: truncateLegend ? maxLegendLines ?? 1 : 0 }, }, }, - chartTheme, ]} - baseTheme={baseTheme} + baseTheme={chartBaseTheme} externalPointerEvents={{ tooltip: { visible: syncTooltips, placement: Placement.Right }, }} diff --git a/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/theme.test.ts b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/theme.test.ts index 0e7dbb81233e2..8983d76eca15b 100644 --- a/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/theme.test.ts +++ b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/theme.test.ts @@ -7,27 +7,29 @@ */ import { getBaseTheme } from './theme'; -import { LIGHT_THEME, DARK_THEME } from '@elastic/charts'; +import { LEGACY_LIGHT_THEME, LEGACY_DARK_THEME } from '@elastic/charts'; describe('TSVB theme', () => { it('should return the basic themes if no bg color is specified', () => { // use original dark/light theme - expect(getBaseTheme(LIGHT_THEME)).toEqual(LIGHT_THEME); - expect(getBaseTheme(DARK_THEME)).toEqual(DARK_THEME); + expect(getBaseTheme(LEGACY_LIGHT_THEME)).toEqual(LEGACY_LIGHT_THEME); + expect(getBaseTheme(LEGACY_DARK_THEME)).toEqual(LEGACY_DARK_THEME); // discard any wrong/missing bg color - expect(getBaseTheme(DARK_THEME, null)).toEqual(DARK_THEME); - expect(getBaseTheme(DARK_THEME, '')).toEqual(DARK_THEME); - expect(getBaseTheme(DARK_THEME, undefined)).toEqual(DARK_THEME); + expect(getBaseTheme(LEGACY_DARK_THEME, null)).toEqual(LEGACY_DARK_THEME); + expect(getBaseTheme(LEGACY_DARK_THEME, '')).toEqual(LEGACY_DARK_THEME); + expect(getBaseTheme(LEGACY_DARK_THEME, undefined)).toEqual(LEGACY_DARK_THEME); }); it('should return a highcontrast color theme for a different background', () => { // red use a near full-black color - expect(getBaseTheme(LIGHT_THEME, 'red').axes.axisTitle.fill).toEqual('rgb(23,23,23)'); + expect(getBaseTheme(LEGACY_LIGHT_THEME, 'red').axes.axisTitle.fill).toEqual('rgb(23,23,23)'); // violet increased the text color to full white for higer contrast - expect(getBaseTheme(LIGHT_THEME, '#ba26ff').axes.axisTitle.fill).toEqual('rgb(255,255,255)'); + expect(getBaseTheme(LEGACY_LIGHT_THEME, '#ba26ff').axes.axisTitle.fill).toEqual( + 'rgb(255,255,255)' + ); - // light yellow, prefer the LIGHT_THEME fill color because already with a good contrast - expect(getBaseTheme(LIGHT_THEME, '#fff49f').axes.axisTitle.fill).toEqual('#333'); + // light yellow, prefer the LEGACY_LIGHT_THEME fill color because already with a good contrast + expect(getBaseTheme(LEGACY_LIGHT_THEME, '#fff49f').axes.axisTitle.fill).toEqual('#333'); }); }); diff --git a/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/theme.ts b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/theme.ts index 82567f8e8223e..6c1142d8b7e0f 100644 --- a/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/theme.ts +++ b/src/plugins/vis_types/timeseries/public/application/visualizations/views/timeseries/utils/theme.ts @@ -8,7 +8,7 @@ // @ts-ignore import colorJS from 'color'; -import { Theme, LIGHT_THEME, DARK_THEME } from '@elastic/charts'; +import { Theme, LEGACY_LIGHT_THEME, LEGACY_DARK_THEME } from '@elastic/charts'; function computeRelativeLuminosity(rgb: string) { return colorJS(rgb).luminosity(); @@ -96,11 +96,11 @@ export function getBaseTheme(baseTheme: Theme, bgColor?: string | null): Theme { } const bgLuminosity = computeRelativeLuminosity(bgColor); - const mainTheme = bgLuminosity <= 0.179 ? DARK_THEME : LIGHT_THEME; + const mainTheme = bgLuminosity <= 0.179 ? LEGACY_DARK_THEME : LEGACY_LIGHT_THEME; const color = findBestContrastColor( bgColor, - LIGHT_THEME.axes.axisTitle.fill, - DARK_THEME.axes.axisTitle.fill + LEGACY_LIGHT_THEME.axes.axisTitle.fill, + LEGACY_DARK_THEME.axes.axisTitle.fill ); return { ...mainTheme, diff --git a/src/plugins/vis_types/timeseries/public/request_handler.ts b/src/plugins/vis_types/timeseries/public/request_handler.ts index a3b6d43893c3c..721fdbea51798 100644 --- a/src/plugins/vis_types/timeseries/public/request_handler.ts +++ b/src/plugins/vis_types/timeseries/public/request_handler.ts @@ -8,7 +8,7 @@ import type { KibanaExecutionContext } from '@kbn/core/public'; import type { Adapters } from '@kbn/inspector-plugin/common'; import { KibanaContext } from '@kbn/data-plugin/public'; -import { getTimezone } from './application/lib/get_timezone'; +import { getTimeZone } from '@kbn/visualization-utils'; import { getUISettings, getDataStart, getCoreStart } from './services'; import { ROUTES } from '../common/constants'; @@ -44,7 +44,7 @@ export const metricsRequestHandler = async ({ expressionAbortSignal.addEventListener('abort', expressionAbortHandler); - const timezone = getTimezone(config); + const timezone = getTimeZone(config); const uiStateObj = uiState[visParams.type] ?? {}; const dataSearch = data.search; const parsedTimeRange = data.query.timefilter.timefilter.calculateBounds(input?.timeRange!); diff --git a/src/plugins/vis_types/timeseries/tsconfig.json b/src/plugins/vis_types/timeseries/tsconfig.json index e87ecc1e3f98f..49152c53f1f2f 100644 --- a/src/plugins/vis_types/timeseries/tsconfig.json +++ b/src/plugins/vis_types/timeseries/tsconfig.json @@ -45,6 +45,7 @@ "@kbn/home-plugin", "@kbn/std", "@kbn/tinymath", + "@kbn/visualization-utils", ], "exclude": [ "target/**/*", diff --git a/src/plugins/vis_types/vega/public/vega_fn.ts b/src/plugins/vis_types/vega/public/vega_fn.ts index 6dbd7c9792502..784d4ce1b7026 100644 --- a/src/plugins/vis_types/vega/public/vega_fn.ts +++ b/src/plugins/vis_types/vega/public/vega_fn.ts @@ -8,7 +8,6 @@ import { get } from 'lodash'; import { i18n } from '@kbn/i18n'; -import { ExecutionContextSearch } from '@kbn/data-plugin/public'; import { ExecutionContext, ExpressionFunctionDefinition, @@ -40,7 +39,7 @@ export type VegaExpressionFunctionDefinition = ExpressionFunctionDefinition< Input, Arguments, Output, - ExecutionContext + ExecutionContext >; export const createVegaFn = ( diff --git a/src/plugins/vis_types/vislib/public/vislib/components/legend/__snapshots__/legend.test.tsx.snap b/src/plugins/vis_types/vislib/public/vislib/components/legend/__snapshots__/legend.test.tsx.snap index cc95e272d54f7..d23c14423efbb 100644 --- a/src/plugins/vis_types/vislib/public/vislib/components/legend/__snapshots__/legend.test.tsx.snap +++ b/src/plugins/vis_types/vislib/public/vislib/components/legend/__snapshots__/legend.test.tsx.snap @@ -2,4 +2,4 @@ exports[`VisLegend Component Legend closed should match the snapshot 1`] = `"
"`; -exports[`VisLegend Component Legend open should match the snapshot 1`] = `"
"`; +exports[`VisLegend Component Legend open should match the snapshot 1`] = `"
"`; diff --git a/src/plugins/visualizations/public/actions/edit_in_lens_action.tsx b/src/plugins/visualizations/public/actions/edit_in_lens_action.tsx index b86e2b2dbb37a..2efe612e433ca 100644 --- a/src/plugins/visualizations/public/actions/edit_in_lens_action.tsx +++ b/src/plugins/visualizations/public/actions/edit_in_lens_action.tsx @@ -94,7 +94,7 @@ export class EditInLensAction implements Action { searchQuery, isEmbeddable: true, description: vis.description || embeddable.getOutput().description, - panelTimeRange: embeddable.getInput()?.timeRange, + panelTimeRange: embeddable.getExplicitInput()?.timeRange, }; if (navigateToLensConfig) { if (this.currentAppId) { diff --git a/test/accessibility/apps/discover.ts b/test/accessibility/apps/discover.ts index e75ddc9172dcc..3f9a1ef9bce0c 100644 --- a/test/accessibility/apps/discover.ts +++ b/test/accessibility/apps/discover.ts @@ -115,17 +115,17 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { // adding a11y tests for the new data grid it('a11y test on single document view', async () => { await testSubjects.click('docTableExpandToggleColumn'); - await PageObjects.discover.clickDocViewerTab(0); + await PageObjects.discover.clickDocViewerTab('doc_view_table'); await a11y.testAppSnapshot(); }); it('a11y test on JSON view of the document', async () => { - await PageObjects.discover.clickDocViewerTab(1); + await PageObjects.discover.clickDocViewerTab('doc_view_source'); await a11y.testAppSnapshot(); }); it('a11y test for actions on a field', async () => { - await PageObjects.discover.clickDocViewerTab(0); + await PageObjects.discover.clickDocViewerTab('doc_view_table'); if (await testSubjects.exists('openFieldActionsButton-Cancelled')) { await testSubjects.click('openFieldActionsButton-Cancelled'); } else { diff --git a/test/api_integration/apis/data_views/default_index_pattern/default_index_pattern.ts b/test/api_integration/apis/data_views/default_index_pattern/default_index_pattern.ts index d34ad5ccd5f4d..b980c684e529b 100644 --- a/test/api_integration/apis/data_views/default_index_pattern/default_index_pattern.ts +++ b/test/api_integration/apis/data_views/default_index_pattern/default_index_pattern.ts @@ -48,7 +48,7 @@ export default function ({ getService }: FtrProviderContext) { expect(response5.status).to.be(200); const response6 = await supertest.get(defaultPath); - expect(response6.body[serviceKeyId]).to.be(null); + expect(response6.body[serviceKeyId]).to.be(''); }); }); }); diff --git a/test/api_integration/apis/home/sample_data.ts b/test/api_integration/apis/home/sample_data.ts index c455eed849c4b..9858dee31cd49 100644 --- a/test/api_integration/apis/home/sample_data.ts +++ b/test/api_integration/apis/home/sample_data.ts @@ -8,6 +8,7 @@ import expect from '@kbn/expect'; import type { Response } from 'superagent'; +import differenceInMilliseconds from 'date-fns/differenceInMilliseconds'; import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService }: FtrProviderContext) { @@ -15,11 +16,10 @@ export default function ({ getService }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const es = getService('es'); - const MILLISECOND_IN_WEEK = 1000 * 60 * 60 * 24 * 7; const SPACES = ['default', 'other']; /** * default ID of the flights overview dashboard - * @see src/plugins/home/server/services/sample_data/data_sets/flights/index.ts + * @see {@link src/plugins/home/server/services/sample_data/data_sets/flights/index.ts} */ const FLIGHTS_OVERVIEW_DASHBOARD_ID = '7adfa750-4c81-11e8-b3d7-01146121b73d'; const FLIGHTS_CANVAS_APPLINK_PATH = @@ -72,8 +72,14 @@ export default function ({ getService }: FtrProviderContext) { }); }); - // FLAKY: https://github.com/elastic/kibana/issues/166572 - describe.skip('dates', () => { + describe('dates', () => { + // dates being compared are not arbitrary, but rather the dates of the earliest and latest timestamp of the flight sample data + // this can be verified in the flight data archive here {@link src/plugins/home/server/services/sample_data/data_sets/flights/flights.json.gz} + const sampleDataTimeIntervalInMS = differenceInMilliseconds( + new Date('2018-02-11T14:54:34'), + new Date('2018-01-01T00:00:00') + ); + it('should load elasticsearch index containing sample data with dates relative to current time', async () => { const resp = await es.search<{ timestamp: string }>({ index: 'kibana_sample_data_flights', @@ -83,8 +89,9 @@ export default function ({ getService }: FtrProviderContext) { const doc = resp.hits.hits[0]; const docMilliseconds = Date.parse(doc._source!.timestamp); const nowMilliseconds = Date.now(); + const delta = Math.abs(nowMilliseconds - docMilliseconds); - expect(delta).to.be.lessThan(MILLISECOND_IN_WEEK * 5); + expect(delta).to.be.lessThan(sampleDataTimeIntervalInMS); }); it('should load elasticsearch index containing sample data with dates relative to now parameter', async () => { @@ -100,7 +107,7 @@ export default function ({ getService }: FtrProviderContext) { const docMilliseconds = Date.parse(doc._source!.timestamp); const nowMilliseconds = Date.parse(nowString); const delta = Math.abs(nowMilliseconds - docMilliseconds); - expect(delta).to.be.lessThan(MILLISECOND_IN_WEEK * 5); + expect(delta).to.be.lessThan(sampleDataTimeIntervalInMS); }); }); }); diff --git a/test/examples/content_management/todo_app.ts b/test/examples/content_management/todo_app.ts index 5c5739c962e2d..7812f1fcf6646 100644 --- a/test/examples/content_management/todo_app.ts +++ b/test/examples/content_management/todo_app.ts @@ -28,17 +28,17 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide expect(todos.length).to.be(2); // check that filters work - await (await find.byCssSelector('label[title="Completed"]')).click(); + await (await find.byButtonText('Completed')).click(); await testSubjects.missingOrFail(`todoPending`); todos = await testSubjects.findAll(`~todoItem`); expect(todos.length).to.be(1); - await (await find.byCssSelector('label[title="Todo"]')).click(); + await (await find.byButtonText('Todo')).click(); await testSubjects.missingOrFail(`todoPending`); todos = await testSubjects.findAll(`~todoItem`); expect(todos.length).to.be(1); - await (await find.byCssSelector('label[title="All"]')).click(); + await (await find.byButtonText('All')).click(); await testSubjects.missingOrFail(`todoPending`); todos = await testSubjects.findAll(`~todoItem`); expect(todos.length).to.be(2); @@ -56,9 +56,10 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide let newTodoCheckbox = await newTodo.findByTestSubject('~todoCheckbox'); expect(await newTodoCheckbox.isSelected()).to.be(false); await (await newTodo.findByTagName('label')).click(); + await newTodo.click(); await testSubjects.missingOrFail(`todoPending`); - await (await find.byCssSelector('label[title="Completed"]')).click(); + await (await find.byButtonText('Completed')).click(); await testSubjects.missingOrFail(`todoPending`); todos = await testSubjects.findAll(`~todoItem`); expect(todos.length).to.be(2); diff --git a/test/functional/apps/dashboard_elements/controls/common/range_slider.ts b/test/functional/apps/dashboard_elements/controls/common/range_slider.ts index 17a1873ed098f..e088066533250 100644 --- a/test/functional/apps/dashboard_elements/controls/common/range_slider.ts +++ b/test/functional/apps/dashboard_elements/controls/common/range_slider.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { RANGE_SLIDER_CONTROL } from '@kbn/controls-plugin/common'; +import { OPTIONS_LIST_CONTROL, RANGE_SLIDER_CONTROL } from '@kbn/controls-plugin/common'; import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../../ftr_provider_context'; @@ -112,6 +112,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const secondId = (await dashboardControls.getAllControlIds())[1]; const newTitle = 'Average ticket price'; await dashboardControls.editExistingControl(secondId); + await dashboardControls.controlsEditorVerifySupportedControlTypes({ + supportedTypes: [OPTIONS_LIST_CONTROL, RANGE_SLIDER_CONTROL], + selectedType: RANGE_SLIDER_CONTROL, + }); await dashboardControls.controlEditorSetTitle(newTitle); await dashboardControls.controlEditorSetWidth('large'); await dashboardControls.controlEditorSave(); @@ -128,7 +132,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(await saveButton.isEnabled()).to.be(true); await dashboardControls.controlsEditorSetDataView('kibana_sample_data_flights'); expect(await saveButton.isEnabled()).to.be(false); - await dashboardControls.controlsEditorSetfield('dayOfWeek', RANGE_SLIDER_CONTROL); + await dashboardControls.controlsEditorSetfield('dayOfWeek'); + await dashboardControls.controlsEditorSetControlType(RANGE_SLIDER_CONTROL); await dashboardControls.controlEditorSave(); await dashboardControls.rangeSliderWaitForLoading(firstId); await dashboardControls.validateRange('placeholder', firstId, '0', '6'); diff --git a/test/functional/apps/dashboard_elements/controls/common/replace_controls.ts b/test/functional/apps/dashboard_elements/controls/common/replace_controls.ts index d3019a34b8802..974f5e942d42a 100644 --- a/test/functional/apps/dashboard_elements/controls/common/replace_controls.ts +++ b/test/functional/apps/dashboard_elements/controls/common/replace_controls.ts @@ -26,9 +26,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const DASHBOARD_NAME = 'Test Replace Controls'; - const changeFieldType = async (controlId: string, newField: string, expectedType?: string) => { + const changeFieldType = async (controlId: string, newField: string, type: string) => { await dashboardControls.editExistingControl(controlId); - await dashboardControls.controlsEditorSetfield(newField, expectedType); + await dashboardControls.controlsEditorSetfield(newField); + await dashboardControls.controlsEditorSetControlType(type); await dashboardControls.controlEditorSave(); }; diff --git a/test/functional/apps/dashboard_elements/controls/options_list/options_list_allow_expensive_queries_off.ts b/test/functional/apps/dashboard_elements/controls/options_list/options_list_allow_expensive_queries_off.ts index 91774aee02f2d..344143f7a0cc6 100644 --- a/test/functional/apps/dashboard_elements/controls/options_list/options_list_allow_expensive_queries_off.ts +++ b/test/functional/apps/dashboard_elements/controls/options_list/options_list_allow_expensive_queries_off.ts @@ -69,9 +69,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); - it('Can search options list for available options', async () => { + it('Can search options list for available options - exact match, case insensitive', async () => { await dashboardControls.optionsListOpenPopover(controlId); - await dashboardControls.optionsListPopoverSearchForOption('meo'); + await dashboardControls.optionsListPopoverSearchForOption('mEOw'); await dashboardControls.ensureAvailableOptionsEqual( controlId, { @@ -84,9 +84,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await dashboardControls.optionsListEnsurePopoverIsClosed(controlId); }); - it('Can search options list for available options - case sensitive', async () => { + it('Can search options list for available options - does not find partial match', async () => { await dashboardControls.optionsListOpenPopover(controlId); - await dashboardControls.optionsListPopoverSearchForOption('MEO'); + await dashboardControls.optionsListPopoverSearchForOption('meo'); const cardinality = await dashboardControls.optionsListPopoverGetAvailableOptionsCount(); expect(cardinality).to.be(0); await dashboardControls.optionsListPopoverClearSearch(); diff --git a/test/functional/apps/dashboard_elements/controls/options_list/options_list_creation_and_editing.ts b/test/functional/apps/dashboard_elements/controls/options_list/options_list_creation_and_editing.ts index 06dcfb6961f82..a19d5bfee82bb 100644 --- a/test/functional/apps/dashboard_elements/controls/options_list/options_list_creation_and_editing.ts +++ b/test/functional/apps/dashboard_elements/controls/options_list/options_list_creation_and_editing.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { OPTIONS_LIST_CONTROL } from '@kbn/controls-plugin/common'; +import { OPTIONS_LIST_CONTROL, RANGE_SLIDER_CONTROL } from '@kbn/controls-plugin/common'; import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../../ftr_provider_context'; @@ -106,7 +106,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { expect(await saveButton.isEnabled()).to.be(true); await dashboardControls.controlsEditorSetDataView('animals-*'); expect(await saveButton.isEnabled()).to.be(false); - await dashboardControls.controlsEditorSetfield('animal.keyword', OPTIONS_LIST_CONTROL); + await dashboardControls.controlsEditorSetfield('animal.keyword'); + await dashboardControls.controlsEditorSetControlType(OPTIONS_LIST_CONTROL); await dashboardControls.controlEditorSave(); const selectionString = await dashboardControls.optionsListGetSelectionsString(firstId); @@ -141,6 +142,17 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await dashboard.clearUnsavedChanges(); }); + it('can change an existing control to a number field', async () => { + const firstId = (await dashboardControls.getAllControlIds())[0]; + await dashboardControls.editExistingControl(firstId); + await dashboardControls.controlsEditorSetfield('weightLbs'); + await dashboardControls.controlsEditorVerifySupportedControlTypes({ + supportedTypes: [OPTIONS_LIST_CONTROL, RANGE_SLIDER_CONTROL], + selectedType: OPTIONS_LIST_CONTROL, + }); + await dashboardControls.controlEditorSave(); + }); + it('deletes an existing control', async () => { const firstId = (await dashboardControls.getAllControlIds())[0]; diff --git a/test/functional/apps/dashboard_elements/controls/options_list/options_list_suggestions.ts b/test/functional/apps/dashboard_elements/controls/options_list/options_list_suggestions.ts index b6c27ece900ab..0b2dda536d41c 100644 --- a/test/functional/apps/dashboard_elements/controls/options_list/options_list_suggestions.ts +++ b/test/functional/apps/dashboard_elements/controls/options_list/options_list_suggestions.ts @@ -7,9 +7,10 @@ */ import { OPTIONS_LIST_CONTROL } from '@kbn/controls-plugin/common'; +import expect from '@kbn/expect'; -import { OPTIONS_LIST_ANIMAL_SOUND_SUGGESTIONS } from '../../../../page_objects/dashboard_page_controls'; import { FtrProviderContext } from '../../../../ftr_provider_context'; +import { OPTIONS_LIST_ANIMAL_SOUND_SUGGESTIONS } from '../../../../page_objects/dashboard_page_controls'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const testSubjects = getService('testSubjects'); @@ -143,42 +144,57 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await dashboardControls.optionsListPopoverClearSearch(); await dashboardControls.optionsListEnsurePopoverIsClosed(controlId); }); - }); - it('wildcard searching causes unsaved changes', async () => { - await dashboardControls.editExistingControl(controlId); - await dashboardControls.optionsListSetAdditionalSettings({ searchTechnique: 'wildcard' }); - await dashboardControls.controlEditorSave(); - await testSubjects.existOrFail('dashboardUnsavedChangesBadge'); - }); + it('wildcard searching causes unsaved changes', async () => { + await dashboardControls.editExistingControl(controlId); + await dashboardControls.optionsListSetAdditionalSettings({ searchTechnique: 'wildcard' }); + await dashboardControls.controlEditorSave(); + await testSubjects.existOrFail('dashboardUnsavedChangesBadge'); + }); - it('wildcard searching works as expected', async () => { - await dashboardControls.optionsListOpenPopover(controlId); - await dashboardControls.optionsListPopoverSearchForOption('r'); - const containsR = Object.entries(OPTIONS_LIST_ANIMAL_SOUND_SUGGESTIONS).reduce( - (result, [key, docCount]) => { - if (key.includes('r')) return { ...result, [key]: docCount }; - return { ...result }; - }, - {} - ); - await dashboardControls.ensureAvailableOptionsEqual( - controlId, - { - suggestions: containsR, - invalidSelections: [], - }, - true - ); - await dashboardControls.optionsListPopoverClearSearch(); - await dashboardControls.optionsListEnsurePopoverIsClosed(controlId); - }); + it('wildcard searching works as expected', async () => { + await dashboardControls.optionsListOpenPopover(controlId); + await dashboardControls.optionsListPopoverSearchForOption('r'); + const containsR = Object.entries(OPTIONS_LIST_ANIMAL_SOUND_SUGGESTIONS).reduce( + (result, [key, docCount]) => { + if (key.includes('r')) return { ...result, [key]: docCount }; + return { ...result }; + }, + {} + ); + await dashboardControls.ensureAvailableOptionsEqual( + controlId, + { + suggestions: containsR, + invalidSelections: [], + }, + true + ); + await dashboardControls.optionsListPopoverClearSearch(); + await dashboardControls.optionsListEnsurePopoverIsClosed(controlId); + }); + + it('exact match searching works as expected', async () => { + await dashboardControls.editExistingControl(controlId); + await dashboardControls.optionsListSetAdditionalSettings({ searchTechnique: 'exact' }); + await dashboardControls.controlEditorSave(); - it('returning to default search technqiue should remove unsaved changes', async () => { - await dashboardControls.editExistingControl(controlId); - await dashboardControls.optionsListSetAdditionalSettings({ searchTechnique: 'prefix' }); - await dashboardControls.controlEditorSave(); - await testSubjects.missingOrFail('dashboardUnsavedChangesBadge'); + await dashboardControls.optionsListOpenPopover(controlId); + await dashboardControls.optionsListPopoverSearchForOption('R'); + expect(await dashboardControls.optionsListPopoverGetAvailableOptionsCount()).to.be(0); + await dashboardControls.optionsListPopoverSearchForOption('RuFf'); + expect(await dashboardControls.optionsListPopoverGetAvailableOptionsCount()).to.be(1); + + await dashboardControls.optionsListPopoverClearSearch(); + await dashboardControls.optionsListEnsurePopoverIsClosed(controlId); + }); + + it('returning to default search technique should remove unsaved changes', async () => { + await dashboardControls.editExistingControl(controlId); + await dashboardControls.optionsListSetAdditionalSettings({ searchTechnique: 'prefix' }); + await dashboardControls.controlEditorSave(); + await testSubjects.missingOrFail('dashboardUnsavedChangesBadge'); + }); }); }); } diff --git a/test/functional/apps/discover/classic/_discover_fields_api.ts b/test/functional/apps/discover/classic/_discover_fields_api.ts index 5f48abda70654..126f69edaacd2 100644 --- a/test/functional/apps/discover/classic/_discover_fields_api.ts +++ b/test/functional/apps/discover/classic/_discover_fields_api.ts @@ -69,7 +69,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('displays _source viewer in doc viewer', async function () { await PageObjects.discover.clickDocTableRowToggle(0); await PageObjects.discover.isShowingDocViewer(); - await PageObjects.discover.clickDocViewerTab(1); + await PageObjects.discover.clickDocViewerTab('doc_view_source'); await PageObjects.discover.expectSourceViewerToExist(); }); diff --git a/test/functional/apps/discover/group1/_discover_histogram.ts b/test/functional/apps/discover/group1/_discover_histogram.ts index 43f39b417864c..bdaf14fca96e4 100644 --- a/test/functional/apps/discover/group1/_discover_histogram.ts +++ b/test/functional/apps/discover/group1/_discover_histogram.ts @@ -33,7 +33,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { const log = getService('log'); const queryBar = getService('queryBar'); - describe('discover histogram', function describeIndexTests() { + // FLAKY: https://github.com/elastic/kibana/issues/173586 + describe.skip('discover histogram', function describeIndexTests() { before(async () => { await esArchiver.loadIfNeeded('test/functional/fixtures/es_archiver/logstash_functional'); await esArchiver.load('test/functional/fixtures/es_archiver/long_window_logstash'); diff --git a/test/functional/apps/discover/group1/_doc_accessibility.ts b/test/functional/apps/discover/group1/_doc_accessibility.ts index becad9c241af8..5ecdef656b2ac 100644 --- a/test/functional/apps/discover/group1/_doc_accessibility.ts +++ b/test/functional/apps/discover/group1/_doc_accessibility.ts @@ -48,7 +48,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await browser.pressKeys(browser.keys.TAB); await browser.pressKeys(browser.keys.SPACE); await browser.pressKeys(browser.keys.TAB); - const tableTab = await testSubjects.find('docViewerTab-0'); + const tableTab = await testSubjects.find('docViewerTab-doc_view_table'); const activeElement = await find.activeElement(); expect(await tableTab.getAttribute('data-test-subj')).to.eql( await activeElement.getAttribute('data-test-subj') diff --git a/test/functional/apps/discover/group2/_data_grid_doc_table.ts b/test/functional/apps/discover/group2/_data_grid_doc_table.ts index b8b892afce306..81fd740ba21fa 100644 --- a/test/functional/apps/discover/group2/_data_grid_doc_table.ts +++ b/test/functional/apps/discover/group2/_data_grid_doc_table.ts @@ -96,7 +96,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { log.debug(`expanded document id: ${expandDocId}`); await dataGrid.clickRowToggle(); - await find.clickByCssSelectorWhenNotDisabledWithoutRetry('#kbn_doc_viewer_tab_1'); + await find.clickByCssSelectorWhenNotDisabledWithoutRetry( + '#kbn_doc_viewer_tab_doc_view_source' + ); await retry.waitForWithTimeout( 'document id in flyout matching the expanded document id', @@ -138,7 +140,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { log.debug(`expanded document id: ${expandDocId}`); await dataGrid.clickRowToggle(); - await find.clickByCssSelectorWhenNotDisabledWithoutRetry('#kbn_doc_viewer_tab_1'); + await find.clickByCssSelectorWhenNotDisabledWithoutRetry( + '#kbn_doc_viewer_tab_doc_view_source' + ); await retry.waitForWithTimeout( 'document id in flyout matching the expanded document id', diff --git a/test/functional/apps/discover/group2/_data_grid_field_tokens.ts b/test/functional/apps/discover/group2/_data_grid_field_tokens.ts index 3731c1e15f446..af4943a13e62a 100644 --- a/test/functional/apps/discover/group2/_data_grid_field_tokens.ts +++ b/test/functional/apps/discover/group2/_data_grid_field_tokens.ts @@ -130,7 +130,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.unifiedFieldList.clickFieldListItemAdd('ip'); await PageObjects.unifiedFieldList.clickFieldListItemAdd('geo.coordinates'); - expect(await findFirstColumnTokens()).to.eql(['Number', 'String', 'String']); + expect(await findFirstColumnTokens()).to.eql(['Number', 'String', 'String', 'String']); expect(await findFirstDocViewerTokens()).to.eql([ 'String', @@ -140,7 +140,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { 'Number', 'String', 'String', - 'Unknown field', + 'String', 'String', 'String', ]); diff --git a/test/functional/apps/discover/group3/_unsaved_changes_badge.ts b/test/functional/apps/discover/group3/_unsaved_changes_badge.ts index 292835b37ef10..c931a11f4f5f4 100644 --- a/test/functional/apps/discover/group3/_unsaved_changes_badge.ts +++ b/test/functional/apps/discover/group3/_unsaved_changes_badge.ts @@ -10,12 +10,14 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../ftr_provider_context'; const SAVED_SEARCH_NAME = 'test saved search'; +const SAVED_SEARCH_WITH_FILTERS_NAME = 'test saved search with filters'; export default function ({ getService, getPageObjects }: FtrProviderContext) { const esArchiver = getService('esArchiver'); const kibanaServer = getService('kibanaServer'); const testSubjects = getService('testSubjects'); const dataGrid = getService('dataGrid'); + const filterBar = getService('filterBar'); const PageObjects = getPageObjects([ 'settings', 'common', @@ -163,5 +165,34 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { await PageObjects.discover.waitUntilSearchingHasFinished(); await testSubjects.missingOrFail('unsavedChangesBadge'); }); + + it('should not show the badge after pinning the first filter but after disabling a filter', async () => { + await filterBar.addFilter({ field: 'extension', operation: 'is', value: 'png' }); + await filterBar.addFilter({ field: 'bytes', operation: 'exists' }); + await PageObjects.discover.saveSearch(SAVED_SEARCH_WITH_FILTERS_NAME); + await PageObjects.discover.waitUntilSearchingHasFinished(); + + await testSubjects.missingOrFail('unsavedChangesBadge'); + + await filterBar.toggleFilterPinned('extension'); + await PageObjects.discover.waitUntilSearchingHasFinished(); + expect(await filterBar.isFilterPinned('extension')).to.be(true); + + await testSubjects.missingOrFail('unsavedChangesBadge'); + + await filterBar.toggleFilterNegated('bytes'); + await PageObjects.discover.waitUntilSearchingHasFinished(); + expect(await filterBar.isFilterNegated('bytes')).to.be(true); + + await testSubjects.existOrFail('unsavedChangesBadge'); + + await PageObjects.discover.revertUnsavedChanges(); + await testSubjects.missingOrFail('unsavedChangesBadge'); + + expect(await filterBar.getFilterCount()).to.be(2); + expect(await filterBar.isFilterPinned('extension')).to.be(false); + expect(await filterBar.isFilterNegated('bytes')).to.be(false); + expect(await PageObjects.discover.getHitCount()).to.be('1,373'); + }); }); } diff --git a/test/functional/apps/discover/group4/_discover_fields_api.ts b/test/functional/apps/discover/group4/_discover_fields_api.ts index e7b3a63cb4778..f684a38e144cb 100644 --- a/test/functional/apps/discover/group4/_discover_fields_api.ts +++ b/test/functional/apps/discover/group4/_discover_fields_api.ts @@ -71,7 +71,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { it('displays _source viewer in doc viewer', async function () { await dataGrid.clickRowToggle(); await PageObjects.discover.isShowingDocViewer(); - await PageObjects.discover.clickDocViewerTab(1); + await PageObjects.discover.clickDocViewerTab('doc_view_source'); await PageObjects.discover.expectSourceViewerToExist(); }); 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..245ac88606b50 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 @@ -187,7 +187,8 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) { }); }); - describe('index pattern edit', function () { + // FLAKY: https://github.com/elastic/kibana/issues/173625 + describe.skip('index pattern edit', function () { it('should update field list', async function () { await PageObjects.settings.editIndexPattern( 'kibana_sample_data_flights', diff --git a/test/functional/page_objects/dashboard_page_controls.ts b/test/functional/page_objects/dashboard_page_controls.ts index effac6f9fbdf9..4e1e9c1ccfdc1 100644 --- a/test/functional/page_objects/dashboard_page_controls.ts +++ b/test/functional/page_objects/dashboard_page_controls.ts @@ -6,24 +6,19 @@ * Side Public License, v 1. */ -import expect from '@kbn/expect'; import { + ControlWidth, OPTIONS_LIST_CONTROL, RANGE_SLIDER_CONTROL, - ControlWidth, } from '@kbn/controls-plugin/common'; -import { OptionsListSearchTechnique } from '@kbn/controls-plugin/common/options_list/types'; import { ControlGroupChainingSystem } from '@kbn/controls-plugin/common/control_group/types'; +import { OptionsListSearchTechnique } from '@kbn/controls-plugin/common/options_list/suggestions_searching'; import { OptionsListSortingType } from '@kbn/controls-plugin/common/options_list/suggestions_sorting'; +import expect from '@kbn/expect'; +import { asyncForEach } from '@kbn/std'; -import { WebElementWrapper } from '../services/lib/web_element_wrapper'; import { FtrService } from '../ftr_provider_context'; - -const CONTROL_DISPLAY_NAMES: { [key: string]: string } = { - default: 'No field selected yet', - [OPTIONS_LIST_CONTROL]: 'Options list', - [RANGE_SLIDER_CONTROL]: 'Range slider', -}; +import { WebElementWrapper } from '../services/lib/web_element_wrapper'; interface OptionsListAdditionalSettings { searchTechnique?: OptionsListSearchTechnique; @@ -112,7 +107,14 @@ export class DashboardPageControls extends FtrService { await this.retry.try(async () => { await this.testSubjects.existOrFail('control-editor-flyout'); }); - await this.controlEditorVerifyType('default'); + + /** All control type options should be disabled until a field is selected */ + const controlTypeOptions = await this.find.allByCssSelector( + '[data-test-subj="controlTypeMenu"] > li > button' + ); + await asyncForEach(controlTypeOptions, async (controlTypeOption) => { + expect(await controlTypeOption.isEnabled()).to.be(false); + }); } /* ----------------------------------------------------------- @@ -270,7 +272,10 @@ export class DashboardPageControls extends FtrService { await this.openCreateControlFlyout(); if (dataViewTitle) await this.controlsEditorSetDataView(dataViewTitle); - if (fieldName) await this.controlsEditorSetfield(fieldName, controlType); + if (fieldName) { + await this.controlsEditorSetfield(fieldName); + await this.controlsEditorSetControlType(controlType); + } if (title) await this.controlEditorSetTitle(title); if (width) await this.controlEditorSetWidth(width); if (grow !== undefined) await this.controlEditorSetGrow(grow); @@ -601,11 +606,7 @@ export class DashboardPageControls extends FtrService { await this.testSubjects.click(`data-view-picker-${dataViewTitle}`); } - public async controlsEditorSetfield( - fieldName: string, - expectedType?: string, - shouldSearch: boolean = true - ) { + public async controlsEditorSetfield(fieldName: string, shouldSearch: boolean = true) { this.log.debug(`Setting control field to ${fieldName}`); if (shouldSearch) { await this.testSubjects.setValue('field-search-input', fieldName); @@ -614,13 +615,31 @@ export class DashboardPageControls extends FtrService { await this.testSubjects.existOrFail(`field-picker-select-${fieldName}`); }); await this.testSubjects.click(`field-picker-select-${fieldName}`); - if (expectedType) await this.controlEditorVerifyType(expectedType); } - public async controlEditorVerifyType(type: string) { - this.log.debug(`Verifying that the control editor picked the type ${type}`); - const autoSelectedType = await this.testSubjects.getVisibleText('control-editor-type'); - expect(autoSelectedType).to.equal(CONTROL_DISPLAY_NAMES[type]); + public async controlsEditorVerifySupportedControlTypes({ + supportedTypes, + selectedType = OPTIONS_LIST_CONTROL, + }: { + supportedTypes: string[]; + selectedType?: string; + }) { + this.log.debug(`Verifying that control types match what is expected for the selected field`); + asyncForEach(supportedTypes, async (type) => { + const controlTypeItem = await this.testSubjects.find(`create__${type}`); + expect(await controlTypeItem.isEnabled()).to.be(true); + if (type === selectedType) { + expect(await controlTypeItem.getAttribute('aria-pressed')).to.be('true'); + } + }); + } + + public async controlsEditorSetControlType(type: string) { + this.log.debug(`Setting control type to ${type}`); + const controlTypeItem = await this.testSubjects.find(`create__${type}`); + expect(await controlTypeItem.isEnabled()).to.be(true); + await controlTypeItem.click(); + expect(await controlTypeItem.getAttribute('aria-pressed')).to.be('true'); } // Options List editor functions diff --git a/test/functional/page_objects/discover_page.ts b/test/functional/page_objects/discover_page.ts index 6005bbd2de7ad..9f636fcd8ec78 100644 --- a/test/functional/page_objects/discover_page.ts +++ b/test/functional/page_objects/discover_page.ts @@ -379,8 +379,8 @@ export class DiscoverPageObject extends FtrService { return await this.testSubjects.exists('kbnDocViewer'); } - public async clickDocViewerTab(index: number) { - return await this.find.clickByCssSelector(`#kbn_doc_viewer_tab_${index}`); + public async clickDocViewerTab(id: string) { + return await this.find.clickByCssSelector(`#kbn_doc_viewer_tab_${id}`); } public async expectSourceViewerToExist() { diff --git a/test/functional/page_objects/time_picker.ts b/test/functional/page_objects/time_picker.ts index 495d4089321b4..aa8832f28848a 100644 --- a/test/functional/page_objects/time_picker.ts +++ b/test/functional/page_objects/time_picker.ts @@ -57,6 +57,7 @@ export class TimePickerPageObject extends FtrService { }); if (isVisible) { await this.testSubjects.click('noDataPopoverDismissButton'); + await this.testSubjects.waitForDeleted('noDataPopoverDismissButton'); } } @@ -106,6 +107,8 @@ export class TimePickerPageObject extends FtrService { } else { await this.testSubjects.setValue(dataTestSubj, value); } + + await this.testSubjects.pressEnter(dataTestSubj); } private async showStartEndTimes() { diff --git a/test/functional/services/combo_box.ts b/test/functional/services/combo_box.ts index 78c36a87636f7..4b23dd3bd8b98 100644 --- a/test/functional/services/combo_box.ts +++ b/test/functional/services/combo_box.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import expect from '@kbn/expect'; import { FtrService } from '../ftr_provider_context'; import { WebElementWrapper } from './lib/web_element_wrapper'; @@ -84,6 +85,7 @@ export class ComboBoxService extends FtrService { const isOptionSelected = await this.isOptionSelected(comboBoxElement, trimmedValue); if (isOptionSelected) { + this.log.debug(`value is already selected. returning`); return; } @@ -116,7 +118,7 @@ export class ComboBoxService extends FtrService { const [alternate] = alternateTitle ? await this.find.allByCssSelector( - `.euiFilterSelectItem[title="${alternateTitle}"]`, + `.euiFilterSelectItem[title="${alternateTitle}" i]`, this.WAIT_FOR_EXISTS_TIME ) : []; @@ -177,15 +179,28 @@ export class ComboBoxService extends FtrService { * @param comboBoxElement element that wraps up EuiComboBox * @param filterValue text */ - private async setFilterValue( + public async setFilterValue( comboBoxElement: WebElementWrapper, filterValue: string ): Promise { const input = await comboBoxElement.findByTagName('input'); - await input.clearValue(); - await this.waitForOptionsListLoading(comboBoxElement); - await input.type(filterValue); - await this.waitForOptionsListLoading(comboBoxElement); + + await this.retry.try(async () => { + // Wait for the input to not be disabled before typing into it (otherwise + // typing will sometimes trigger the global search bar instead) + expect(await input.isEnabled()).to.equal(true); + + // Some Kibana comboboxes force state to not be clearable, so we can't use `input.clearValue()`. + // This is not-great production UX and shouldn't be happening, but for now we're going to + // work around it in FTR tests by selecting all existing text and typing to replace + if (!!(await input.getAttribute('value'))) { + await input.selectValueWithKeyboard(); + } + await input.type(filterValue); + await this.waitForOptionsListLoading(comboBoxElement); + + expect(await input.getAttribute('value')).to.equal(filterValue); + }); } /** @@ -241,9 +256,29 @@ export class ComboBoxService extends FtrService { this.log.debug(`comboBox.getComboBoxSelectedOptions, comboBoxSelector: ${comboBoxSelector}`); const comboBox = await this.testSubjects.find(comboBoxSelector); const $ = await comboBox.parseDomContent(); - return $('.euiComboBoxPill') + + if (await this.isSingleSelectionPlainText(comboBox)) { + const input = $('[data-test-subj="comboBoxSearchInput"]'); + this.log.debug('Single selection value: ', input.val()); + + const isValid = input.attr('aria-invalid') !== 'true'; + + if (isValid) { + const value = input.val(); + return value ? [value] : []; // Don't return empty strings + } else { + this.log.debug( + 'Single selection value is not valid and thus not selected - returning empty array' + ); + return []; + } + } + + const options = $('.euiComboBoxPill') .toArray() .map((option) => $(option).text()); + + return options; } /** @@ -315,8 +350,8 @@ export class ComboBoxService extends FtrService { if (!isOptionsListOpen) { await this.retry.try(async () => { - const toggleBtn = await comboBoxElement.findByTestSubject('comboBoxInput'); - await toggleBtn.click(); + const inputWrapper = await this.getComboBoxInputWrapper(comboBoxElement); + await inputWrapper.click(); }); } } @@ -333,9 +368,21 @@ export class ComboBoxService extends FtrService { ): Promise { this.log.debug(`comboBox.isOptionSelected, value: ${value}`); const $ = await comboBoxElement.parseDomContent(); + + if (await this.isSingleSelectionPlainText(comboBoxElement)) { + const input = $('input[role="combobox"]'); + + const hasValidValue = + input.attr('aria-invalid') !== 'true' && + value.toLowerCase().trim() === input.val().toLowerCase().trim(); // Normalizing text here for Firefox driver shenanigans + + return !!hasValidValue; + } + const selectedOptions = $('.euiComboBoxPill') .toArray() .map((option) => $(option).text()); + return ( selectedOptions.length === 1 && selectedOptions[0].toLowerCase().trim() === value.toLowerCase().trim() @@ -369,4 +416,26 @@ export class ComboBoxService extends FtrService { this.log.debug(`isDisabled:${isDisabled}`); return isDisabled?.toLowerCase() === 'true'; } + + /** + * Single selection plain text comboboxes do not render pill text, but instead render + * selected as well as search values in the child + */ + private async isSingleSelectionPlainText(comboBoxElement: WebElementWrapper): Promise { + const inputWrapper = await this.getComboBoxInputWrapper(comboBoxElement); + return await inputWrapper.elementHasClass('euiComboBox__inputWrap--plainText'); + } + + /** + * Kibana devs sometimes pass in the `comboBoxInput` element and not the parent wrapper 🤷 + * This util accounts for that and returns the `data-test-subj="comboBoxInput"` element no matter what + */ + private async getComboBoxInputWrapper( + comboBoxElement: WebElementWrapper + ): Promise { + const isInputWrapper = await comboBoxElement.elementHasClass('euiComboBox__inputWrap'); + return isInputWrapper + ? comboBoxElement + : await comboBoxElement.findByTestSubject('comboBoxInput'); + } } diff --git a/test/functional/services/common/index.ts b/test/functional/services/common/index.ts index 54c9e5a1ee54c..b7b8c67a4280d 100644 --- a/test/functional/services/common/index.ts +++ b/test/functional/services/common/index.ts @@ -14,4 +14,3 @@ export { PngService } from './png'; export { ScreenshotsService } from './screenshots'; export { SnapshotsService } from './snapshots'; export { TestSubjects } from './test_subjects'; -export { RetryOnStaleProvider } from './retry_on_stale'; diff --git a/test/functional/services/filter_bar.ts b/test/functional/services/filter_bar.ts index eb9aae45621e1..cd927ee18fb83 100644 --- a/test/functional/services/filter_bar.ts +++ b/test/functional/services/filter_bar.ts @@ -178,6 +178,11 @@ export class FilterBarService extends FtrService { return (await filter.getAttribute('data-test-subj')).includes('filter-pinned'); } + public async isFilterNegated(key: string): Promise { + const filter = await this.testSubjects.find(`~filter & ~filter-key-${key}`); + return (await filter.getAttribute('data-test-subj')).includes('filter-negated'); + } + public async getFilterCount(): Promise { const filters = await this.testSubjects.findAll('~filter'); return filters.length; diff --git a/test/functional/services/index.ts b/test/functional/services/index.ts index 11975f560e2d7..dc151f1b243e3 100644 --- a/test/functional/services/index.ts +++ b/test/functional/services/index.ts @@ -6,6 +6,7 @@ * Side Public License, v 1. */ +import { commonFunctionalUIServices } from '@kbn/ftr-common-functional-ui-services'; import { services as commonServiceProviders } from '../../common/services'; import { AppsMenuService } from './apps_menu'; @@ -17,7 +18,6 @@ import { ScreenshotsService, SnapshotsService, TestSubjects, - RetryOnStaleProvider, } from './common'; import { ComboBoxService } from './combo_box'; import { @@ -60,7 +60,7 @@ import { DashboardSettingsProvider } from './dashboard/dashboard_settings'; export const services = { ...commonServiceProviders, - + ...commonFunctionalUIServices, __webdriver__: RemoteProvider, filterBar: FilterBarService, queryBar: QueryBarService, @@ -101,7 +101,6 @@ export const services = { managementMenu: ManagementMenuService, monacoEditor: MonacoEditorService, menuToggle: MenuToggleService, - retryOnStale: RetryOnStaleProvider, usageCollection: UsageCollectionService, savedObjectsFinder: SavedObjectsFinderService, }; diff --git a/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts b/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts index 70df204b1de03..6a914c7273ba3 100644 --- a/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts +++ b/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts @@ -261,28 +261,28 @@ export class WebElementWrapper { * @default { charByChar: false } */ async clearValueWithKeyboard(options: TypeOptions = { charByChar: false }) { + const value = await this.getAttribute('value'); + if (!value.length) { + return; + } + if (options.charByChar === true) { - const value = await this.getAttribute('value'); for (let i = 0; i <= value.length; i++) { await this.pressKeys(this.Keys.BACK_SPACE); await setTimeoutAsync(100); } } else { - if (this.isChromium) { - // https://bugs.chromium.org/p/chromedriver/issues/detail?id=30 - await this.retryCall(async function clearValueWithKeyboard(wrapper) { - await wrapper.driver.executeScript(`arguments[0].select();`, wrapper._webElement); - }); - await this.pressKeys(this.Keys.BACK_SPACE); - } else { - const selectionKey = this.Keys[process.platform === 'darwin' ? 'COMMAND' : 'CONTROL']; - await this.pressKeys([selectionKey, 'a']); - await this.pressKeys(this.Keys.NULL); // Release modifier keys - await this.pressKeys(this.Keys.BACK_SPACE); // Delete all content - } + await this.selectValueWithKeyboard(); + await this.pressKeys(this.Keys.BACK_SPACE); } } + async selectValueWithKeyboard() { + const selectionKey = this.Keys[process.platform === 'darwin' ? 'COMMAND' : 'CONTROL']; + await this.pressKeys([selectionKey, 'a']); + await this.pressKeys(this.Keys.NULL); // Release modifier keys + } + /** * Types a key sequence on the DOM element represented by this instance. Modifier keys * (SHIFT, CONTROL, ALT, META) are stateful; once a modifier is processed in the key sequence, diff --git a/test/server_integration/http/platform/status.ts b/test/server_integration/http/platform/status.ts index 8ce193c670849..df472ec36d818 100644 --- a/test/server_integration/http/platform/status.ts +++ b/test/server_integration/http/platform/status.ts @@ -35,13 +35,13 @@ export default function ({ getService }: FtrProviderContext) { // This test must come first because the timeout only applies to the initial emission it("returns a timeout for status check that doesn't emit after 30s", async () => { let aStatus = await getStatus('statusPluginA'); - expect(aStatus.level).to.eql('unavailable'); + expect(aStatus === undefined || aStatus.level === 'unavailable').to.eql(true); // Status will remain in unavailable until the custom status check times out // Keep polling until that condition ends, up to a timeout await retry.waitForWithTimeout(`Status check to timeout`, 40_000, async () => { aStatus = await getStatus('statusPluginA'); - return aStatus.summary === 'Status check timed out after 30s'; + return aStatus?.summary === 'Status check timed out after 30s'; }); expect(aStatus.level).to.eql('unavailable'); @@ -53,7 +53,7 @@ export default function ({ getService }: FtrProviderContext) { await retry.waitForWithTimeout( `statusPluginA status to update`, 5_000, - async () => (await getStatus('statusPluginA')).level === 'degraded' + async () => (await getStatus('statusPluginA'))?.level === 'degraded' ); await statusPropagation(); expect((await getStatus('statusPluginA')).level).to.eql('degraded'); diff --git a/test/tsconfig.json b/test/tsconfig.json index a763d6f6a44d6..d451e64e6329b 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -71,5 +71,6 @@ "@kbn/event-annotation-plugin", "@kbn/event-annotation-common", "@kbn/links-plugin", + "@kbn/ftr-common-functional-ui-services", ] } diff --git a/tsconfig.base.json b/tsconfig.base.json index 5f25914be2352..178f70c927e28 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -106,6 +106,8 @@ "@kbn/banners-plugin/*": ["x-pack/plugins/banners/*"], "@kbn/bazel-runner": ["packages/kbn-bazel-runner"], "@kbn/bazel-runner/*": ["packages/kbn-bazel-runner/*"], + "@kbn/bfetch-error": ["packages/kbn-bfetch-error"], + "@kbn/bfetch-error/*": ["packages/kbn-bfetch-error/*"], "@kbn/bfetch-explorer-plugin": ["examples/bfetch_explorer"], "@kbn/bfetch-explorer-plugin/*": ["examples/bfetch_explorer/*"], "@kbn/bfetch-plugin": ["src/plugins/bfetch"], @@ -166,6 +168,8 @@ "@kbn/cloud-security-posture-plugin/*": ["x-pack/plugins/cloud_security_posture/*"], "@kbn/code-editor": ["packages/shared-ux/code_editor"], "@kbn/code-editor/*": ["packages/shared-ux/code_editor/*"], + "@kbn/code-owners": ["packages/kbn-code-owners"], + "@kbn/code-owners/*": ["packages/kbn-code-owners/*"], "@kbn/coloring": ["packages/kbn-coloring"], "@kbn/coloring/*": ["packages/kbn-coloring/*"], "@kbn/config": ["packages/kbn-config"], @@ -750,6 +754,8 @@ "@kbn/eslint-plugin-imports/*": ["packages/kbn-eslint-plugin-imports/*"], "@kbn/eslint-plugin-telemetry": ["packages/kbn-eslint-plugin-telemetry"], "@kbn/eslint-plugin-telemetry/*": ["packages/kbn-eslint-plugin-telemetry/*"], + "@kbn/eso-model-version-example": ["examples/eso_model_version_example"], + "@kbn/eso-model-version-example/*": ["examples/eso_model_version_example/*"], "@kbn/eso-plugin": ["x-pack/test/encrypted_saved_objects_api_integration/plugins/api_consumer_plugin"], "@kbn/eso-plugin/*": ["x-pack/test/encrypted_saved_objects_api_integration/plugins/api_consumer_plugin/*"], "@kbn/event-annotation-common": ["packages/kbn-event-annotation-common"], @@ -840,6 +846,8 @@ "@kbn/ftr-apis-plugin/*": ["src/plugins/ftr_apis/*"], "@kbn/ftr-common-functional-services": ["packages/kbn-ftr-common-functional-services"], "@kbn/ftr-common-functional-services/*": ["packages/kbn-ftr-common-functional-services/*"], + "@kbn/ftr-common-functional-ui-services": ["packages/kbn-ftr-common-functional-ui-services"], + "@kbn/ftr-common-functional-ui-services/*": ["packages/kbn-ftr-common-functional-ui-services/*"], "@kbn/ftr-screenshot-filename": ["packages/kbn-ftr-screenshot-filename"], "@kbn/ftr-screenshot-filename/*": ["packages/kbn-ftr-screenshot-filename/*"], "@kbn/functional-with-es-ssl-cases-test-plugin": ["x-pack/test/functional_with_es_ssl/plugins/cases"], @@ -1242,6 +1250,8 @@ "@kbn/rison/*": ["packages/kbn-rison/*"], "@kbn/rollup-plugin": ["x-pack/plugins/rollup"], "@kbn/rollup-plugin/*": ["x-pack/plugins/rollup/*"], + "@kbn/router-utils": ["packages/kbn-router-utils"], + "@kbn/router-utils/*": ["packages/kbn-router-utils/*"], "@kbn/routing-example-plugin": ["examples/routing_example"], "@kbn/routing-example-plugin/*": ["examples/routing_example/*"], "@kbn/rrule": ["packages/kbn-rrule"], @@ -1294,6 +1304,8 @@ "@kbn/search-api-panels/*": ["packages/kbn-search-api-panels/*"], "@kbn/search-connectors": ["packages/kbn-search-connectors"], "@kbn/search-connectors/*": ["packages/kbn-search-connectors/*"], + "@kbn/search-errors": ["packages/kbn-search-errors"], + "@kbn/search-errors/*": ["packages/kbn-search-errors/*"], "@kbn/search-examples-plugin": ["examples/search_examples"], "@kbn/search-examples-plugin/*": ["examples/search_examples/*"], "@kbn/search-index-documents": ["packages/kbn-search-index-documents"], @@ -1696,6 +1708,8 @@ "@kbn/vis-type-xy-plugin/*": ["src/plugins/vis_types/xy/*"], "@kbn/visualization-ui-components": ["packages/kbn-visualization-ui-components"], "@kbn/visualization-ui-components/*": ["packages/kbn-visualization-ui-components/*"], + "@kbn/visualization-utils": ["packages/kbn-visualization-utils"], + "@kbn/visualization-utils/*": ["packages/kbn-visualization-utils/*"], "@kbn/visualizations-plugin": ["src/plugins/visualizations"], "@kbn/visualizations-plugin/*": ["src/plugins/visualizations/*"], "@kbn/watcher-plugin": ["x-pack/plugins/watcher"], diff --git a/versions.json b/versions.json index a8ff665dcbd05..ce91f8f76bb7e 100644 --- a/versions.json +++ b/versions.json @@ -2,19 +2,19 @@ "notice": "This file is not maintained outside of the main branch and should only be used for tooling.", "versions": [ { - "version": "8.12.0", + "version": "8.13.0", "branch": "main", "currentMajor": true, "currentMinor": true }, { - "version": "8.11.2", - "branch": "8.11", + "version": "8.12.0", + "branch": "8.12", "currentMajor": true, "previousMinor": true }, { - "version": "7.17.16", + "version": "7.17.17", "branch": "7.17", "previousMajor": true } diff --git a/x-pack/README.md b/x-pack/README.md index ea8777f46b143..255a4d3e2b6e8 100644 --- a/x-pack/README.md +++ b/x-pack/README.md @@ -14,12 +14,6 @@ xpack.observability.unsafe.alertDetails.metrics.enabled: true **[For Infrastructure rule types]** In Kibana configuration, will allow the user to navigate to the new Alert Details page, instead of the Alert Flyout when clicking on `View alert details` in the Alert table -```yaml -xpack.observability.unsafe.alertDetails.logs.enabled: true -``` - -**[For Logs threshold rule type]** In Kibana configuration, will allow the user to navigate to the new Alert Details page, instead of the Alert Flyout when clicking on `View alert details` in the Alert table - ```yaml xpack.observability.unsafe.alertDetails.uptime.enabled: true ``` diff --git a/x-pack/package.json b/x-pack/package.json index 66a2b81da7dd2..47de19fb67a3c 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -1,6 +1,6 @@ { "name": "x-pack", - "version": "8.12.0", + "version": "8.13.0", "author": "Elastic", "private": true, "license": "Elastic-License", diff --git a/x-pack/packages/kbn-elastic-assistant/impl/alerts/settings/alerts_settings.tsx b/x-pack/packages/kbn-elastic-assistant/impl/alerts/settings/alerts_settings.tsx index 2e0bf6504d289..f23470bbbe7a7 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/alerts/settings/alerts_settings.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/alerts/settings/alerts_settings.tsx @@ -109,6 +109,18 @@ const AlertsSettingsComponent = ({ knowledgeBase, setUpdatedKnowledgeBaseSetting {i18n.LATEST_AND_RISKIEST_OPEN_ALERTS}
+ + + + {i18n.YOUR_ANONYMIZATION_SETTINGS} + + + + + + {i18n.SELECT_FEWER_ALERTS} + +
); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api.test.tsx index a4dd40e4f1c4d..ebb5afe2f12a1 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api.test.tsx @@ -133,6 +133,28 @@ describe('API tests', () => { ); }); + it('calls the actions connector api with invoke when assistantStreamingEnabled is true when assistantLangChain is false and alerts is true', async () => { + const testProps: FetchConnectorExecuteAction = { + ...fetchConnectorArgs, + assistantLangChain: false, + alerts: true, + }; + + await fetchConnectorExecuteAction(testProps); + + expect(mockHttp.fetch).toHaveBeenCalledWith( + '/internal/elastic_assistant/actions/connector/foo/_execute', + { + body: '{"params":{"subActionParams":{"model":"gpt-4","messages":[{"role":"user","content":"This is a test"}],"n":1,"stop":null,"temperature":0.2},"subAction":"invokeAI"},"assistantLangChain":false}', + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + signal: undefined, + } + ); + }); + it('returns API_ERROR when the response status is error and langchain is on', async () => { (mockHttp.fetch as jest.Mock).mockResolvedValue({ status: 'error' }); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api.tsx index 9b1d3d74035fe..c2bdd4806a99a 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/api.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/api.tsx @@ -84,7 +84,7 @@ export const fetchConnectorExecuteAction = async ({ // tracked here: https://github.com/elastic/security-team/issues/7363 // In part 3 I will make enhancements to langchain to introduce streaming // Once implemented, invokeAI can be removed - const isStream = assistantStreamingEnabled && !assistantLangChain; + const isStream = assistantStreamingEnabled && !assistantLangChain && !alerts; const optionalRequestParams = getOptionalRequestParams({ alerts, alertsIndexPattern, diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_selector/index.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_selector/index.test.tsx index 8cd7c1dd0bd64..4ab45c10b021a 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_selector/index.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_selector/index.test.tsx @@ -58,7 +58,7 @@ describe('Conversation selector', () => { ); expect(getByTestId('conversation-selector')).toBeInTheDocument(); - expect(getByTestId('euiComboBoxPill')).toHaveTextContent(welcomeConvo.id); + expect(getByTestId('comboBoxSearchInput')).toHaveValue(welcomeConvo.id); }); it('On change, selects new item', () => { const { getByTestId } = render( @@ -78,7 +78,7 @@ describe('Conversation selector', () => { expect(onConversationSelected).toHaveBeenCalledWith(alertConvo.id); }); it('On clear input, clears selected options', () => { - const { getByText, queryByText, getByTestId, queryByTestId } = render( + const { getByPlaceholderText, queryByPlaceholderText, getByTestId, queryByTestId } = render( ({ @@ -90,10 +90,10 @@ describe('Conversation selector', () => { ); - expect(getByTestId('euiComboBoxPill')).toBeInTheDocument(); - expect(queryByText(CONVERSATION_SELECTOR_PLACE_HOLDER)).not.toBeInTheDocument(); + expect(getByTestId('comboBoxSearchInput')).toBeInTheDocument(); + expect(queryByPlaceholderText(CONVERSATION_SELECTOR_PLACE_HOLDER)).not.toBeInTheDocument(); fireEvent.click(getByTestId('comboBoxClearButton')); - expect(getByText(CONVERSATION_SELECTOR_PLACE_HOLDER)).toBeInTheDocument(); + expect(getByPlaceholderText(CONVERSATION_SELECTOR_PLACE_HOLDER)).toBeInTheDocument(); expect(queryByTestId('euiComboBoxPill')).not.toBeInTheDocument(); }); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_selector_settings/index.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_selector_settings/index.test.tsx index afe6ede592910..150784d9db4cf 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_selector_settings/index.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/conversations/conversation_selector_settings/index.test.tsx @@ -30,7 +30,7 @@ describe('ConversationSelectorSettings', () => { }); it('Selects an existing conversation', () => { const { getByTestId } = render(); - expect(getByTestId('comboBoxInput')).toHaveTextContent(welcomeConvo.id); + expect(getByTestId('comboBoxSearchInput')).toHaveValue(welcomeConvo.id); fireEvent.click(getByTestId('comboBoxToggleListButton')); fireEvent.click(getByTestId(alertConvo.id)); expect(onConversationSelectionChange).toHaveBeenCalledWith(alertConvo); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/system_prompt_modal/system_prompt_selector/system_prompt_selector.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/system_prompt_modal/system_prompt_selector/system_prompt_selector.test.tsx index ea16553e71c6e..d8d14a8ffebe0 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/system_prompt_modal/system_prompt_selector/system_prompt_selector.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/assistant/prompt_editor/system_prompt/system_prompt_modal/system_prompt_selector/system_prompt_selector.test.tsx @@ -25,7 +25,7 @@ describe('SystemPromptSelector', () => { }); it('Selects an existing quick prompt', () => { const { getByTestId } = render(); - expect(getByTestId('euiComboBoxPill')).toHaveTextContent(mockSystemPrompts[0].name); + expect(getByTestId('comboBoxSearchInput')).toHaveValue(mockSystemPrompts[0].name); fireEvent.click(getByTestId('comboBoxToggleListButton')); fireEvent.click(getByTestId(`systemPromptSelector-${mockSystemPrompts[1].id}`)); expect(onSystemPromptSelectionChange).toHaveBeenCalledWith(mockSystemPrompts[1]); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/models/model_selector/model_selector.test.tsx b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/models/model_selector/model_selector.test.tsx index 459dfd3426584..9138c02381eb4 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/connectorland/models/model_selector/model_selector.test.tsx +++ b/x-pack/packages/kbn-elastic-assistant/impl/connectorland/models/model_selector/model_selector.test.tsx @@ -15,7 +15,7 @@ describe('ModelSelector', () => { const { getByTestId } = render( ); - expect(getByTestId('euiComboBoxPill')).toHaveTextContent(MODEL_GPT_3_5_TURBO); + expect(getByTestId('comboBoxSearchInput')).toHaveValue(MODEL_GPT_3_5_TURBO); }); it('should call onModelSelectionChange when custom option', () => { const onModelSelectionChange = jest.fn(); diff --git a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/translations.ts b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/translations.ts index 3c7cdda10f924..03e989ab6a055 100644 --- a/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/translations.ts +++ b/x-pack/packages/kbn-elastic-assistant/impl/knowledge_base/translations.ts @@ -23,9 +23,23 @@ export const ASK_QUESTIONS_ABOUT = i18n.translate( export const LATEST_AND_RISKIEST_OPEN_ALERTS = i18n.translate( 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.latestAndRiskiestOpenAlertsLabel', + { + defaultMessage: 'latest and riskiest open and acknowledged alerts in your environment.', + } +); + +export const YOUR_ANONYMIZATION_SETTINGS = i18n.translate( + 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.yourAnonymizationSettingsLabel', + { + defaultMessage: 'Your Anonymization settings will be applied to the alerts.', + } +); + +export const SELECT_FEWER_ALERTS = i18n.translate( + 'xpack.elasticAssistant.assistant.settings.knowledgeBaseSettings.selectFewerAlertsLabel', { defaultMessage: - 'latest and riskiest open alerts in your environment. Your Anonymization settings will be applied to the alerts', + "Select fewer alerts if the model's maximum context length is frequently exceeded.", } ); diff --git a/x-pack/packages/kbn-slo-schema/index.ts b/x-pack/packages/kbn-slo-schema/index.ts index 3d9e295055a61..98b183d391bb7 100644 --- a/x-pack/packages/kbn-slo-schema/index.ts +++ b/x-pack/packages/kbn-slo-schema/index.ts @@ -8,3 +8,4 @@ export * from './src/schema'; export * from './src/rest_specs'; export * from './src/models/duration'; +export * from './src/models/pagination'; diff --git a/x-pack/packages/kbn-slo-schema/src/models/pagination.ts b/x-pack/packages/kbn-slo-schema/src/models/pagination.ts new file mode 100644 index 0000000000000..815c30f71d4c4 --- /dev/null +++ b/x-pack/packages/kbn-slo-schema/src/models/pagination.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +export interface Paginated { + total: number; + page: number; + perPage: number; + results: T[]; +} + +export interface Pagination { + page: number; + perPage: number; +} diff --git a/x-pack/packages/kbn-slo-schema/src/rest_specs/slo.ts b/x-pack/packages/kbn-slo-schema/src/rest_specs/slo.ts index 54e463adc210c..574a7eb1f9244 100644 --- a/x-pack/packages/kbn-slo-schema/src/rest_specs/slo.ts +++ b/x-pack/packages/kbn-slo-schema/src/rest_specs/slo.ts @@ -6,6 +6,7 @@ */ import * as t from 'io-ts'; +import { toBooleanRt } from '@kbn/io-ts-utils'; import { allOrAnyString, apmTransactionDurationIndicatorSchema, @@ -109,6 +110,7 @@ const sloResponseSchema = t.intersection([ groupBy: allOrAnyString, createdAt: dateType, updatedAt: dateType, + version: t.number, }), t.partial({ instanceId: allOrAnyString, @@ -157,6 +159,12 @@ const manageSLOParamsSchema = t.type({ path: t.type({ id: sloIdSchema }), }); +const resetSLOParamsSchema = t.type({ + path: t.type({ id: sloIdSchema }), +}); + +const resetSLOResponseSchema = sloResponseSchema; + const updateSLOResponseSchema = sloResponseSchema; const findSLOResponseSchema = t.type({ @@ -182,23 +190,21 @@ const fetchHistoricalSummaryResponseSchema = t.array( }) ); -/** - * The query params schema for /internal/observability/slo/_definitions - * - * @private - */ -const findSloDefinitionsParamsSchema = t.type({ - query: t.type({ +const findSloDefinitionsParamsSchema = t.partial({ + query: t.partial({ search: t.string, + includeOutdatedOnly: toBooleanRt, + page: t.string, + perPage: t.string, }), }); -/** - * The response schema for /internal/observability/slo/_definitions - * - * @private - */ -const findSloDefinitionsResponseSchema = t.array(sloResponseSchema); +const findSloDefinitionsResponseSchema = t.type({ + page: t.number, + perPage: t.number, + total: t.number, + results: t.array(sloResponseSchema), +}); const getSLOBurnRatesResponseSchema = t.type({ burnRates: t.array( @@ -244,6 +250,9 @@ type GetSLOResponse = t.OutputOf; type ManageSLOParams = t.TypeOf; +type ResetSLOParams = t.TypeOf; +type ResetSLOResponse = t.OutputOf; + type UpdateSLOInput = t.OutputOf; type UpdateSLOParams = t.TypeOf; type UpdateSLOResponse = t.OutputOf; @@ -258,12 +267,8 @@ type FetchHistoricalSummaryParams = t.TypeOf; type HistoricalSummaryResponse = t.OutputOf; -/** - * The response type for /internal/observability/slo/_definitions - * - * @private - */ -type FindSloDefinitionsResponse = t.OutputOf; +type FindSLODefinitionsParams = t.TypeOf; +type FindSLODefinitionsResponse = t.OutputOf; type GetPreviewDataParams = t.TypeOf; type GetPreviewDataResponse = t.OutputOf; @@ -300,6 +305,8 @@ export { findSloDefinitionsParamsSchema, findSloDefinitionsResponseSchema, manageSLOParamsSchema, + resetSLOParamsSchema, + resetSLOResponseSchema, sloResponseSchema, sloWithSummaryResponseSchema, updateSLOParamsSchema, @@ -325,8 +332,11 @@ export type { FetchHistoricalSummaryParams, FetchHistoricalSummaryResponse, HistoricalSummaryResponse, - FindSloDefinitionsResponse, + FindSLODefinitionsParams, + FindSLODefinitionsResponse, ManageSLOParams, + ResetSLOParams, + ResetSLOResponse, SLOResponse, SLOWithSummaryResponse, UpdateSLOInput, diff --git a/x-pack/packages/kbn-slo-schema/src/schema/slo.ts b/x-pack/packages/kbn-slo-schema/src/schema/slo.ts index 29df82010710d..27e8a9e998f71 100644 --- a/x-pack/packages/kbn-slo-schema/src/schema/slo.ts +++ b/x-pack/packages/kbn-slo-schema/src/schema/slo.ts @@ -50,6 +50,7 @@ const sloSchema = t.type({ createdAt: dateType, updatedAt: dateType, groupBy: allOrAnyString, + version: t.number, }); const sloWithSummarySchema = t.intersection([sloSchema, t.type({ summary: summarySchema })]); diff --git a/x-pack/packages/kbn-slo-schema/tsconfig.json b/x-pack/packages/kbn-slo-schema/tsconfig.json index 3c94d9a902af8..bc9fd2fdede8a 100644 --- a/x-pack/packages/kbn-slo-schema/tsconfig.json +++ b/x-pack/packages/kbn-slo-schema/tsconfig.json @@ -11,7 +11,8 @@ "**/*.ts" ], "kbn_references": [ - "@kbn/std" + "@kbn/std", + "@kbn/io-ts-utils" ], "exclude": [ "target/**/*", diff --git a/x-pack/packages/ml/aiops_components/src/document_count_chart/document_count_chart.tsx b/x-pack/packages/ml/aiops_components/src/document_count_chart/document_count_chart.tsx index 7fa45baa1fdc6..615816852a13b 100644 --- a/x-pack/packages/ml/aiops_components/src/document_count_chart/document_count_chart.tsx +++ b/x-pack/packages/ml/aiops_components/src/document_count_chart/document_count_chart.tsx @@ -24,7 +24,7 @@ import { BarStyleAccessor, RectAnnotationSpec, } from '@elastic/charts/dist/chart_types/xy_chart/utils/specs'; - +import { getTimeZone } from '@kbn/visualization-utils'; import { i18n } from '@kbn/i18n'; import { IUiSettingsClient } from '@kbn/core/public'; import { @@ -146,16 +146,6 @@ enum VIEW_MODE { BRUSH = 'brush', } -function getTimezone(uiSettings: IUiSettingsClient) { - if (uiSettings.isDefault('dateFormat:tz')) { - const detectedTimezone = moment.tz.guess(); - if (detectedTimezone) return detectedTimezone; - else return moment().format('Z'); - } else { - return uiSettings.get('dateFormat:tz', 'Browser'); - } -} - function getBaselineBadgeOverflow( windowParametersAsPixels: WindowParameters, baselineBadgeWidth: number @@ -200,7 +190,6 @@ export const DocumentCountChart: FC = (props) => { const { data, uiSettings, fieldFormats, charts } = dependencies; - const chartTheme = charts.theme.useChartsTheme(); const chartBaseTheme = charts.theme.useChartsBaseTheme(); const xAxisFormatter = fieldFormats.deserialize({ id: 'date' }); @@ -297,7 +286,7 @@ export const DocumentCountChart: FC = (props) => { timefilterUpdateHandler({ from, to }); }; - const timeZone = getTimezone(uiSettings); + const timeZone = getTimeZone(uiSettings); const [originalWindowParameters, setOriginalWindowParameters] = useState< WindowParameters | undefined @@ -484,7 +473,6 @@ export const DocumentCountChart: FC = (props) => { setMlBrushMarginLeft(projection.left); setMlBrushWidth(projection.width); }} - theme={chartTheme} baseTheme={chartBaseTheme} debugState={window._echDebugStateFlag ?? false} showLegend={false} @@ -509,6 +497,7 @@ export const DocumentCountChart: FC = (props) => { yScaleType={ScaleType.Linear} xAccessor="time" yAccessors={['value']} + stackAccessors={['true']} data={adjustedChartPoints} timeZone={timeZone} color={barColor} @@ -524,6 +513,7 @@ export const DocumentCountChart: FC = (props) => { yScaleType={ScaleType.Linear} xAccessor="time" yAccessors={['value']} + stackAccessors={['true']} data={adjustedChartPointsSplit} timeZone={timeZone} color={barHighlightColor} diff --git a/x-pack/packages/ml/aiops_components/tsconfig.json b/x-pack/packages/ml/aiops_components/tsconfig.json index 5e4ebc04b91aa..2b3f56be981fe 100644 --- a/x-pack/packages/ml/aiops_components/tsconfig.json +++ b/x-pack/packages/ml/aiops_components/tsconfig.json @@ -26,6 +26,7 @@ "@kbn/charts-plugin", "@kbn/data-plugin", "@kbn/field-formats-plugin", + "@kbn/visualization-utils", ], "exclude": [ "target/**/*", diff --git a/x-pack/packages/ml/data_grid/components/column_chart.tsx b/x-pack/packages/ml/data_grid/components/column_chart.tsx index 9d71c16224b0c..9f8b866bea930 100644 --- a/x-pack/packages/ml/data_grid/components/column_chart.tsx +++ b/x-pack/packages/ml/data_grid/components/column_chart.tsx @@ -8,7 +8,7 @@ import React, { type FC } from 'react'; import { css } from '@emotion/react'; -import { BarSeries, Chart, Settings, ScaleType } from '@elastic/charts'; +import { BarSeries, Chart, Settings, ScaleType, LEGACY_LIGHT_THEME } from '@elastic/charts'; import { euiTextTruncate, type EuiDataGridColumn } from '@elastic/eui'; import { euiThemeVars } from '@kbn/ui-theme'; @@ -81,8 +81,9 @@ export const ColumnChart: FC = ({
= Object defaultMessage: 'E5 (EmbEddings from bidirEctional Encoder rEpresentations)', }), license: 'MIT', + licenseUrl: 'https://huggingface.co/elastic/multilingual-e5-small', type: ['pytorch', 'text_embedding'], }, '.multilingual-e5-small_linux-x86_64': { @@ -122,6 +123,7 @@ export const ELASTIC_MODEL_DEFINITIONS: Record = Object 'E5 (EmbEddings from bidirEctional Encoder rEpresentations), optimized for linux-x86_64', }), license: 'MIT', + licenseUrl: 'https://huggingface.co/elastic/multilingual-e5-small_linux-x86_64', type: ['pytorch', 'text_embedding'], }, } as const); @@ -142,9 +144,13 @@ export interface ModelDefinition { os?: string; arch?: string; default?: boolean; + /** Indicates if model version is recommended for deployment based on the cluster configuration */ recommended?: boolean; hidden?: boolean; + /** Software license of a model, e.g. MIT */ license?: string; + /** Link to the external license/documentation page */ + licenseUrl?: string; type?: readonly string[]; } diff --git a/x-pack/performance/journeys/apm_service_inventory.ts b/x-pack/performance/journeys/apm_service_inventory.ts index cbef53858eea6..84f2518575df8 100644 --- a/x-pack/performance/journeys/apm_service_inventory.ts +++ b/x-pack/performance/journeys/apm_service_inventory.ts @@ -11,6 +11,8 @@ import { SynthtraceClient } from '../services/synthtrace'; import { generateData } from '../synthtrace_data/apm_data'; export const journey = new Journey({ + // FAILING VERSION BUMP: https://github.com/elastic/kibana/issues/172757 + skipped: true, beforeSteps: async ({ kbnUrl, log, auth, es }) => { // Install APM Package const synthClient = new SynthtraceClient({ diff --git a/x-pack/performance/journeys/many_fields_transform.ts b/x-pack/performance/journeys/many_fields_transform.ts index 14187c20e5c59..6fe945914c358 100644 --- a/x-pack/performance/journeys/many_fields_transform.ts +++ b/x-pack/performance/journeys/many_fields_transform.ts @@ -15,11 +15,11 @@ export const journey = new Journey({ .step('Go to Transforms', async ({ page, kbnUrl, kibanaPage }) => { await page.goto(kbnUrl.get(`app/management/data/transform`)); await kibanaPage.waitForHeader(); - await page.waitForSelector(subj('transformButtonCreate')); + await page.waitForSelector(subj('transformCreateFirstButton')); await page.waitForSelector(subj('globalLoadingIndicator-hidden')); }) .step('Go to data view selection', async ({ page }) => { - const createButtons = page.locator(subj('transformButtonCreate')); + const createButtons = page.locator(subj('transformCreateFirstButton')); await createButtons.first().click(); await page.waitForSelector(subj('savedObjectsFinderTable')); }) diff --git a/x-pack/plugins/actions/server/actions_client/actions_client.test.ts b/x-pack/plugins/actions/server/actions_client/actions_client.test.ts index 35860bdfb0c53..02c647f17664d 100644 --- a/x-pack/plugins/actions/server/actions_client/actions_client.test.ts +++ b/x-pack/plugins/actions/server/actions_client/actions_client.test.ts @@ -43,7 +43,7 @@ import { actionsAuthorizationMock } from '../authorization/actions_authorization import { trackLegacyRBACExemption } from '../lib/track_legacy_rbac_exemption'; import { ConnectorTokenClient } from '../lib/connector_token_client'; import { encryptedSavedObjectsMock } from '@kbn/encrypted-saved-objects-plugin/server/mocks'; -import { Logger } from '@kbn/core/server'; +import { Logger, SavedObject } from '@kbn/core/server'; import { connectorTokenClientMock } from '../lib/connector_token_client.mock'; import { inMemoryMetricsMock } from '../monitoring/in_memory_metrics.mock'; import { getOAuthJwtAccessToken } from '../lib/get_oauth_jwt_access_token'; @@ -120,6 +120,14 @@ const executor: ExecutorType<{}, {}, {}, void> = async (options) => { const connectorTokenClient = connectorTokenClientMock.create(); const inMemoryMetrics = inMemoryMetricsMock.create(); +const actionTypeIdFromSavedObjectMock = (actionTypeId: string = 'my-action-type') => { + return { + attributes: { + actionTypeId, + }, + } as SavedObject; +}; + beforeEach(() => { jest.resetAllMocks(); mockedLicenseState = licenseStateMock.create(); @@ -2664,6 +2672,7 @@ describe('execute()', () => { (getAuthorizationModeBySource as jest.Mock).mockImplementationOnce(() => { return AuthorizationMode.RBAC; }); + unsecuredSavedObjectsClient.get.mockResolvedValueOnce(actionTypeIdFromSavedObjectMock()); await actionsClient.execute({ actionId: 'action-id', params: { @@ -2672,6 +2681,7 @@ describe('execute()', () => { source: asHttpRequestExecutionSource(request), }); expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ + actionTypeId: 'my-action-type', operation: 'execute', additionalPrivileges: [], }); @@ -2685,6 +2695,8 @@ describe('execute()', () => { new Error(`Unauthorized to execute all actions`) ); + unsecuredSavedObjectsClient.get.mockResolvedValueOnce(actionTypeIdFromSavedObjectMock()); + await expect( actionsClient.execute({ actionId: 'action-id', @@ -2696,6 +2708,7 @@ describe('execute()', () => { ).rejects.toMatchInlineSnapshot(`[Error: Unauthorized to execute all actions]`); expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ + actionTypeId: 'my-action-type', operation: 'execute', additionalPrivileges: [], }); @@ -2768,12 +2781,15 @@ describe('execute()', () => { executor, }); + unsecuredSavedObjectsClient.get.mockResolvedValueOnce(actionTypeIdFromSavedObjectMock()); + await actionsClient.execute({ actionId: 'system-connector-.cases', params: {}, }); expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ + actionTypeId: 'my-action-type', operation: 'execute', additionalPrivileges: ['test/create'], }); @@ -2832,12 +2848,15 @@ describe('execute()', () => { executor, }); + unsecuredSavedObjectsClient.get.mockResolvedValueOnce(actionTypeIdFromSavedObjectMock()); + await actionsClient.execute({ actionId: 'testPreconfigured', params: {}, }); expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ + actionTypeId: 'my-action-type', operation: 'execute', additionalPrivileges: [], }); @@ -2895,12 +2914,15 @@ describe('execute()', () => { executor, }); + unsecuredSavedObjectsClient.get.mockResolvedValueOnce(actionTypeIdFromSavedObjectMock()); + await actionsClient.execute({ actionId: 'system-connector-.cases', params: { foo: 'bar' }, }); expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ + actionTypeId: 'my-action-type', operation: 'execute', additionalPrivileges: ['test/create'], }); @@ -3032,6 +3054,7 @@ describe('bulkEnqueueExecution()', () => { }, ]); expect(authorization.ensureAuthorized).toHaveBeenCalledWith({ + actionTypeId: 'my-action-type', operation: 'execute', }); }); diff --git a/x-pack/plugins/actions/server/actions_client/actions_client.ts b/x-pack/plugins/actions/server/actions_client/actions_client.ts index 85376e64897ad..a5314ab2d7d3d 100644 --- a/x-pack/plugins/actions/server/actions_client/actions_client.ts +++ b/x-pack/plugins/actions/server/actions_client/actions_client.ts @@ -11,7 +11,7 @@ import url from 'url'; import { UsageCounter } from '@kbn/usage-collection-plugin/server'; import { i18n } from '@kbn/i18n'; -import { omitBy, isUndefined, compact } from 'lodash'; +import { omitBy, isUndefined, compact, uniq } from 'lodash'; import { IScopedClusterClient, SavedObjectsClientContract, @@ -681,14 +681,39 @@ export class ActionsClient { }: Omit): Promise< ActionTypeExecutorResult > { + const log = this.context.logger; + if ( (await getAuthorizationModeBySource(this.context.unsecuredSavedObjectsClient, source)) === AuthorizationMode.RBAC ) { const additionalPrivileges = this.getSystemActionKibanaPrivileges(actionId, params); + let actionTypeId: string | undefined; + + try { + if (this.isPreconfigured(actionId)) { + const connector = this.context.inMemoryConnectors.find( + (inMemoryConnector) => inMemoryConnector.id === actionId + ); + + actionTypeId = connector?.actionTypeId; + } else { + // TODO: Optimize so we don't do another get on top of getAuthorizationModeBySource and within the actionExecutor.execute + const { attributes } = await this.context.unsecuredSavedObjectsClient.get( + 'action', + actionId + ); + + actionTypeId = attributes.actionTypeId; + } + } catch (err) { + log.debug(`Failed to retrieve actionTypeId for action [${actionId}]`, err); + } + await this.context.authorization.ensureAuthorized({ operation: 'execute', additionalPrivileges, + actionTypeId, }); } else { trackLegacyRBACExemption('execute', this.context.usageCounter); @@ -723,6 +748,11 @@ export class ActionsClient { * inside the ActionExecutor at execution time */ await this.context.authorization.ensureAuthorized({ operation: 'execute' }); + await Promise.all( + uniq(options.map((o) => o.actionTypeId)).map((actionTypeId) => + this.context.authorization.ensureAuthorized({ operation: 'execute', actionTypeId }) + ) + ); } if (authModes[AuthorizationMode.Legacy] > 0) { trackLegacyRBACExemption( @@ -740,7 +770,10 @@ export class ActionsClient { (await getAuthorizationModeBySource(this.context.unsecuredSavedObjectsClient, source)) === AuthorizationMode.RBAC ) { - await this.context.authorization.ensureAuthorized({ operation: 'execute' }); + await this.context.authorization.ensureAuthorized({ + operation: 'execute', + actionTypeId: options.actionTypeId, + }); } else { trackLegacyRBACExemption('ephemeralEnqueuedExecution', this.context.usageCounter); } diff --git a/x-pack/plugins/actions/server/authorization/actions_authorization.test.ts b/x-pack/plugins/actions/server/authorization/actions_authorization.test.ts index fa65b06777f98..f9f2094ba05ba 100644 --- a/x-pack/plugins/actions/server/authorization/actions_authorization.test.ts +++ b/x-pack/plugins/actions/server/authorization/actions_authorization.test.ts @@ -14,10 +14,16 @@ import { } from '../constants/saved_objects'; import { AuthenticatedUser } from '@kbn/security-plugin/server'; import { AuthorizationMode } from './get_authorization_mode_by_source'; +import { + CONNECTORS_ADVANCED_EXECUTE_PRIVILEGE_API_TAG, + CONNECTORS_BASIC_EXECUTE_PRIVILEGE_API_TAG, +} from '../feature'; const request = {} as KibanaRequest; const mockAuthorizationAction = (type: string, operation: string) => `${type}/${operation}`; +const BASIC_EXECUTE_AUTHZ = `api:${CONNECTORS_BASIC_EXECUTE_PRIVILEGE_API_TAG}`; +const ADVANCED_EXECUTE_AUTHZ = `api:${CONNECTORS_ADVANCED_EXECUTE_PRIVILEGE_API_TAG}`; function mockSecurity() { const security = securityMock.createSetup(); @@ -83,7 +89,7 @@ describe('ensureAuthorized', () => { expect(authorization.actions.savedObject.get).toHaveBeenCalledWith('action', 'create'); expect(checkPrivileges).toHaveBeenCalledWith({ - kibana: [mockAuthorizationAction('action', 'create')], + kibana: [mockAuthorizationAction('action', 'create'), BASIC_EXECUTE_AUTHZ], }); }); @@ -123,6 +129,7 @@ describe('ensureAuthorized', () => { kibana: [ mockAuthorizationAction(ACTION_SAVED_OBJECT_TYPE, 'get'), mockAuthorizationAction(ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE, 'create'), + BASIC_EXECUTE_AUTHZ, ], }); }); @@ -225,6 +232,54 @@ describe('ensureAuthorized', () => { mockAuthorizationAction(ACTION_SAVED_OBJECT_TYPE, 'get'), mockAuthorizationAction(ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE, 'create'), 'test/create', + BASIC_EXECUTE_AUTHZ, + ], + }); + }); + + test('checks SentinelOne connector privileges correctly', async () => { + const { authorization } = mockSecurity(); + const checkPrivileges: jest.MockedFunction< + ReturnType + > = jest.fn(); + + authorization.checkPrivilegesDynamicallyWithRequest.mockReturnValue(checkPrivileges); + const actionsAuthorization = new ActionsAuthorization({ + request, + authorization, + }); + + checkPrivileges.mockResolvedValueOnce({ + username: 'some-user', + hasAllRequested: true, + privileges: [ + { + privilege: mockAuthorizationAction('myType', 'execute'), + authorized: true, + }, + ], + }); + + await actionsAuthorization.ensureAuthorized({ + operation: 'execute', + actionTypeId: '.sentinelone', + }); + + expect(authorization.actions.savedObject.get).toHaveBeenCalledWith( + ACTION_SAVED_OBJECT_TYPE, + 'get' + ); + + expect(authorization.actions.savedObject.get).toHaveBeenCalledWith( + ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE, + 'create' + ); + + expect(checkPrivileges).toHaveBeenCalledWith({ + kibana: [ + mockAuthorizationAction(ACTION_SAVED_OBJECT_TYPE, 'get'), + mockAuthorizationAction(ACTION_TASK_PARAMS_SAVED_OBJECT_TYPE, 'create'), + ADVANCED_EXECUTE_AUTHZ, ], }); }); diff --git a/x-pack/plugins/actions/server/authorization/actions_authorization.ts b/x-pack/plugins/actions/server/authorization/actions_authorization.ts index 34eec819b431b..df282b95e9686 100644 --- a/x-pack/plugins/actions/server/authorization/actions_authorization.ts +++ b/x-pack/plugins/actions/server/authorization/actions_authorization.ts @@ -74,7 +74,15 @@ export class ActionsAuthorization { : [authorization.actions.savedObject.get(ACTION_SAVED_OBJECT_TYPE, operation)]; const { hasAllRequested } = await checkPrivileges({ - kibana: [...privileges, ...additionalPrivileges], + kibana: [ + ...privileges, + ...additionalPrivileges, + // SentinelOne sub-actions require that a user have `all` privilege to Actions and Connectors. + // This is a temporary solution until a more robust RBAC approach can be implemented for sub-actions + actionTypeId === '.sentinelone' + ? 'api:actions:execute-advanced-connectors' + : 'api:actions:execute-basic-connectors', + ], }); if (!hasAllRequested) { throw Boom.forbidden( diff --git a/x-pack/plugins/actions/server/feature.ts b/x-pack/plugins/actions/server/feature.ts index aa0b88dbee633..b44aaf61cad62 100644 --- a/x-pack/plugins/actions/server/feature.ts +++ b/x-pack/plugins/actions/server/feature.ts @@ -13,6 +13,9 @@ import { CONNECTOR_TOKEN_SAVED_OBJECT_TYPE, } from './constants/saved_objects'; +export const CONNECTORS_ADVANCED_EXECUTE_PRIVILEGE_API_TAG = 'actions:execute-advanced-connectors'; +export const CONNECTORS_BASIC_EXECUTE_PRIVILEGE_API_TAG = 'actions:execute-basic-connectors'; + /** * The order of appearance in the feature privilege page * under the management section. @@ -33,7 +36,10 @@ export const ACTIONS_FEATURE = { privileges: { all: { app: [], - api: [], + api: [ + CONNECTORS_ADVANCED_EXECUTE_PRIVILEGE_API_TAG, + CONNECTORS_BASIC_EXECUTE_PRIVILEGE_API_TAG, + ], catalogue: [], management: { insightsAndAlerting: ['triggersActions', 'triggersActionsConnectors'], @@ -50,7 +56,7 @@ export const ACTIONS_FEATURE = { }, read: { app: [], - api: [], + api: [CONNECTORS_BASIC_EXECUTE_PRIVILEGE_API_TAG], catalogue: [], management: { insightsAndAlerting: ['triggersActions', 'triggersActionsConnectors'], diff --git a/x-pack/plugins/actions/server/lib/action_executor.test.ts b/x-pack/plugins/actions/server/lib/action_executor.test.ts index 9e277b9084329..c58e16a11aa4a 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.test.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.test.ts @@ -835,6 +835,7 @@ test('successfully authorize system actions', async () => { await actionExecutor.execute({ ...executeParams, actionId: 'system-connector-.cases' }); expect(authorizationMock.ensureAuthorized).toBeCalledWith({ + actionTypeId: '.cases', operation: 'execute', additionalPrivileges: ['test/create'], }); @@ -875,7 +876,10 @@ test('Execute of SentinelOne sub-actions require create privilege', async () => await actionExecutor.execute({ ...executeParams, actionId: 'sentinel-one-connector-authz' }); - expect(authorizationMock.ensureAuthorized).toHaveBeenCalledWith({ operation: 'create' }); + expect(authorizationMock.ensureAuthorized).toHaveBeenCalledWith({ + operation: 'execute', + actionTypeId: '.sentinelone', + }); }); test('pass the params to the actionTypeRegistry when authorizing system actions', async () => { @@ -909,6 +913,7 @@ test('pass the params to the actionTypeRegistry when authorizing system actions' }); expect(authorizationMock.ensureAuthorized).toBeCalledWith({ + actionTypeId: '.cases', operation: 'execute', additionalPrivileges: ['test/create'], }); diff --git a/x-pack/plugins/actions/server/lib/action_executor.ts b/x-pack/plugins/actions/server/lib/action_executor.ts index fd1d8f92a0482..69aab56e4e5b3 100644 --- a/x-pack/plugins/actions/server/lib/action_executor.ts +++ b/x-pack/plugins/actions/server/lib/action_executor.ts @@ -565,14 +565,17 @@ const ensureAuthorizedToExecute = async ({ params ); - await authorization.ensureAuthorized({ operation: 'execute', additionalPrivileges }); - } - - // SentinelOne sub-actions require that a user have `all` privilege to Actions and Connectors. - // This is a temporary solution until a more robust RBAC approach can be implemented for sub-actions - if (actionTypeId === '.sentinelone') { await authorization.ensureAuthorized({ - operation: 'create', + operation: 'execute', + additionalPrivileges, + actionTypeId, + }); + } else if (actionTypeId === '.sentinelone') { + // SentinelOne sub-actions require that a user have `all` privilege to Actions and Connectors. + // This is a temporary solution until a more robust RBAC approach can be implemented for sub-actions + await authorization.ensureAuthorized({ + operation: 'execute', + actionTypeId, }); } } catch (error) { diff --git a/x-pack/plugins/aiops/public/components/mini_histogram/mini_histogram.tsx b/x-pack/plugins/aiops/public/components/mini_histogram/mini_histogram.tsx index 6f1564cb6f86c..6dd730f1e694e 100644 --- a/x-pack/plugins/aiops/public/components/mini_histogram/mini_histogram.tsx +++ b/x-pack/plugins/aiops/public/components/mini_histogram/mini_histogram.tsx @@ -46,7 +46,7 @@ export const MiniHistogram: FC = ({ const { charts } = useAiopsAppContext(); const euiTheme = useEuiTheme(); - const defaultChartTheme = charts.theme.useChartsTheme(); + const chartBaseTheme = charts.theme.useChartsBaseTheme(); const miniHistogramChartTheme: PartialTheme = { chartMargins: { @@ -107,7 +107,8 @@ export const MiniHistogram: FC = ({ diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.test.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.test.tsx index 3fea6d6611f08..a354e47975baf 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.test.tsx +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/create_maintenance_windows_form.test.tsx @@ -131,7 +131,9 @@ describe('CreateMaintenanceWindowForm', () => { 'Press the down key to open a popover containing a calendar.' ); const recurringInput = within(result.getByTestId('recurring-field')).getByTestId('input'); - const timezoneInput = within(result.getByTestId('timezone-field')).getByTestId('input'); + const timezoneInput = within(result.getByTestId('timezone-field')).getByTestId( + 'comboBoxSearchInput' + ); await waitFor(() => { expect( @@ -156,7 +158,7 @@ describe('CreateMaintenanceWindowForm', () => { expect(dateInputs[0]).toHaveValue('03/23/2023 09:00 PM'); expect(dateInputs[1]).toHaveValue('03/25/2023 09:00 PM'); expect(recurringInput).toBeChecked(); - expect(timezoneInput).toHaveTextContent('America/Los_Angeles'); + expect(timezoneInput).toHaveValue('America/Los_Angeles'); }); it('should initialize MWs without category ids properly', async () => { diff --git a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/recurring_schedule_form/recurring_schedule.test.tsx b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/recurring_schedule_form/recurring_schedule.test.tsx index 6422d285db7ef..76390aea7ef18 100644 --- a/x-pack/plugins/alerting/public/pages/maintenance_windows/components/recurring_schedule_form/recurring_schedule.test.tsx +++ b/x-pack/plugins/alerting/public/pages/maintenance_windows/components/recurring_schedule_form/recurring_schedule.test.tsx @@ -94,7 +94,7 @@ describe('RecurringSchedule', () => { const endsInput = within(result.getByTestId('ends-field')).getByTestId('never'); expect(frequencyInput).toHaveValue('3'); - expect(endsInput).toBeChecked(); + expect(endsInput).toHaveAttribute('aria-pressed', 'true'); }); it('should prefill the form when provided with initialValue', () => { @@ -119,7 +119,7 @@ describe('RecurringSchedule', () => { 'Press the down key to open a popover containing a calendar.' ); expect(frequencyInput).toHaveValue('1'); - expect(endsInput).toBeChecked(); + expect(endsInput).toHaveAttribute('aria-pressed', 'true'); expect(untilInput).toHaveValue('03/24/2023'); }); }); diff --git a/x-pack/plugins/alerting/server/application/rule/methods/aggregate/aggregate_rules.test.ts b/x-pack/plugins/alerting/server/application/rule/methods/aggregate/aggregate_rules.test.ts index 31df2212bb5b5..d230f18f7ea60 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/aggregate/aggregate_rules.test.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/aggregate/aggregate_rules.test.ts @@ -26,6 +26,7 @@ import { fromKueryExpression, nodeTypes } from '@kbn/es-query'; import { RecoveredActionGroup } from '../../../../../common'; import { DefaultRuleAggregationResult } from '../../../../routes/rule/apis/aggregate/types'; import { defaultRuleAggregationFactory } from '.'; +import { RULE_SAVED_OBJECT_TYPE } from '../../../../saved_objects'; const taskManager = taskManagerMock.createStart(); const ruleTypeRegistry = ruleTypeRegistryMock.create(); @@ -289,7 +290,7 @@ describe('aggregate()', () => { filter: undefined, page: 1, perPage: 0, - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, aggs: { status: { terms: { field: 'alert.attributes.executionStatus.status' }, @@ -350,7 +351,7 @@ describe('aggregate()', () => { ]), page: 1, perPage: 0, - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, aggs: { status: { terms: { field: 'alert.attributes.executionStatus.status' }, diff --git a/x-pack/plugins/alerting/server/application/rule/methods/bulk_delete/bulk_delete_rules.test.ts b/x-pack/plugins/alerting/server/application/rule/methods/bulk_delete/bulk_delete_rules.test.ts index d5f18db2f1065..c3cbfd340984c 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/bulk_delete/bulk_delete_rules.test.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/bulk_delete/bulk_delete_rules.test.ts @@ -30,6 +30,7 @@ import { siemRuleForBulkOps1, } from '../../../../rules_client/tests/test_helpers'; import { migrateLegacyActions } from '../../../../rules_client/lib'; +import { RULE_SAVED_OBJECT_TYPE } from '../../../../saved_objects'; jest.mock('../../../../rules_client/lib/siem_legacy_actions/migrate_legacy_actions', () => { return { @@ -85,7 +86,7 @@ const rulesClientParams: jest.Mocked = { const getBulkOperationStatusErrorResponse = (statusCode: number) => ({ id: 'id2', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, success: false, error: { error: '', @@ -164,9 +165,9 @@ describe('bulkDelete', () => { test('should try to delete rules, two successful and one with 500 error', async () => { unsecuredSavedObjectsClient.bulkDelete.mockResolvedValue({ statuses: [ - { id: 'id1', type: 'alert', success: true }, + { id: 'id1', type: RULE_SAVED_OBJECT_TYPE, success: true }, getBulkOperationStatusErrorResponse(500), - { id: 'id3', type: 'alert', success: true }, + { id: 'id3', type: RULE_SAVED_OBJECT_TYPE, success: true }, ], }); @@ -176,7 +177,7 @@ describe('bulkDelete', () => { expect(unsecuredSavedObjectsClient.bulkDelete).toHaveBeenCalledWith( [enabledRuleForBulkOps1, enabledRuleForBulkOps2, enabledRuleForBulkOps3].map(({ id }) => ({ id, - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, })), undefined ); @@ -201,7 +202,7 @@ describe('bulkDelete', () => { unsecuredSavedObjectsClient.bulkDelete .mockResolvedValueOnce({ statuses: [ - { id: 'id1', type: 'alert', success: true }, + { id: 'id1', type: RULE_SAVED_OBJECT_TYPE, success: true }, getBulkOperationStatusErrorResponse(409), ], }) @@ -265,7 +266,7 @@ describe('bulkDelete', () => { unsecuredSavedObjectsClient.bulkDelete .mockResolvedValueOnce({ statuses: [ - { id: 'id1', type: 'alert', success: true }, + { id: 'id1', type: RULE_SAVED_OBJECT_TYPE, success: true }, getBulkOperationStatusErrorResponse(409), ], }) @@ -273,7 +274,7 @@ describe('bulkDelete', () => { statuses: [ { id: 'id2', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, success: true, }, ], @@ -356,9 +357,9 @@ describe('bulkDelete', () => { test('should not mark API keys for invalidation if the user is authenticated using an api key', async () => { unsecuredSavedObjectsClient.bulkDelete.mockResolvedValue({ statuses: [ - { id: 'id3', type: 'alert', success: true }, - { id: 'id1', type: 'alert', success: true }, - { id: 'id2', type: 'alert', success: true }, + { id: 'id3', type: RULE_SAVED_OBJECT_TYPE, success: true }, + { id: 'id1', type: RULE_SAVED_OBJECT_TYPE, success: true }, + { id: 'id2', type: RULE_SAVED_OBJECT_TYPE, success: true }, ], }); @@ -376,15 +377,15 @@ describe('bulkDelete', () => { test('should return task id if deleting task failed', async () => { unsecuredSavedObjectsClient.bulkDelete.mockResolvedValue({ statuses: [ - { id: 'id1', type: 'alert', success: true }, - { id: 'id2', type: 'alert', success: true }, + { id: 'id1', type: RULE_SAVED_OBJECT_TYPE, success: true }, + { id: 'id2', type: RULE_SAVED_OBJECT_TYPE, success: true }, ], }); taskManager.bulkRemove.mockImplementation(async () => ({ statuses: [ { id: 'id1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, success: true, }, getBulkOperationStatusErrorResponse(500), @@ -404,8 +405,8 @@ describe('bulkDelete', () => { test('should not throw an error if taskManager throw an error', async () => { unsecuredSavedObjectsClient.bulkDelete.mockResolvedValue({ statuses: [ - { id: 'id1', type: 'alert', success: true }, - { id: 'id2', type: 'alert', success: true }, + { id: 'id1', type: RULE_SAVED_OBJECT_TYPE, success: true }, + { id: 'id2', type: RULE_SAVED_OBJECT_TYPE, success: true }, ], }); taskManager.bulkRemove.mockImplementation(() => { @@ -424,20 +425,20 @@ describe('bulkDelete', () => { mockCreatePointInTimeFinderAsInternalUser(); unsecuredSavedObjectsClient.bulkDelete.mockResolvedValue({ statuses: [ - { id: 'id1', type: 'alert', success: true }, - { id: 'id2', type: 'alert', success: true }, + { id: 'id1', type: RULE_SAVED_OBJECT_TYPE, success: true }, + { id: 'id2', type: RULE_SAVED_OBJECT_TYPE, success: true }, ], }); taskManager.bulkRemove.mockImplementation(async () => ({ statuses: [ { id: 'id1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, success: true, }, { id: 'id2', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, success: true, }, ], @@ -468,9 +469,9 @@ describe('bulkDelete', () => { unsecuredSavedObjectsClient.bulkDelete.mockResolvedValue({ statuses: [ - { id: enabledRuleForBulkOps1.id, type: 'alert', success: true }, - { id: enabledRuleForBulkOps2.id, type: 'alert', success: true }, - { id: siemRuleForBulkOps1.id, type: 'alert', success: true }, + { id: enabledRuleForBulkOps1.id, type: RULE_SAVED_OBJECT_TYPE, success: true }, + { id: enabledRuleForBulkOps2.id, type: RULE_SAVED_OBJECT_TYPE, success: true }, + { id: siemRuleForBulkOps1.id, type: RULE_SAVED_OBJECT_TYPE, success: true }, ], }); @@ -501,8 +502,8 @@ describe('bulkDelete', () => { test('logs audit event when deleting rules', async () => { unsecuredSavedObjectsClient.bulkDelete.mockResolvedValue({ statuses: [ - { id: 'id1', type: 'alert', success: true }, - { id: 'id2', type: 'alert', success: true }, + { id: 'id1', type: RULE_SAVED_OBJECT_TYPE, success: true }, + { id: 'id2', type: RULE_SAVED_OBJECT_TYPE, success: true }, ], }); @@ -511,12 +512,12 @@ describe('bulkDelete', () => { expect(auditLogger.log.mock.calls[0][0]?.event?.action).toEqual('rule_delete'); expect(auditLogger.log.mock.calls[0][0]?.event?.outcome).toEqual('unknown'); expect(auditLogger.log.mock.calls[0][0]?.kibana).toEqual({ - saved_object: { id: 'id1', type: 'alert' }, + saved_object: { id: 'id1', type: RULE_SAVED_OBJECT_TYPE }, }); expect(auditLogger.log.mock.calls[1][0]?.event?.action).toEqual('rule_delete'); expect(auditLogger.log.mock.calls[1][0]?.event?.outcome).toEqual('unknown'); expect(auditLogger.log.mock.calls[1][0]?.kibana).toEqual({ - saved_object: { id: 'id2', type: 'alert' }, + saved_object: { id: 'id2', type: RULE_SAVED_OBJECT_TYPE }, }); }); @@ -525,7 +526,7 @@ describe('bulkDelete', () => { throw new Error('Unauthorized'); }); unsecuredSavedObjectsClient.bulkDelete.mockResolvedValue({ - statuses: [{ id: 'id1', type: 'alert', success: true }], + statuses: [{ id: 'id1', type: RULE_SAVED_OBJECT_TYPE, success: true }], }); await expect(rulesClient.bulkDeleteRules({ filter: 'fake_filter' })).rejects.toThrowError( @@ -541,7 +542,7 @@ describe('bulkDelete', () => { throw new Error('Error'); }); unsecuredSavedObjectsClient.bulkDelete.mockResolvedValue({ - statuses: [{ id: 'id1', type: 'alert', success: true }], + statuses: [{ id: 'id1', type: RULE_SAVED_OBJECT_TYPE, success: true }], }); await expect(rulesClient.bulkDeleteRules({ filter: 'fake_filter' })).rejects.toThrowError( diff --git a/x-pack/plugins/alerting/server/application/rule/methods/bulk_delete/bulk_delete_rules.ts b/x-pack/plugins/alerting/server/application/rule/methods/bulk_delete/bulk_delete_rules.ts index 33da0fd6b2e07..429afed34926c 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/bulk_delete/bulk_delete_rules.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/bulk_delete/bulk_delete_rules.ts @@ -9,6 +9,7 @@ import Boom from '@hapi/boom'; import { KueryNode, nodeBuilder } from '@kbn/es-query'; import { SavedObjectsBulkUpdateObject } from '@kbn/core/server'; import { withSpan } from '@kbn/apm-utils'; +import { RULE_SAVED_OBJECT_TYPE } from '../../../../saved_objects'; import { convertRuleIdsToKueryNode } from '../../../../lib'; import { bulkMarkApiKeysForInvalidation } from '../../../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation'; import { ruleAuditEvent, RuleAuditAction } from '../../../../rules_client/common/audit_events'; @@ -136,7 +137,7 @@ const bulkDeleteWithOCC = async ( context.encryptedSavedObjectsClient.createPointInTimeFinderDecryptedAsInternalUser( { filter, - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, perPage: 100, ...(context.namespace ? { namespaces: [context.namespace] } : undefined), } @@ -168,7 +169,7 @@ const bulkDeleteWithOCC = async ( ruleAuditEvent({ action: RuleAuditAction.DELETE, outcome: 'unknown', - savedObject: { type: 'alert', id: rule.id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id: rule.id }, }) ); } diff --git a/x-pack/plugins/alerting/server/application/rule/methods/bulk_disable/bulk_disable_rules.test.ts b/x-pack/plugins/alerting/server/application/rule/methods/bulk_disable/bulk_disable_rules.test.ts index f7e8eff33591f..2b7c72fd4c51b 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/bulk_disable/bulk_disable_rules.test.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/bulk_disable/bulk_disable_rules.test.ts @@ -35,6 +35,7 @@ import { siemRuleForBulkOps2, } from '../../../../rules_client/tests/test_helpers'; import { migrateLegacyActions } from '../../../../rules_client/lib'; +import { RULE_SAVED_OBJECT_TYPE } from '../../../../saved_objects'; jest.mock('../../../../task_runner/alert_task_instance', () => ({ taskInstanceToAlertTaskInstance: jest.fn(), @@ -428,8 +429,8 @@ describe('bulkDisableRules', () => { taskManager.bulkRemove.mockResolvedValue({ statuses: [ - { id: 'id1', type: 'alert', success: true }, - { id: 'id2', type: 'alert', success: false }, + { id: 'id1', type: RULE_SAVED_OBJECT_TYPE, success: true }, + { id: 'id2', type: RULE_SAVED_OBJECT_TYPE, success: false }, ], }); @@ -543,12 +544,12 @@ describe('bulkDisableRules', () => { expect(auditLogger.log.mock.calls[0][0]?.event?.action).toEqual('rule_disable'); expect(auditLogger.log.mock.calls[0][0]?.event?.outcome).toEqual('unknown'); expect(auditLogger.log.mock.calls[0][0]?.kibana).toEqual({ - saved_object: { id: 'id1', type: 'alert' }, + saved_object: { id: 'id1', type: RULE_SAVED_OBJECT_TYPE }, }); expect(auditLogger.log.mock.calls[1][0]?.event?.action).toEqual('rule_disable'); expect(auditLogger.log.mock.calls[1][0]?.event?.outcome).toEqual('unknown'); expect(auditLogger.log.mock.calls[1][0]?.kibana).toEqual({ - saved_object: { id: 'id2', type: 'alert' }, + saved_object: { id: 'id2', type: RULE_SAVED_OBJECT_TYPE }, }); }); diff --git a/x-pack/plugins/alerting/server/application/rule/methods/bulk_disable/bulk_disable_rules.ts b/x-pack/plugins/alerting/server/application/rule/methods/bulk_disable/bulk_disable_rules.ts index e981a30181a49..0ac84ce2ef6d7 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/bulk_disable/bulk_disable_rules.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/bulk_disable/bulk_disable_rules.ts @@ -11,6 +11,7 @@ import { withSpan } from '@kbn/apm-utils'; import pMap from 'p-map'; import { Logger } from '@kbn/core/server'; import { TaskManagerStartContract } from '@kbn/task-manager-plugin/server'; +import { RULE_SAVED_OBJECT_TYPE } from '../../../../saved_objects'; import type { RawRule, SanitizedRule, RawRuleAction } from '../../../../types'; import { convertRuleIdsToKueryNode } from '../../../../lib'; import { ruleAuditEvent, RuleAuditAction } from '../../../../rules_client/common/audit_events'; @@ -132,7 +133,7 @@ const bulkDisableRulesWithOCC = async ( context.encryptedSavedObjectsClient.createPointInTimeFinderDecryptedAsInternalUser( { filter: filter ? nodeBuilder.and([filter, additionalFilter]) : additionalFilter, - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, perPage: 100, ...(context.namespace ? { namespaces: [context.namespace] } : undefined), } @@ -200,7 +201,7 @@ const bulkDisableRulesWithOCC = async ( ruleAuditEvent({ action: RuleAuditAction.DISABLE, outcome: 'unknown', - savedObject: { type: 'alert', id: rule.id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id: rule.id }, }) ); } catch (error) { diff --git a/x-pack/plugins/alerting/server/application/rule/methods/bulk_edit/bulk_edit_rules.test.ts b/x-pack/plugins/alerting/server/application/rule/methods/bulk_edit/bulk_edit_rules.test.ts index 38a04fbd76c76..dbd056bb1df30 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/bulk_edit/bulk_edit_rules.test.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/bulk_edit/bulk_edit_rules.test.ts @@ -35,6 +35,7 @@ import { } from '../../../../rules_client/tests/test_helpers'; import { migrateLegacyActions } from '../../../../rules_client/lib'; import { migrateLegacyActionsMock } from '../../../../rules_client/lib/siem_legacy_actions/retrieve_migrated_legacy_actions.mock'; +import { RULE_SAVED_OBJECT_TYPE } from '../../../../saved_objects'; jest.mock('../../../../rules_client/lib/siem_legacy_actions/migrate_legacy_actions', () => { return { @@ -121,7 +122,7 @@ describe('bulkEdit()', () => { let actionsClient: jest.Mocked; const existingRule = { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: false, tags: ['foo'], @@ -269,7 +270,7 @@ describe('bulkEdit()', () => { saved_objects: [ { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, tags: ['foo', 'test-1'], @@ -314,7 +315,7 @@ describe('bulkEdit()', () => { [ expect.objectContaining({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: expect.objectContaining({ tags: ['foo', 'test-1'], revision: 1, @@ -330,7 +331,7 @@ describe('bulkEdit()', () => { saved_objects: [ { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, tags: [], @@ -371,7 +372,7 @@ describe('bulkEdit()', () => { [ expect.objectContaining({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: expect.objectContaining({ tags: [], revision: 1, @@ -387,7 +388,7 @@ describe('bulkEdit()', () => { saved_objects: [ { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, tags: ['test-1', 'test-2'], @@ -429,7 +430,7 @@ describe('bulkEdit()', () => { [ expect.objectContaining({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: expect.objectContaining({ tags: ['test-1', 'test-2'], revision: 1, @@ -931,7 +932,7 @@ describe('bulkEdit()', () => { saved_objects: [ { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, tags: ['foo'], @@ -982,7 +983,7 @@ describe('bulkEdit()', () => { [ expect.objectContaining({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: expect.objectContaining({ params: expect.objectContaining({ index: ['test-1', 'test-2', 'test-4', 'test-5'], @@ -1000,7 +1001,7 @@ describe('bulkEdit()', () => { saved_objects: [ { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, tags: ['foo'], @@ -1046,7 +1047,7 @@ describe('bulkEdit()', () => { [ expect.objectContaining({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: expect.objectContaining({ params: expect.objectContaining({ index: ['test-1'], @@ -1104,7 +1105,7 @@ describe('bulkEdit()', () => { saved_objects: [ { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, tags: ['foo', 'test-1'], @@ -1149,7 +1150,7 @@ describe('bulkEdit()', () => { [ expect.objectContaining({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: expect.objectContaining({ snoozeSchedule: [snoozePayload], revision: 0, @@ -1180,7 +1181,7 @@ describe('bulkEdit()', () => { [ expect.objectContaining({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: expect.objectContaining({ revision: 0, snoozeSchedule: [snoozePayload], @@ -1226,7 +1227,7 @@ describe('bulkEdit()', () => { [ expect.objectContaining({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: expect.objectContaining({ revision: 0, snoozeSchedule: [...existingSnooze, snoozePayload], @@ -1271,7 +1272,7 @@ describe('bulkEdit()', () => { [ expect.objectContaining({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: expect.objectContaining({ muteAll: true, revision: 0, @@ -1316,7 +1317,7 @@ describe('bulkEdit()', () => { [ expect.objectContaining({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: expect.objectContaining({ revision: 0, snoozeSchedule: [existingSnooze[1], existingSnooze[2]], @@ -1361,7 +1362,7 @@ describe('bulkEdit()', () => { [ expect.objectContaining({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: expect.objectContaining({ revision: 0, snoozeSchedule: [], @@ -1406,7 +1407,7 @@ describe('bulkEdit()', () => { [ expect.objectContaining({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: expect.objectContaining({ revision: 0, snoozeSchedule: [existingSnooze[0]], @@ -1525,7 +1526,7 @@ describe('bulkEdit()', () => { saved_objects: [ { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, tags: ['foo', 'test-1'], @@ -1578,7 +1579,7 @@ describe('bulkEdit()', () => { [ expect.objectContaining({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: expect.objectContaining({ tags: ['foo', 'test-1'], params: { @@ -1597,7 +1598,7 @@ describe('bulkEdit()', () => { saved_objects: [ { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, tags: ['foo', 'test-1'], @@ -1651,7 +1652,7 @@ describe('bulkEdit()', () => { [ expect.objectContaining({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: expect.objectContaining({ tags: ['foo', 'test-1'], params: { @@ -1670,7 +1671,7 @@ describe('bulkEdit()', () => { saved_objects: [ { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, tags: ['foo'], @@ -1724,7 +1725,7 @@ describe('bulkEdit()', () => { [ expect.objectContaining({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: expect.objectContaining({ tags: ['foo'], params: { @@ -1814,7 +1815,7 @@ describe('bulkEdit()', () => { }, page: 1, perPage: 0, - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }); }); test('should call unsecuredSavedObjectsClient.find for aggregations when called with ids options', async () => { @@ -1884,7 +1885,7 @@ describe('bulkEdit()', () => { }, page: 1, perPage: 0, - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }); }); test('should throw if number of matched rules greater than 10_000', async () => { @@ -2032,7 +2033,7 @@ describe('bulkEdit()', () => { type: 'function', }, perPage: 100, - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, namespaces: ['default'], }); }); @@ -2087,7 +2088,7 @@ describe('bulkEdit()', () => { saved_objects: [ { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, tags: ['foo'], @@ -2228,7 +2229,7 @@ describe('bulkEdit()', () => { saved_objects: [ { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, tags: ['foo'], @@ -2489,7 +2490,7 @@ describe('bulkEdit()', () => { saved_objects: [ { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, tags: ['foo', 'test-1'], @@ -2551,7 +2552,7 @@ describe('bulkEdit()', () => { saved_objects: [ { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, tags: ['foo'], @@ -2593,7 +2594,7 @@ describe('bulkEdit()', () => { [ expect.objectContaining({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: expect.objectContaining({ params: expect.objectContaining({ index: ['test-index-*'], @@ -2632,7 +2633,7 @@ describe('bulkEdit()', () => { saved_objects: [ { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, tags: ['foo'], @@ -2675,7 +2676,7 @@ describe('bulkEdit()', () => { saved_objects: [ { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, tags: ['foo'], diff --git a/x-pack/plugins/alerting/server/application/rule/methods/bulk_edit/bulk_edit_rules.ts b/x-pack/plugins/alerting/server/application/rule/methods/bulk_edit/bulk_edit_rules.ts index 996f67448c7f8..d9ff12bf9d3f3 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/bulk_edit/bulk_edit_rules.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/bulk_edit/bulk_edit_rules.ts @@ -15,6 +15,7 @@ import { SavedObjectsFindResult, SavedObjectsUpdateResponse, } from '@kbn/core/server'; +import { RULE_SAVED_OBJECT_TYPE } from '../../../../saved_objects'; import { BulkActionSkipResult } from '../../../../../common/bulk_edit'; import { RuleTypeRegistry } from '../../../../types'; import { @@ -279,7 +280,7 @@ async function bulkEditRulesOcc( await context.encryptedSavedObjectsClient.createPointInTimeFinderDecryptedAsInternalUser( { filter, - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, perPage: 100, ...(context.namespace ? { namespaces: [context.namespace] } : undefined), } diff --git a/x-pack/plugins/alerting/server/application/rule/methods/create/create_rule.test.ts b/x-pack/plugins/alerting/server/application/rule/methods/create/create_rule.test.ts index 4fcb2b0d55716..ca2ccc98ea292 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/create/create_rule.test.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/create/create_rule.test.ts @@ -27,6 +27,7 @@ import { getBeforeSetup, setGlobalDate } from '../../../../rules_client/tests/li import { RecoveredActionGroup } from '../../../../../common'; import { bulkMarkApiKeysForInvalidation } from '../../../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation'; import { getRuleExecutionStatusPending, getDefaultMonitoring } from '../../../../lib'; +import { RULE_SAVED_OBJECT_TYPE } from '../../../../saved_objects'; jest.mock('../../../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation', () => ({ bulkMarkApiKeysForInvalidation: jest.fn(), @@ -174,7 +175,7 @@ describe('create()', () => { ): Promise { unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: '123', schedule: { interval: '1m' }, @@ -206,7 +207,7 @@ describe('create()', () => { }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { actions: [], scheduledTaskId: 'task-123', @@ -270,7 +271,7 @@ describe('create()', () => { }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { ...data, executionStatus: getRuleExecutionStatusPending('2019-02-12T21:01:22.479Z'), @@ -285,7 +286,7 @@ describe('create()', () => { action: 'rule_create', outcome: 'unknown', }), - kibana: { saved_object: { id: 'mock-saved-object-id', type: 'alert' } }, + kibana: { saved_object: { id: 'mock-saved-object-id', type: RULE_SAVED_OBJECT_TYPE } }, }) ); }); @@ -310,7 +311,7 @@ describe('create()', () => { kibana: { saved_object: { id: 'mock-saved-object-id', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }, }, error: { @@ -351,7 +352,7 @@ describe('create()', () => { }; unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { ...createdAttributes, running: false, @@ -367,7 +368,7 @@ describe('create()', () => { }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { ...createdAttributes, running: false, @@ -433,7 +434,7 @@ describe('create()', () => { `); expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledTimes(1); expect(unsecuredSavedObjectsClient.create.mock.calls[0]).toHaveLength(3); - expect(unsecuredSavedObjectsClient.create.mock.calls[0][0]).toEqual('alert'); + expect(unsecuredSavedObjectsClient.create.mock.calls[0][0]).toEqual(RULE_SAVED_OBJECT_TYPE); expect(unsecuredSavedObjectsClient.create.mock.calls[0][1]).toMatchInlineSnapshot(` Object { "actions": Array [ @@ -543,7 +544,7 @@ describe('create()', () => { `); expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledTimes(1); expect(unsecuredSavedObjectsClient.update.mock.calls[0]).toHaveLength(4); - expect(unsecuredSavedObjectsClient.update.mock.calls[0][0]).toEqual('alert'); + expect(unsecuredSavedObjectsClient.update.mock.calls[0][0]).toEqual(RULE_SAVED_OBJECT_TYPE); expect(unsecuredSavedObjectsClient.update.mock.calls[0][1]).toEqual('1'); expect(unsecuredSavedObjectsClient.update.mock.calls[0][2]).toMatchInlineSnapshot(` Object { @@ -582,7 +583,7 @@ describe('create()', () => { }; unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '123', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { ...createdAttributes, running: false, @@ -646,7 +647,7 @@ describe('create()', () => { }; unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '123', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { ...createdAttributes, running: false, @@ -798,7 +799,7 @@ describe('create()', () => { ]); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { running: false, executionStatus: getRuleExecutionStatusPending('2019-02-12T21:01:22.479Z'), @@ -857,7 +858,7 @@ describe('create()', () => { }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { actions: [], scheduledTaskId: 'task-123', @@ -1000,7 +1001,7 @@ describe('create()', () => { actionsClient.isPreconfigured.mockReturnValueOnce(false); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { executionStatus: getRuleExecutionStatusPending('2019-02-12T21:01:22.479Z'), alertTypeId: '123', @@ -1054,7 +1055,7 @@ describe('create()', () => { }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { actions: [], scheduledTaskId: 'task-123', @@ -1110,7 +1111,7 @@ describe('create()', () => { } `); expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledWith( - 'alert', + RULE_SAVED_OBJECT_TYPE, { actions: [ { @@ -1261,7 +1262,7 @@ describe('create()', () => { unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { executionStatus: getRuleExecutionStatusPending('2019-02-12T21:01:22.479Z'), alertTypeId: '123', @@ -1314,7 +1315,7 @@ describe('create()', () => { unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { actions: [], scheduledTaskId: 'task-123', @@ -1371,7 +1372,7 @@ describe('create()', () => { `); expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledWith( - 'alert', + RULE_SAVED_OBJECT_TYPE, { actions: [ { @@ -1444,7 +1445,7 @@ describe('create()', () => { const data = getMockData({ enabled: false }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: false, alertTypeId: '123', @@ -1561,7 +1562,7 @@ describe('create()', () => { }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: '123', schedule: { interval: '1m' }, @@ -1600,7 +1601,7 @@ describe('create()', () => { }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { actions: [], scheduledTaskId: 'task-123', @@ -1611,7 +1612,7 @@ describe('create()', () => { expect(extractReferencesFn).toHaveBeenCalledWith(ruleParams); expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledWith( - 'alert', + RULE_SAVED_OBJECT_TYPE, { actions: [ { @@ -1750,7 +1751,7 @@ describe('create()', () => { }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: '123', schedule: { interval: '1m' }, @@ -1789,7 +1790,7 @@ describe('create()', () => { }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { actions: [], scheduledTaskId: 'task-123', @@ -1800,7 +1801,7 @@ describe('create()', () => { expect(extractReferencesFn).toHaveBeenCalledWith(ruleParams); expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledWith( - 'alert', + RULE_SAVED_OBJECT_TYPE, { actions: [ { @@ -1894,7 +1895,7 @@ describe('create()', () => { const data = getMockData({ name: ' my alert name ' }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: false, name: ' my alert name ', @@ -1963,7 +1964,7 @@ describe('create()', () => { }; unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: createdAttributes, references: [ { @@ -1975,7 +1976,7 @@ describe('create()', () => { }); const result = await rulesClient.create({ data }); expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledWith( - 'alert', + RULE_SAVED_OBJECT_TYPE, { actions: [ { @@ -2104,7 +2105,7 @@ describe('create()', () => { }; unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: createdAttributes, references: [ { @@ -2116,7 +2117,7 @@ describe('create()', () => { }); const result = await rulesClient.create({ data }); expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledWith( - 'alert', + RULE_SAVED_OBJECT_TYPE, { actions: [ { @@ -2245,7 +2246,7 @@ describe('create()', () => { }; unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: createdAttributes, references: [ { @@ -2257,7 +2258,7 @@ describe('create()', () => { }); const result = await rulesClient.create({ data }); expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledWith( - 'alert', + RULE_SAVED_OBJECT_TYPE, { actions: [ { @@ -2394,7 +2395,7 @@ describe('create()', () => { }; unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '123', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: createdAttributes, references: [ { @@ -2408,7 +2409,7 @@ describe('create()', () => { const result = await rulesClient.create({ data }); expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledWith( - 'alert', + RULE_SAVED_OBJECT_TYPE, { enabled: true, name: 'abc', @@ -2607,7 +2608,7 @@ describe('create()', () => { const data = getMockData(); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: '123', schedule: { interval: '1m' }, @@ -2656,7 +2657,7 @@ describe('create()', () => { const data = getMockData(); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: '123', schedule: { interval: '1m' }, @@ -2703,7 +2704,7 @@ describe('create()', () => { const data = getMockData(); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: '123', schedule: { interval: '1m' }, @@ -2761,7 +2762,7 @@ describe('create()', () => { }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: '123', schedule: { interval: '1m' }, @@ -2791,7 +2792,7 @@ describe('create()', () => { }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { actions: [], scheduledTaskId: 'task-123', @@ -2808,7 +2809,7 @@ describe('create()', () => { expect(rulesClientParams.createAPIKey).toHaveBeenCalledTimes(1); expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledWith( - 'alert', + RULE_SAVED_OBJECT_TYPE, { actions: [ { @@ -2866,7 +2867,7 @@ describe('create()', () => { const data = getMockData({ enabled: false }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: '123', schedule: { interval: '1m' }, @@ -2896,7 +2897,7 @@ describe('create()', () => { }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { actions: [], scheduledTaskId: 'task-123', @@ -2913,7 +2914,7 @@ describe('create()', () => { expect(rulesClientParams.createAPIKey).not.toHaveBeenCalled(); expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledWith( - 'alert', + RULE_SAVED_OBJECT_TYPE, { actions: [ { @@ -3070,7 +3071,7 @@ describe('create()', () => { }; unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: createdAttributes, references: [ { @@ -3534,7 +3535,7 @@ describe('create()', () => { ]); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: '123', schedule: { interval: '1m' }, @@ -3576,7 +3577,7 @@ describe('create()', () => { }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { actions: [], scheduledTaskId: 'task-123', @@ -3744,7 +3745,7 @@ describe('create()', () => { rulesClientParams.isAuthenticationTypeAPIKey.mockReturnValueOnce(true); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: '123', schedule: { interval: '1m' }, @@ -3774,7 +3775,7 @@ describe('create()', () => { }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { actions: [], scheduledTaskId: 'task-123', @@ -3792,7 +3793,7 @@ describe('create()', () => { expect(rulesClientParams.isAuthenticationTypeAPIKey).toHaveBeenCalledTimes(1); expect(rulesClientParams.getAuthenticationAPIKey).toHaveBeenCalledTimes(1); expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledWith( - 'alert', + RULE_SAVED_OBJECT_TYPE, { actions: [ { diff --git a/x-pack/plugins/alerting/server/application/rule/methods/create/create_rule.ts b/x-pack/plugins/alerting/server/application/rule/methods/create/create_rule.ts index 74dd0775d0c22..bdd11da2483f7 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/create/create_rule.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/create/create_rule.ts @@ -8,6 +8,7 @@ import Semver from 'semver'; import Boom from '@hapi/boom'; import { SavedObject, SavedObjectsUtils } from '@kbn/core/server'; import { withSpan } from '@kbn/apm-utils'; +import { RULE_SAVED_OBJECT_TYPE } from '../../../../saved_objects'; import { parseDuration, getRuleCircuitBreakerErrorMessage } from '../../../../../common'; import { WriteOperations, AlertingAuthorizationEntity } from '../../../../authorization'; import { @@ -101,7 +102,7 @@ export async function createRule( context.auditLogger?.log( ruleAuditEvent({ action: RuleAuditAction.CREATE, - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, error, }) ); diff --git a/x-pack/plugins/alerting/server/application/rule/methods/get_schedule_frequency/get_schedule_frequency.ts b/x-pack/plugins/alerting/server/application/rule/methods/get_schedule_frequency/get_schedule_frequency.ts index b670adeccae8a..d30d172113248 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/get_schedule_frequency/get_schedule_frequency.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/get_schedule_frequency/get_schedule_frequency.ts @@ -4,6 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { RULE_SAVED_OBJECT_TYPE } from '../../../../saved_objects'; import { RulesClientContext } from '../../../../rules_client/types'; import { RuleDomain } from '../../types'; import { convertDurationToFrequency } from '../../../../../common/parse_duration'; @@ -41,7 +42,7 @@ export const getScheduleFrequency = async ( RuleDomain, SchedulesIntervalAggregationResult >({ - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, filter: 'alert.attributes.enabled: true', namespaces: ['*'], aggs: { diff --git a/x-pack/plugins/alerting/server/application/rule/methods/mute_alert/mute_instance.ts b/x-pack/plugins/alerting/server/application/rule/methods/mute_alert/mute_instance.ts index 8adbdf7ae58c9..5758794b550fb 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/mute_alert/mute_instance.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/mute_alert/mute_instance.ts @@ -6,6 +6,7 @@ */ import Boom from '@hapi/boom'; +import { RULE_SAVED_OBJECT_TYPE } from '../../../../saved_objects'; import { updateRuleSo } from '../../../../data/rule/methods/update_rule_so'; import { muteAlertParamsSchema } from './schemas'; import type { MuteAlertParams } from './types'; @@ -38,7 +39,7 @@ async function muteInstanceWithOCC( { alertId, alertInstanceId }: MuteAlertParams ) { const { attributes, version } = await context.unsecuredSavedObjectsClient.get( - 'alert', + RULE_SAVED_OBJECT_TYPE, alertId ); @@ -57,7 +58,7 @@ async function muteInstanceWithOCC( context.auditLogger?.log( ruleAuditEvent({ action: RuleAuditAction.MUTE_ALERT, - savedObject: { type: 'alert', id: alertId }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id: alertId }, error, }) ); @@ -68,7 +69,7 @@ async function muteInstanceWithOCC( ruleAuditEvent({ action: RuleAuditAction.MUTE_ALERT, outcome: 'unknown', - savedObject: { type: 'alert', id: alertId }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id: alertId }, }) ); diff --git a/x-pack/plugins/alerting/server/application/rule/methods/resolve/resolve_rule.ts b/x-pack/plugins/alerting/server/application/rule/methods/resolve/resolve_rule.ts index cbde52a44b1fe..de9f421a95924 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/resolve/resolve_rule.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/resolve/resolve_rule.ts @@ -7,6 +7,7 @@ import Boom from '@hapi/boom'; import { withSpan } from '@kbn/apm-utils'; import { AlertConsumers } from '@kbn/rule-data-utils'; +import { RULE_SAVED_OBJECT_TYPE } from '../../../../saved_objects'; import { resolveRuleSavedObject } from '../../../../rules_client/lib'; import { ruleAuditEvent, RuleAuditAction } from '../../../../rules_client/common/audit_events'; import { RuleTypeParams } from '../../../../types'; @@ -52,7 +53,7 @@ Promise> { context.auditLogger?.log( ruleAuditEvent({ action: RuleAuditAction.RESOLVE, - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, error, }) ); @@ -61,7 +62,7 @@ Promise> { context.auditLogger?.log( ruleAuditEvent({ action: RuleAuditAction.RESOLVE, - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, }) ); diff --git a/x-pack/plugins/alerting/server/application/rule/methods/snooze/snooze_rule.ts b/x-pack/plugins/alerting/server/application/rule/methods/snooze/snooze_rule.ts index b10df195e5b77..162fe98d31f04 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/snooze/snooze_rule.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/snooze/snooze_rule.ts @@ -7,6 +7,7 @@ import Boom from '@hapi/boom'; import { withSpan } from '@kbn/apm-utils'; +import { RULE_SAVED_OBJECT_TYPE } from '../../../../saved_objects'; import { getRuleSavedObject } from '../../../../rules_client/lib'; import { ruleAuditEvent, RuleAuditAction } from '../../../../rules_client/common/audit_events'; import { WriteOperations, AlertingAuthorizationEntity } from '../../../../authorization'; @@ -71,7 +72,7 @@ async function snoozeWithOCC( context.auditLogger?.log( ruleAuditEvent({ action: RuleAuditAction.SNOOZE, - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, error, }) ); @@ -82,7 +83,7 @@ async function snoozeWithOCC( ruleAuditEvent({ action: RuleAuditAction.SNOOZE, outcome: 'unknown', - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, }) ); diff --git a/x-pack/plugins/alerting/server/application/rule/methods/tags/get_rule_tags.test.ts b/x-pack/plugins/alerting/server/application/rule/methods/tags/get_rule_tags.test.ts index dd1f3150d0a08..28e9fc08c3b8a 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/tags/get_rule_tags.test.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/tags/get_rule_tags.test.ts @@ -22,6 +22,7 @@ import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup } from '../../../../rules_client/tests/lib'; import { RecoveredActionGroup } from '../../../../../common'; import { RegistryRuleType } from '../../../../rule_type_registry'; +import { RULE_SAVED_OBJECT_TYPE } from '../../../../saved_objects'; const taskManager = taskManagerMock.createStart(); const ruleTypeRegistry = ruleTypeRegistryMock.create(); @@ -147,7 +148,7 @@ describe('getTags()', () => { tags: { terms: { field: 'alert.attributes.tags', order: { _key: 'asc' }, size: 10000 } }, }, filter: undefined, - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }); expect(result.data).toEqual(['a', 'b', 'c']); diff --git a/x-pack/plugins/alerting/server/application/rule/methods/unsnooze/unsnooze_rule.ts b/x-pack/plugins/alerting/server/application/rule/methods/unsnooze/unsnooze_rule.ts index c47b39f28aedb..b5cb6f8f29e33 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/unsnooze/unsnooze_rule.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/unsnooze/unsnooze_rule.ts @@ -7,6 +7,7 @@ import Boom from '@hapi/boom'; import { withSpan } from '@kbn/apm-utils'; +import { RULE_SAVED_OBJECT_TYPE } from '../../../../saved_objects'; import { ruleAuditEvent, RuleAuditAction } from '../../../../rules_client/common/audit_events'; import { getRuleSavedObject } from '../../../../rules_client/lib'; import { WriteOperations, AlertingAuthorizationEntity } from '../../../../authorization'; @@ -62,7 +63,7 @@ async function unsnoozeWithOCC(context: RulesClientContext, { id, scheduleIds }: context.auditLogger?.log( ruleAuditEvent({ action: RuleAuditAction.UNSNOOZE, - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, error, }) ); @@ -73,7 +74,7 @@ async function unsnoozeWithOCC(context: RulesClientContext, { id, scheduleIds }: ruleAuditEvent({ action: RuleAuditAction.UNSNOOZE, outcome: 'unknown', - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, }) ); diff --git a/x-pack/plugins/alerting/server/data/rule/methods/bulk_delete_rules_so.ts b/x-pack/plugins/alerting/server/data/rule/methods/bulk_delete_rules_so.ts index 7f3568f76cf98..15449dc003cd2 100644 --- a/x-pack/plugins/alerting/server/data/rule/methods/bulk_delete_rules_so.ts +++ b/x-pack/plugins/alerting/server/data/rule/methods/bulk_delete_rules_so.ts @@ -10,6 +10,7 @@ import { SavedObjectsBulkDeleteOptions, SavedObjectsBulkDeleteResponse, } from '@kbn/core/server'; +import { RULE_SAVED_OBJECT_TYPE } from '../../../saved_objects'; export interface BulkDeleteRulesSoParams { savedObjectsClient: SavedObjectsClientContract; @@ -23,7 +24,7 @@ export const bulkDeleteRulesSo = ( const { savedObjectsClient, ids, savedObjectsBulkDeleteOptions } = params; return savedObjectsClient.bulkDelete( - ids.map((id) => ({ id, type: 'alert' })), + ids.map((id) => ({ id, type: RULE_SAVED_OBJECT_TYPE })), savedObjectsBulkDeleteOptions ); }; diff --git a/x-pack/plugins/alerting/server/data/rule/methods/create_rule_so.ts b/x-pack/plugins/alerting/server/data/rule/methods/create_rule_so.ts index 7574e9aca1608..b276bb0e2e10d 100644 --- a/x-pack/plugins/alerting/server/data/rule/methods/create_rule_so.ts +++ b/x-pack/plugins/alerting/server/data/rule/methods/create_rule_so.ts @@ -10,6 +10,7 @@ import { SavedObjectsCreateOptions, SavedObject, } from '@kbn/core/server'; +import { RULE_SAVED_OBJECT_TYPE } from '../../../saved_objects'; import { RuleAttributes } from '../types'; export interface CreateRuleSoParams { @@ -21,5 +22,9 @@ export interface CreateRuleSoParams { export const createRuleSo = (params: CreateRuleSoParams): Promise> => { const { savedObjectsClient, ruleAttributes, savedObjectsCreateOptions } = params; - return savedObjectsClient.create('alert', ruleAttributes, savedObjectsCreateOptions); + return savedObjectsClient.create( + RULE_SAVED_OBJECT_TYPE, + ruleAttributes, + savedObjectsCreateOptions + ); }; diff --git a/x-pack/plugins/alerting/server/data/rule/methods/delete_rule_so.ts b/x-pack/plugins/alerting/server/data/rule/methods/delete_rule_so.ts index e3428cfc78f63..7c15fd847303f 100644 --- a/x-pack/plugins/alerting/server/data/rule/methods/delete_rule_so.ts +++ b/x-pack/plugins/alerting/server/data/rule/methods/delete_rule_so.ts @@ -6,6 +6,7 @@ */ import { SavedObjectsClientContract, SavedObjectsDeleteOptions } from '@kbn/core/server'; +import { RULE_SAVED_OBJECT_TYPE } from '../../../saved_objects'; export interface DeleteRuleSoParams { savedObjectsClient: SavedObjectsClientContract; @@ -16,5 +17,5 @@ export interface DeleteRuleSoParams { export const deleteRuleSo = (params: DeleteRuleSoParams): Promise<{}> => { const { savedObjectsClient, id, savedObjectsDeleteOptions } = params; - return savedObjectsClient.delete('alert', id, savedObjectsDeleteOptions); + return savedObjectsClient.delete(RULE_SAVED_OBJECT_TYPE, id, savedObjectsDeleteOptions); }; diff --git a/x-pack/plugins/alerting/server/data/rule/methods/find_rules_so.ts b/x-pack/plugins/alerting/server/data/rule/methods/find_rules_so.ts index 1362c2406dbae..e929ccf019205 100644 --- a/x-pack/plugins/alerting/server/data/rule/methods/find_rules_so.ts +++ b/x-pack/plugins/alerting/server/data/rule/methods/find_rules_so.ts @@ -10,6 +10,7 @@ import { SavedObjectsFindOptions, SavedObjectsFindResponse, } from '@kbn/core/server'; +import { RULE_SAVED_OBJECT_TYPE } from '../../../saved_objects'; import { RuleAttributes } from '../types'; export interface FindRulesSoParams { @@ -24,6 +25,6 @@ export const findRulesSo = >( return savedObjectsClient.find({ ...savedObjectsFindOptions, - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }); }; diff --git a/x-pack/plugins/alerting/server/data/rule/methods/get_rule_so.ts b/x-pack/plugins/alerting/server/data/rule/methods/get_rule_so.ts index c8777afef3f78..051644bab56f2 100644 --- a/x-pack/plugins/alerting/server/data/rule/methods/get_rule_so.ts +++ b/x-pack/plugins/alerting/server/data/rule/methods/get_rule_so.ts @@ -7,6 +7,7 @@ import { SavedObjectsClientContract, SavedObject } from '@kbn/core/server'; import { SavedObjectsGetOptions } from '@kbn/core-saved-objects-api-server'; +import { RULE_SAVED_OBJECT_TYPE } from '../../../saved_objects'; import { RuleAttributes } from '../types'; export interface GetRuleSoParams { @@ -18,5 +19,5 @@ export interface GetRuleSoParams { export const getRuleSo = (params: GetRuleSoParams): Promise> => { const { savedObjectsClient, id, savedObjectsGetOptions } = params; - return savedObjectsClient.get('alert', id, savedObjectsGetOptions); + return savedObjectsClient.get(RULE_SAVED_OBJECT_TYPE, id, savedObjectsGetOptions); }; diff --git a/x-pack/plugins/alerting/server/data/rule/methods/resolve_rule_so.ts b/x-pack/plugins/alerting/server/data/rule/methods/resolve_rule_so.ts index b8061cc6e3c3c..d0429b4166d8e 100644 --- a/x-pack/plugins/alerting/server/data/rule/methods/resolve_rule_so.ts +++ b/x-pack/plugins/alerting/server/data/rule/methods/resolve_rule_so.ts @@ -7,6 +7,7 @@ import { SavedObjectsClientContract, SavedObjectsResolveResponse } from '@kbn/core/server'; import { SavedObjectsResolveOptions } from '@kbn/core-saved-objects-api-server'; +import { RULE_SAVED_OBJECT_TYPE } from '../../../saved_objects'; import { RuleAttributes } from '../types'; export interface ResolveRuleSoParams { @@ -20,5 +21,5 @@ export const resolveRuleSo = ( ): Promise> => { const { savedObjectsClient, id, savedObjectsResolveOptions } = params; - return savedObjectsClient.resolve('alert', id, savedObjectsResolveOptions); + return savedObjectsClient.resolve(RULE_SAVED_OBJECT_TYPE, id, savedObjectsResolveOptions); }; diff --git a/x-pack/plugins/alerting/server/data/rule/methods/update_rule_so.ts b/x-pack/plugins/alerting/server/data/rule/methods/update_rule_so.ts index 8739202c237ea..dfb8b6b5c1e7e 100644 --- a/x-pack/plugins/alerting/server/data/rule/methods/update_rule_so.ts +++ b/x-pack/plugins/alerting/server/data/rule/methods/update_rule_so.ts @@ -10,6 +10,7 @@ import { SavedObjectsUpdateOptions, SavedObjectsUpdateResponse, } from '@kbn/core/server'; +import { RULE_SAVED_OBJECT_TYPE } from '../../../saved_objects'; import { RuleAttributes } from '../types'; export interface UpdateRuleSoParams { @@ -25,7 +26,7 @@ export const updateRuleSo = ( const { savedObjectsClient, id, updateRuleAttributes, savedObjectsUpdateOptions } = params; return savedObjectsClient.update( - 'alert', + RULE_SAVED_OBJECT_TYPE, id, updateRuleAttributes, savedObjectsUpdateOptions diff --git a/x-pack/plugins/alerting/server/health/get_health.test.ts b/x-pack/plugins/alerting/server/health/get_health.test.ts index 3715113c84232..51e03dc890e98 100644 --- a/x-pack/plugins/alerting/server/health/get_health.test.ts +++ b/x-pack/plugins/alerting/server/health/get_health.test.ts @@ -6,6 +6,7 @@ */ import { savedObjectsRepositoryMock, savedObjectsServiceMock } from '@kbn/core/server/mocks'; +import { RULE_SAVED_OBJECT_TYPE } from '../saved_objects'; import { RuleExecutionStatusErrorReasons, HealthStatus } from '../types'; import { getAlertingHealthStatus, getHealth } from './get_health'; @@ -22,7 +23,7 @@ describe('getHealth()', () => { saved_objects: [ { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: 'myType', schedule: { interval: '10s' }, @@ -80,7 +81,7 @@ describe('getHealth()', () => { saved_objects: [ { id: '2', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: 'myType', schedule: { interval: '1s' }, @@ -134,7 +135,7 @@ describe('getHealth()', () => { saved_objects: [ { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: 'myType', schedule: { interval: '10s' }, @@ -185,7 +186,7 @@ describe('getHealth()', () => { saved_objects: [ { id: '2', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: 'myType', schedule: { interval: '1s' }, @@ -233,7 +234,7 @@ describe('getAlertingHealthStatus()', () => { saved_objects: [ { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: 'myType', schedule: { interval: '10s' }, diff --git a/x-pack/plugins/alerting/server/health/get_health.ts b/x-pack/plugins/alerting/server/health/get_health.ts index 5d2c43fe69182..f4b727170a4b9 100644 --- a/x-pack/plugins/alerting/server/health/get_health.ts +++ b/x-pack/plugins/alerting/server/health/get_health.ts @@ -6,6 +6,7 @@ */ import { ISavedObjectsRepository, SavedObjectsServiceStart } from '@kbn/core/server'; +import { RULE_SAVED_OBJECT_TYPE } from '../saved_objects'; import { AlertsHealth, HealthStatus, RawRule, RuleExecutionStatusErrorReasons } from '../types'; import type { LatestTaskStateSchema } from './task_state'; @@ -30,7 +31,7 @@ export const getHealth = async ( const { saved_objects: decryptErrorData } = await internalSavedObjectsRepository.find({ filter: `alert.attributes.executionStatus.status:error and alert.attributes.executionStatus.error.reason:${RuleExecutionStatusErrorReasons.Decrypt}`, fields: ['executionStatus'], - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, sortField: 'executionStatus.lastExecutionDate', sortOrder: 'desc', page: 1, @@ -48,7 +49,7 @@ export const getHealth = async ( const { saved_objects: executeErrorData } = await internalSavedObjectsRepository.find({ filter: `alert.attributes.executionStatus.status:error and alert.attributes.executionStatus.error.reason:${RuleExecutionStatusErrorReasons.Execute}`, fields: ['executionStatus'], - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, sortField: 'executionStatus.lastExecutionDate', sortOrder: 'desc', page: 1, @@ -66,7 +67,7 @@ export const getHealth = async ( const { saved_objects: readErrorData } = await internalSavedObjectsRepository.find({ filter: `alert.attributes.executionStatus.status:error and alert.attributes.executionStatus.error.reason:${RuleExecutionStatusErrorReasons.Read}`, fields: ['executionStatus'], - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, sortField: 'executionStatus.lastExecutionDate', sortOrder: 'desc', page: 1, @@ -84,7 +85,7 @@ export const getHealth = async ( const { saved_objects: noErrorData } = await internalSavedObjectsRepository.find({ filter: 'not alert.attributes.executionStatus.status:error', fields: ['executionStatus'], - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, sortField: 'executionStatus.lastExecutionDate', sortOrder: 'desc', namespaces: ['*'], @@ -107,7 +108,9 @@ export const getAlertingHealthStatus = async ( savedObjects: SavedObjectsServiceStart, stateRuns: number ) => { - const alertingHealthStatus = await getHealth(savedObjects.createInternalRepository(['alert'])); + const alertingHealthStatus = await getHealth( + savedObjects.createInternalRepository([RULE_SAVED_OBJECT_TYPE]) + ); const state: LatestTaskStateSchema = { runs: stateRuns + 1, health_status: alertingHealthStatus.decryptionHealth.status, diff --git a/x-pack/plugins/alerting/server/index.ts b/x-pack/plugins/alerting/server/index.ts index 04c48ab77c8aa..5be648354e61f 100644 --- a/x-pack/plugins/alerting/server/index.ts +++ b/x-pack/plugins/alerting/server/index.ts @@ -34,6 +34,7 @@ export type { GetViewInAppRelativeUrlFnOpts, DataStreamAdapter, } from './types'; +export { RULE_SAVED_OBJECT_TYPE } from './saved_objects'; export { RuleNotifyWhen } from '../common'; export { DEFAULT_MAX_EPHEMERAL_ACTIONS_PER_ALERT } from './config'; export type { PluginSetupContract, PluginStartContract } from './plugin'; diff --git a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.ts b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.ts index f3fdcec752def..680279f5dbac7 100644 --- a/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.ts +++ b/x-pack/plugins/alerting/server/lib/alerting_event_logger/alerting_event_logger.ts @@ -13,6 +13,7 @@ import { } from '@kbn/event-log-plugin/server'; import { EVENT_LOG_ACTIONS } from '../../plugin'; import { UntypedNormalizedRuleType } from '../../rule_type_registry'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; import { TaskRunnerTimings } from '../../task_runner/task_runner_timer'; import { AlertInstanceState, RuleExecutionStatus } from '../../types'; import { createAlertEventLogRecordObject } from '../create_alert_event_log_record_object'; @@ -259,7 +260,7 @@ export function createAlertRecord(context: RuleContextOpts, alert: AlertOpts) { savedObjects: [ { id: context.ruleId, - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, typeId: context.ruleType.id, relation: SAVED_OBJECT_REL_PRIMARY, }, @@ -286,7 +287,7 @@ export function createActionExecuteRecord(context: RuleContextOpts, action: Acti savedObjects: [ { id: context.ruleId, - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, typeId: context.ruleType.id, relation: SAVED_OBJECT_REL_PRIMARY, }, @@ -319,7 +320,7 @@ export function createExecuteTimeoutRecord(context: RuleContextOpts) { savedObjects: [ { id: context.ruleId, - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, typeId: context.ruleType.id, relation: SAVED_OBJECT_REL_PRIMARY, }, @@ -346,7 +347,7 @@ export function initializeExecuteRecord(context: RuleContext) { savedObjects: [ { id: context.ruleId, - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, typeId: context.ruleType.id, relation: SAVED_OBJECT_REL_PRIMARY, }, diff --git a/x-pack/plugins/alerting/server/lib/is_alerting_error.test.ts b/x-pack/plugins/alerting/server/lib/is_alerting_error.test.ts index 2c4a40efb05f0..f197a8e7424de 100644 --- a/x-pack/plugins/alerting/server/lib/is_alerting_error.test.ts +++ b/x-pack/plugins/alerting/server/lib/is_alerting_error.test.ts @@ -10,10 +10,11 @@ import { ErrorWithReason } from './error_with_reason'; import { SavedObjectsErrorHelpers } from '@kbn/core/server'; import { v4 as uuidv4 } from 'uuid'; import { RuleExecutionStatusErrorReasons } from '../types'; +import { RULE_SAVED_OBJECT_TYPE } from '../saved_objects'; describe('isAlertSavedObjectNotFoundError', () => { const id = uuidv4(); - const errorSONF = SavedObjectsErrorHelpers.createGenericNotFoundError('alert', id); + const errorSONF = SavedObjectsErrorHelpers.createGenericNotFoundError(RULE_SAVED_OBJECT_TYPE, id); test('identifies SavedObjects Not Found errors', () => { // ensure the error created by SO parses as a string with the format we expect @@ -34,7 +35,10 @@ describe('isAlertSavedObjectNotFoundError', () => { describe('isEsUnavailableError', () => { const id = uuidv4(); - const errorSONF = SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError('alert', id); + const errorSONF = SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError( + RULE_SAVED_OBJECT_TYPE, + id + ); test('identifies es unavailable errors', () => { // ensure the error created by SO parses as a string with the format we expect diff --git a/x-pack/plugins/alerting/server/lib/retry_if_conflicts.test.ts b/x-pack/plugins/alerting/server/lib/retry_if_conflicts.test.ts index 8cce79e22fe88..d1af51e179233 100644 --- a/x-pack/plugins/alerting/server/lib/retry_if_conflicts.test.ts +++ b/x-pack/plugins/alerting/server/lib/retry_if_conflicts.test.ts @@ -8,6 +8,7 @@ import { SavedObjectsErrorHelpers } from '@kbn/core/server'; import { retryIfConflicts, RetryForConflictsAttempts } from './retry_if_conflicts'; import { loggingSystemMock } from '@kbn/core/server/mocks'; +import { RULE_SAVED_OBJECT_TYPE } from '../saved_objects'; describe('retry_if_conflicts', () => { beforeEach(() => { @@ -47,7 +48,9 @@ describe('retry_if_conflicts', () => { MockOperationName, getOperationConflictsTimes(RetryForConflictsAttempts + 1) ) - ).rejects.toThrowError(SavedObjectsErrorHelpers.createConflictError('alert', MockAlertId)); + ).rejects.toThrowError( + SavedObjectsErrorHelpers.createConflictError(RULE_SAVED_OBJECT_TYPE, MockAlertId) + ); expect(MockLogger.debug).toBeCalledTimes(RetryForConflictsAttempts); expect(MockLogger.warn).toBeCalledTimes(1); expect(MockLogger.warn).toBeCalledWith(`${MockOperationName} conflict, exceeded retries`); @@ -71,7 +74,7 @@ function getOperationConflictsTimes(times: number) { return async function OperationConflictsTimes() { times--; if (times >= 0) { - throw SavedObjectsErrorHelpers.createConflictError('alert', MockAlertId); + throw SavedObjectsErrorHelpers.createConflictError(RULE_SAVED_OBJECT_TYPE, MockAlertId); } return MockResult; diff --git a/x-pack/plugins/alerting/server/plugin.ts b/x-pack/plugins/alerting/server/plugin.ts index 64bc8ca05281f..87f6e4c3a3979 100644 --- a/x-pack/plugins/alerting/server/plugin.ts +++ b/x-pack/plugins/alerting/server/plugin.ts @@ -77,7 +77,7 @@ import { } from './types'; import { registerAlertingUsageCollector } from './usage'; import { initializeAlertingTelemetry, scheduleAlertingTelemetry } from './usage/task'; -import { setupSavedObjects, getLatestRuleVersion } from './saved_objects'; +import { setupSavedObjects, getLatestRuleVersion, RULE_SAVED_OBJECT_TYPE } from './saved_objects'; import { initializeApiKeyInvalidator, scheduleApiKeyInvalidatorTask, @@ -463,7 +463,7 @@ export class AlertingPlugin { licenseState?.setNotifyUsage(plugins.licensing.featureUsage.notifyUsage); const encryptedSavedObjectsClient = plugins.encryptedSavedObjects.getClient({ - includedHiddenTypes: ['alert'], + includedHiddenTypes: [RULE_SAVED_OBJECT_TYPE], }); const spaceIdToNamespace = (spaceId?: string) => { @@ -491,7 +491,9 @@ export class AlertingPlugin { taskManager: plugins.taskManager, securityPluginSetup: security, securityPluginStart: plugins.security, - internalSavedObjectsRepository: core.savedObjects.createInternalRepository(['alert']), + internalSavedObjectsRepository: core.savedObjects.createInternalRepository([ + RULE_SAVED_OBJECT_TYPE, + ]), encryptedSavedObjectsClient, spaceIdToNamespace, getSpaceId(request: KibanaRequest) { @@ -556,7 +558,9 @@ export class AlertingPlugin { encryptedSavedObjectsClient, basePathService: core.http.basePath, eventLogger: this.eventLogger!, - internalSavedObjectsRepository: core.savedObjects.createInternalRepository(['alert']), + internalSavedObjectsRepository: core.savedObjects.createInternalRepository([ + RULE_SAVED_OBJECT_TYPE, + ]), executionContext: core.executionContext, ruleTypeRegistry: this.ruleTypeRegistry!, alertsService: this.alertsService, @@ -571,7 +575,7 @@ export class AlertingPlugin { getMaintenanceWindowClientWithRequest, }); - this.eventLogService!.registerSavedObjectProvider('alert', (request) => { + this.eventLogService!.registerSavedObjectProvider(RULE_SAVED_OBJECT_TYPE, (request) => { const client = getRulesClientWithRequest(request); return (objects?: SavedObjectsBulkGetObject[]) => objects @@ -594,7 +598,7 @@ export class AlertingPlugin { getAlertingAuthorizationWithRequest, getRulesClientWithRequest, getFrameworkHealth: async () => - await getHealth(core.savedObjects.createInternalRepository(['alert'])), + await getHealth(core.savedObjects.createInternalRepository([RULE_SAVED_OBJECT_TYPE])), }; } @@ -621,7 +625,7 @@ export class AlertingPlugin { }, listTypes: ruleTypeRegistry!.list.bind(ruleTypeRegistry!), getFrameworkHealth: async () => - await getHealth(savedObjects.createInternalRepository(['alert'])), + await getHealth(savedObjects.createInternalRepository([RULE_SAVED_OBJECT_TYPE])), areApiKeysEnabled: async () => { const [, { security }] = await core.getStartServices(); return security?.authc.apiKeys.areAPIKeysEnabled() ?? false; diff --git a/x-pack/plugins/alerting/server/routes/get_action_error_log.test.ts b/x-pack/plugins/alerting/server/routes/get_action_error_log.test.ts index 414d2e8b54122..408ed049044c6 100644 --- a/x-pack/plugins/alerting/server/routes/get_action_error_log.test.ts +++ b/x-pack/plugins/alerting/server/routes/get_action_error_log.test.ts @@ -11,6 +11,7 @@ import { licenseStateMock } from '../lib/license_state.mock'; import { mockHandlerArguments } from './_mock_handler_arguments'; import { SavedObjectsErrorHelpers } from '@kbn/core/server'; import { rulesClientMock } from '../rules_client.mock'; +import { RULE_SAVED_OBJECT_TYPE } from '../saved_objects'; const rulesClient = rulesClientMock.create(); jest.mock('../lib/license_api_access', () => ({ @@ -99,7 +100,9 @@ describe('getActionErrorLogRoute', () => { rulesClient.getActionErrorLog = jest .fn() - .mockRejectedValueOnce(SavedObjectsErrorHelpers.createGenericNotFoundError('alert', '1')); + .mockRejectedValueOnce( + SavedObjectsErrorHelpers.createGenericNotFoundError(RULE_SAVED_OBJECT_TYPE, '1') + ); const [context, req, res] = mockHandlerArguments( { rulesClient }, diff --git a/x-pack/plugins/alerting/server/routes/get_rule_alert_summary.test.ts b/x-pack/plugins/alerting/server/routes/get_rule_alert_summary.test.ts index f7fe1a3406e9c..5eca11c27a4b2 100644 --- a/x-pack/plugins/alerting/server/routes/get_rule_alert_summary.test.ts +++ b/x-pack/plugins/alerting/server/routes/get_rule_alert_summary.test.ts @@ -12,6 +12,7 @@ import { mockHandlerArguments } from './_mock_handler_arguments'; import { SavedObjectsErrorHelpers } from '@kbn/core/server'; import { rulesClientMock } from '../rules_client.mock'; import { AlertSummary } from '../types'; +import { RULE_SAVED_OBJECT_TYPE } from '../saved_objects'; const rulesClient = rulesClientMock.create(); jest.mock('../lib/license_api_access', () => ({ @@ -98,7 +99,9 @@ describe('getRuleAlertSummaryRoute', () => { rulesClient.getAlertSummary = jest .fn() - .mockRejectedValueOnce(SavedObjectsErrorHelpers.createGenericNotFoundError('alert', '1')); + .mockRejectedValueOnce( + SavedObjectsErrorHelpers.createGenericNotFoundError(RULE_SAVED_OBJECT_TYPE, '1') + ); const [context, req, res] = mockHandlerArguments( { rulesClient }, diff --git a/x-pack/plugins/alerting/server/routes/get_rule_execution_kpi.test.ts b/x-pack/plugins/alerting/server/routes/get_rule_execution_kpi.test.ts index ff15a60623168..e92b54769d329 100644 --- a/x-pack/plugins/alerting/server/routes/get_rule_execution_kpi.test.ts +++ b/x-pack/plugins/alerting/server/routes/get_rule_execution_kpi.test.ts @@ -11,6 +11,7 @@ import { mockHandlerArguments } from './_mock_handler_arguments'; import { SavedObjectsErrorHelpers } from '@kbn/core/server'; import { rulesClientMock } from '../rules_client.mock'; import { getRuleExecutionKPIRoute } from './get_rule_execution_kpi'; +import { RULE_SAVED_OBJECT_TYPE } from '../saved_objects'; const rulesClient = rulesClientMock.create(); jest.mock('../lib/license_api_access', () => ({ @@ -83,7 +84,9 @@ describe('getRuleExecutionKPIRoute', () => { rulesClient.getRuleExecutionKPI = jest .fn() - .mockRejectedValueOnce(SavedObjectsErrorHelpers.createGenericNotFoundError('alert', '1')); + .mockRejectedValueOnce( + SavedObjectsErrorHelpers.createGenericNotFoundError(RULE_SAVED_OBJECT_TYPE, '1') + ); const [context, req, res] = mockHandlerArguments( { rulesClient }, diff --git a/x-pack/plugins/alerting/server/routes/get_rule_state.test.ts b/x-pack/plugins/alerting/server/routes/get_rule_state.test.ts index d84c6fe73b616..b68d751147ebe 100644 --- a/x-pack/plugins/alerting/server/routes/get_rule_state.test.ts +++ b/x-pack/plugins/alerting/server/routes/get_rule_state.test.ts @@ -11,6 +11,7 @@ import { licenseStateMock } from '../lib/license_state.mock'; import { mockHandlerArguments } from './_mock_handler_arguments'; import { SavedObjectsErrorHelpers } from '@kbn/core/server'; import { rulesClientMock } from '../rules_client.mock'; +import { RULE_SAVED_OBJECT_TYPE } from '../saved_objects'; const rulesClient = rulesClientMock.create(); jest.mock('../lib/license_api_access', () => ({ @@ -124,7 +125,9 @@ describe('getRuleStateRoute', () => { rulesClient.getAlertState = jest .fn() - .mockResolvedValueOnce(SavedObjectsErrorHelpers.createGenericNotFoundError('alert', '1')); + .mockResolvedValueOnce( + SavedObjectsErrorHelpers.createGenericNotFoundError(RULE_SAVED_OBJECT_TYPE, '1') + ); const [context, req, res] = mockHandlerArguments( { rulesClient }, diff --git a/x-pack/plugins/alerting/server/routes/legacy/get_alert_instance_summary.test.ts b/x-pack/plugins/alerting/server/routes/legacy/get_alert_instance_summary.test.ts index a78fcd7f86f65..c04aa0b39c022 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/get_alert_instance_summary.test.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/get_alert_instance_summary.test.ts @@ -13,6 +13,7 @@ import { SavedObjectsErrorHelpers } from '@kbn/core/server'; import { rulesClientMock } from '../../rules_client.mock'; import { AlertSummary } from '../../types'; import { trackLegacyRouteUsage } from '../../lib/track_legacy_route_usage'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; const rulesClient = rulesClientMock.create(); jest.mock('../../lib/license_api_access', () => ({ @@ -98,7 +99,9 @@ describe('getAlertInstanceSummaryRoute', () => { rulesClient.getAlertSummary = jest .fn() - .mockResolvedValueOnce(SavedObjectsErrorHelpers.createGenericNotFoundError('alert', '1')); + .mockResolvedValueOnce( + SavedObjectsErrorHelpers.createGenericNotFoundError(RULE_SAVED_OBJECT_TYPE, '1') + ); const [context, req, res] = mockHandlerArguments( { rulesClient }, diff --git a/x-pack/plugins/alerting/server/routes/legacy/get_alert_state.test.ts b/x-pack/plugins/alerting/server/routes/legacy/get_alert_state.test.ts index 67fd50a965ea4..eab550ee4b304 100644 --- a/x-pack/plugins/alerting/server/routes/legacy/get_alert_state.test.ts +++ b/x-pack/plugins/alerting/server/routes/legacy/get_alert_state.test.ts @@ -12,6 +12,7 @@ import { mockHandlerArguments } from '../_mock_handler_arguments'; import { SavedObjectsErrorHelpers } from '@kbn/core/server'; import { rulesClientMock } from '../../rules_client.mock'; import { trackLegacyRouteUsage } from '../../lib/track_legacy_route_usage'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; const rulesClient = rulesClientMock.create(); jest.mock('../../lib/license_api_access', () => ({ @@ -129,7 +130,9 @@ describe('getAlertStateRoute', () => { rulesClient.getAlertState = jest .fn() - .mockResolvedValueOnce(SavedObjectsErrorHelpers.createGenericNotFoundError('alert', '1')); + .mockResolvedValueOnce( + SavedObjectsErrorHelpers.createGenericNotFoundError(RULE_SAVED_OBJECT_TYPE, '1') + ); const [context, req, res] = mockHandlerArguments( { rulesClient }, diff --git a/x-pack/plugins/alerting/server/rules_client/common/audit_events.test.ts b/x-pack/plugins/alerting/server/rules_client/common/audit_events.test.ts index 781b8fe1f4715..6d42be630ffd1 100644 --- a/x-pack/plugins/alerting/server/rules_client/common/audit_events.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/common/audit_events.test.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; import { RuleAuditAction, ruleAuditEvent } from './audit_events'; describe('#ruleAuditEvent', () => { @@ -13,7 +14,7 @@ describe('#ruleAuditEvent', () => { ruleAuditEvent({ action: RuleAuditAction.CREATE, outcome: 'unknown', - savedObject: { type: 'alert', id: 'ALERT_ID' }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id: 'ALERT_ID' }, }) ).toMatchInlineSnapshot(` Object { @@ -43,7 +44,7 @@ describe('#ruleAuditEvent', () => { expect( ruleAuditEvent({ action: RuleAuditAction.CREATE, - savedObject: { type: 'alert', id: 'ALERT_ID' }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id: 'ALERT_ID' }, }) ).toMatchInlineSnapshot(` Object { @@ -73,7 +74,7 @@ describe('#ruleAuditEvent', () => { expect( ruleAuditEvent({ action: RuleAuditAction.CREATE, - savedObject: { type: 'alert', id: 'ALERT_ID' }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id: 'ALERT_ID' }, error: new Error('ERROR_MESSAGE'), }) ).toMatchInlineSnapshot(` diff --git a/x-pack/plugins/alerting/server/rules_client/common/retry_if_bulk_edit_conflicts.test.ts b/x-pack/plugins/alerting/server/rules_client/common/retry_if_bulk_edit_conflicts.test.ts index c5570a7422d02..d099ba8693c3f 100644 --- a/x-pack/plugins/alerting/server/rules_client/common/retry_if_bulk_edit_conflicts.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/common/retry_if_bulk_edit_conflicts.test.ts @@ -10,6 +10,7 @@ import { retryIfBulkEditConflicts } from './retry_if_bulk_edit_conflicts'; import { RETRY_IF_CONFLICTS_ATTEMPTS } from './wait_before_next_retry'; import { loggingSystemMock } from '@kbn/core/server/mocks'; import { BulkEditSkipReason } from '../../../common/bulk_edit'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; const mockFilter: KueryNode = { type: 'function', @@ -21,12 +22,12 @@ const mockLogger = loggingSystemMock.create().get(); const mockSuccessfulResult = { apiKeysToInvalidate: [], rules: [ - { id: '1', type: 'alert', attributes: {} }, - { id: '2', type: 'alert', attributes: { name: 'Test rule 2' } }, + { id: '1', type: RULE_SAVED_OBJECT_TYPE, attributes: {} }, + { id: '2', type: RULE_SAVED_OBJECT_TYPE, attributes: { name: 'Test rule 2' } }, ], resultSavedObjects: [ - { id: '1', type: 'alert', attributes: {}, references: [] }, - { id: '2', type: 'alert', attributes: { name: 'Test rule 2' }, references: [] }, + { id: '1', type: RULE_SAVED_OBJECT_TYPE, attributes: {}, references: [] }, + { id: '2', type: RULE_SAVED_OBJECT_TYPE, attributes: { name: 'Test rule 2' }, references: [] }, ], errors: [], skipped: [ @@ -50,10 +51,10 @@ function getOperationConflictsTimes(times: number) { return { ...mockSuccessfulResult, resultSavedObjects: [ - { id: '1', type: 'alert', attributes: {}, references: [] }, + { id: '1', type: RULE_SAVED_OBJECT_TYPE, attributes: {}, references: [] }, { id: '2', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: {}, references: [], error: { @@ -94,7 +95,7 @@ describe('retryIfBulkEditConflicts', () => { attributes: {}, id: '1', references: [], - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }, { attributes: { @@ -102,7 +103,7 @@ describe('retryIfBulkEditConflicts', () => { }, id: '2', references: [], - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }, ], }); diff --git a/x-pack/plugins/alerting/server/rules_client/common/retry_if_bulk_edit_conflicts.ts b/x-pack/plugins/alerting/server/rules_client/common/retry_if_bulk_edit_conflicts.ts index 1a506b1b3fa86..86e3897183849 100644 --- a/x-pack/plugins/alerting/server/rules_client/common/retry_if_bulk_edit_conflicts.ts +++ b/x-pack/plugins/alerting/server/rules_client/common/retry_if_bulk_edit_conflicts.ts @@ -14,6 +14,7 @@ import { convertRuleIdsToKueryNode } from '../../lib'; import { BulkOperationError } from '../types'; import { RuleAttributes } from '../../data/rule/types'; import { waitBeforeNextRetry, RETRY_IF_CONFLICTS_ATTEMPTS } from './wait_before_next_retry'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; // max number of failed SO ids in one retry filter const MaxIdsNumberInRetryFilter = 1000; @@ -70,7 +71,7 @@ export const retryIfBulkEditConflicts = async ( const conflictErrorMap = resultSavedObjects.reduce>( (acc, item) => { - if (item.type === 'alert' && item?.error?.statusCode === 409) { + if (item.type === RULE_SAVED_OBJECT_TYPE && item?.error?.statusCode === 409) { return acc.set(item.id, { message: item.error.message }); } return acc; diff --git a/x-pack/plugins/alerting/server/rules_client/common/validate_attributes.ts b/x-pack/plugins/alerting/server/rules_client/common/validate_attributes.ts index 80353ad0f6055..b58cc453d5800 100644 --- a/x-pack/plugins/alerting/server/rules_client/common/validate_attributes.ts +++ b/x-pack/plugins/alerting/server/rules_client/common/validate_attributes.ts @@ -8,6 +8,7 @@ import { KueryNode } from '@kbn/es-query'; import { get, isEmpty } from 'lodash'; import { alertMappings } from '../../../common/saved_objects/rules/mappings'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; const astFunctionType = ['is', 'range', 'nested']; @@ -137,7 +138,10 @@ export const validateFilterKueryNode = ({ }: ValidateFilterKueryNodeParams) => { const action = ({ index, fieldName }: IterateActionProps) => { if (index === 0) { - const firstAttribute = getFieldNameAttribute(fieldName, ['alert', 'attributes']); + const firstAttribute = getFieldNameAttribute(fieldName, [ + RULE_SAVED_OBJECT_TYPE, + 'attributes', + ]); if (excludedFieldNames.includes(firstAttribute)) { throw new Error(`Filter is not supported on this field ${fieldName}`); } diff --git a/x-pack/plugins/alerting/server/rules_client/lib/check_authorization_and_get_total.ts b/x-pack/plugins/alerting/server/rules_client/lib/check_authorization_and_get_total.ts index 4327176841ad4..a4e75c58bde4b 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/check_authorization_and_get_total.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/check_authorization_and_get_total.ts @@ -18,6 +18,7 @@ import { } from '../common/constants'; import { RulesClientContext } from '../types'; import { ruleAuditEvent, RuleAuditAction } from '../common/audit_events'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; export const checkAuthorizationAndGetTotal = async ( context: RulesClientContext, @@ -54,7 +55,7 @@ export const checkAuthorizationAndGetTotal = async ( filter, page: 1, perPage: 0, - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, aggs: { alertTypeId: { multi_terms: { diff --git a/x-pack/plugins/alerting/server/rules_client/lib/create_rule_saved_object.ts b/x-pack/plugins/alerting/server/rules_client/lib/create_rule_saved_object.ts index 14662953ba73b..cc0d6a895dcc0 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/create_rule_saved_object.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/create_rule_saved_object.ts @@ -17,6 +17,7 @@ import { updateMeta } from './update_meta'; import { scheduleTask } from './schedule_task'; import { getAlertFromRaw } from './get_alert_from_raw'; import { createRuleSo, deleteRuleSo, updateRuleSo } from '../../data/rule'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; interface CreateRuleSavedObjectParams { intervalInMs: number; @@ -56,7 +57,7 @@ export async function createRuleSavedObject { const currentRule: SavedObject = { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, name: 'rule-name', diff --git a/x-pack/plugins/alerting/server/rules_client/lib/resolve_rule_saved_object.ts b/x-pack/plugins/alerting/server/rules_client/lib/resolve_rule_saved_object.ts index 101f846ae9ba9..f133aea9035a0 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/resolve_rule_saved_object.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/resolve_rule_saved_object.ts @@ -11,6 +11,7 @@ import { ruleAuditEvent, RuleAuditAction } from '../common/audit_events'; import { RulesClientContext } from '../types'; import { resolveRuleSo } from '../../data/rule'; import { RuleAttributes } from '../../data/rule/types'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; interface ResolveRuleSavedObjectParams { ruleId: string; @@ -26,7 +27,7 @@ export async function resolveRuleSavedObject( ruleAuditEvent({ action: RuleAuditAction.RESOLVE, outcome: 'unknown', - savedObject: { type: 'alert', id: ruleId }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id: ruleId }, }) ); diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/format_legacy_actions.test.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/format_legacy_actions.test.ts index 072a1e98cc3de..8830ce96a6c43 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/format_legacy_actions.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/format_legacy_actions.test.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { RULE_SAVED_OBJECT_TYPE } from '../../../saved_objects'; import type { SavedObjectsFindResult, SavedObjectAttribute } from '@kbn/core/server'; import { loggingSystemMock, savedObjectsClientMock } from '@kbn/core/server/mocks'; @@ -37,7 +38,7 @@ describe('legacyGetBulkRuleActionsSavedObject', () => { test('calls "savedObjectsClient.find" with the expected "hasReferences"', async () => { await legacyGetBulkRuleActionsSavedObject({ alertIds: ['123'], savedObjectsClient, logger }); expect(savedObjectsClient.find).toHaveBeenCalledWith({ - hasReference: [{ id: '123', type: 'alert' }], + hasReference: [{ id: '123', type: RULE_SAVED_OBJECT_TYPE }], perPage: 10000, type: legacyRuleActionsSavedObjectType, }); @@ -70,7 +71,7 @@ describe('legacyGetBulkRuleActionsSavedObject', () => { { name: 'alert_0', id: 'alert-123', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }, { name: 'action_0', @@ -134,7 +135,7 @@ describe('legacyGetBulkRuleActionsSavedObject', () => { { name: 'alert_0', id: 'alert-123', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }, { name: 'action_0', @@ -163,7 +164,7 @@ describe('legacyGetBulkRuleActionsSavedObject', () => { { name: 'alert_0', id: 'alert-456', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }, { name: 'action_0', @@ -244,7 +245,7 @@ describe('legacyGetBulkRuleActionsSavedObject', () => { { name: 'alert_0', id: 'alert-123', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }, { name: 'action_0', @@ -330,7 +331,7 @@ describe('legacyGetBulkRuleActionsSavedObject', () => { { name: 'alert_0', id: 'alert-123', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }, { name: 'action_0', @@ -401,7 +402,7 @@ describe('legacyGetBulkRuleActionsSavedObject', () => { { name: 'alert_0', id: 'alert-123', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }, { name: 'action_0', @@ -468,7 +469,7 @@ describe('legacyGetBulkRuleActionsSavedObject', () => { id: '123', type: legacyRuleActionsSavedObjectType, references: [ - // Missing the "alert_0" of { name: 'alert_0', id: 'alert-123', type: 'alert', }, + // Missing the "alert_0" of { name: 'alert_0', id: 'alert-123', type: RULE_SAVED_OBJECT_TYPE, }, { name: 'action_0', id: 'action-123', @@ -539,7 +540,7 @@ describe('formatLegacyActions', () => { { name: 'alert_0', id: 'alert-123', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }, { name: 'action_0', diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/format_legacy_actions.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/format_legacy_actions.ts index a0aa3286f1f6d..74a56d1964ada 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/format_legacy_actions.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/format_legacy_actions.ts @@ -8,6 +8,7 @@ import { chunk } from 'lodash'; import type { SavedObjectsFindOptionsReference, Logger } from '@kbn/core/server'; import pMap from 'p-map'; +import { RULE_SAVED_OBJECT_TYPE } from '../../../saved_objects'; import { RuleAction, Rule } from '../../../types'; import type { RuleExecutorServices } from '../../..'; import { injectReferencesIntoActions } from '../../common'; @@ -49,7 +50,7 @@ export const legacyGetBulkRuleActionsSavedObject = async ({ }: LegacyGetBulkRuleActionsSavedObject): Promise> => { const references = alertIds.map((alertId) => ({ id: alertId, - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, })); const errors: unknown[] = []; const results = await pMap( @@ -82,7 +83,7 @@ export const legacyGetBulkRuleActionsSavedObject = async ({ return actionSavedObjects.reduce((acc: { [key: string]: LegacyActionsObj }, savedObject) => { const ruleAlertId = savedObject.references.find((reference) => { // Find the first rule alert and assume that is the one we want since we should only ever have 1. - return reference.type === 'alert'; + return reference.type === RULE_SAVED_OBJECT_TYPE; }); // We check to ensure we have found a "ruleAlertId" and hopefully we have. const ruleAlertIdKey = ruleAlertId != null ? ruleAlertId.id : undefined; diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/retrieve_migrated_legacy_actions.mock.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/retrieve_migrated_legacy_actions.mock.ts index d45c86c3f05a5..6a1a554655a3c 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/retrieve_migrated_legacy_actions.mock.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/retrieve_migrated_legacy_actions.mock.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { RULE_SAVED_OBJECT_TYPE } from '../../../saved_objects'; import type { SavedObjectsFindResponse, SavedObjectsFindResult, @@ -179,7 +180,7 @@ export const legacyGetSiemNotificationRuleNoActionsSOResult = ( ruleThrottle: 'no_actions', alertThrottle: null, }, - references: [{ id: ruleId, type: 'alert', name: 'alert_0' }], + references: [{ id: ruleId, type: RULE_SAVED_OBJECT_TYPE, name: 'alert_0' }], migrationVersion: { 'siem-detection-engine-rule-actions': '7.11.2', }, @@ -214,7 +215,7 @@ export const legacyGetSiemNotificationRuleEveryRunSOResult = ( ruleThrottle: 'rule', alertThrottle: null, }, - references: [{ id: ruleId, type: 'alert', name: 'alert_0' }], + references: [{ id: ruleId, type: RULE_SAVED_OBJECT_TYPE, name: 'alert_0' }], migrationVersion: { 'siem-detection-engine-rule-actions': '7.11.2', }, @@ -251,7 +252,7 @@ export const legacyGetSiemNotificationRuleHourlyActionsSOResult = ( alertThrottle: '1h', }, references: [ - { id: ruleId, type: 'alert', name: 'alert_0' }, + { id: ruleId, type: RULE_SAVED_OBJECT_TYPE, name: 'alert_0' }, { id: connectorId, type: 'action', name: 'action_0' }, ], migrationVersion: { @@ -290,7 +291,7 @@ export const legacyGetSiemNotificationRuleDailyActionsSOResult = ( alertThrottle: '1d', }, references: [ - { id: ruleId, type: 'alert', name: 'alert_0' }, + { id: ruleId, type: RULE_SAVED_OBJECT_TYPE, name: 'alert_0' }, { id: connectorId, type: 'action', name: 'action_0' }, ], migrationVersion: { @@ -329,7 +330,7 @@ export const legacyGetSiemNotificationRuleWeeklyActionsSOResult = ( alertThrottle: '7d', }, references: [ - { id: ruleId, type: 'alert', name: 'alert_0' }, + { id: ruleId, type: RULE_SAVED_OBJECT_TYPE, name: 'alert_0' }, { id: connectorId, type: 'action', name: 'action_0' }, ], migrationVersion: { diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/retrieve_migrated_legacy_actions.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/retrieve_migrated_legacy_actions.ts index 9678dff6898c8..fe4bb56713861 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/retrieve_migrated_legacy_actions.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/retrieve_migrated_legacy_actions.ts @@ -6,6 +6,7 @@ */ import type { SavedObjectReference } from '@kbn/core/server'; +import { RULE_SAVED_OBJECT_TYPE } from '../../../saved_objects'; import type { RulesClientContext } from '../..'; import { RawRuleAction } from '../../../types'; import { find } from '../../methods/find'; @@ -52,7 +53,7 @@ export const retrieveMigratedLegacyActions: RetrieveMigratedLegacyActions = asyn options: { filter: 'alert.attributes.alertTypeId:(siem.notifications)', hasReference: { - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, id: ruleId, }, }, @@ -60,7 +61,7 @@ export const retrieveMigratedLegacyActions: RetrieveMigratedLegacyActions = asyn unsecuredSavedObjectsClient.find({ type: legacyRuleActionsSavedObjectType, hasReference: { - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, id: ruleId, }, }), diff --git a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.test.ts b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.test.ts index cea32ce3e1913..f7d9a70a3c53c 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/siem_legacy_actions/transform_legacy_actions.test.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { RULE_SAVED_OBJECT_TYPE } from '../../../saved_objects'; import type { SavedObjectReference } from '@kbn/core/server'; import { transformFromLegacyActions } from './transform_legacy_actions'; @@ -48,7 +49,7 @@ describe('transformFromLegacyActions', () => { { name: 'alert_0', id: 'alert-1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }, ]); diff --git a/x-pack/plugins/alerting/server/rules_client/lib/untrack_rule_alerts.ts b/x-pack/plugins/alerting/server/rules_client/lib/untrack_rule_alerts.ts index 17794aeb4ebdf..d69d84c7fd8c7 100644 --- a/x-pack/plugins/alerting/server/rules_client/lib/untrack_rule_alerts.ts +++ b/x-pack/plugins/alerting/server/rules_client/lib/untrack_rule_alerts.ts @@ -15,6 +15,7 @@ import { EVENT_LOG_ACTIONS } from '../../plugin'; import { createAlertEventLogRecordObject } from '../../lib/create_alert_event_log_record_object'; import { RulesClientContext } from '../types'; import { RuleAttributes } from '../../data/rule/types'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; export const untrackRuleAlerts = async ( context: RulesClientContext, @@ -67,7 +68,7 @@ export const untrackRuleAlerts = async ( savedObjects: [ { id, - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, typeId: attributes.alertTypeId, relation: SAVED_OBJECT_REL_PRIMARY, }, diff --git a/x-pack/plugins/alerting/server/rules_client/methods/bulk_enable.ts b/x-pack/plugins/alerting/server/rules_client/methods/bulk_enable.ts index cac39ccb367d4..0f01e88cfaa12 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/bulk_enable.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/bulk_enable.ts @@ -32,6 +32,7 @@ import { import { RulesClientContext, BulkOperationError, BulkOptions } from '../types'; import { validateScheduleLimit } from '../../application/rule/methods/get_schedule_frequency'; import { RuleAttributes } from '../../data/rule/types'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; const getShouldScheduleTask = async ( context: RulesClientContext, @@ -118,7 +119,7 @@ const bulkEnableRulesWithOCC = async ( await context.encryptedSavedObjectsClient.createPointInTimeFinderDecryptedAsInternalUser( { filter: filter ? nodeBuilder.and([filter, additionalFilter]) : additionalFilter, - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, perPage: 100, ...(context.namespace ? { namespaces: [context.namespace] } : undefined), } @@ -241,7 +242,7 @@ const bulkEnableRulesWithOCC = async ( ruleAuditEvent({ action: RuleAuditAction.ENABLE, outcome: 'unknown', - savedObject: { type: 'alert', id: rule.id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id: rule.id }, }) ); } catch (error) { diff --git a/x-pack/plugins/alerting/server/rules_client/methods/clear_expired_snoozes.ts b/x-pack/plugins/alerting/server/rules_client/methods/clear_expired_snoozes.ts index 90156246331f0..70f0e8a29eac2 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/clear_expired_snoozes.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/clear_expired_snoozes.ts @@ -6,7 +6,7 @@ */ import { RuleTypeParams, SanitizedRule } from '../../../common'; -import { partiallyUpdateAlert } from '../../saved_objects'; +import { partiallyUpdateRule } from '../../saved_objects'; import { isSnoozeExpired } from '../../lib'; import { RulesClientContext } from '../types'; import { updateMeta } from '../lib'; @@ -39,7 +39,7 @@ export async function clearExpiredSnoozes( const updateOptions = { version, refresh: false }; - await partiallyUpdateAlert( + await partiallyUpdateRule( context.unsecuredSavedObjectsClient, rule.id, updateAttributes, diff --git a/x-pack/plugins/alerting/server/rules_client/methods/clone.ts b/x-pack/plugins/alerting/server/rules_client/methods/clone.ts index f11b5c69012c4..acc8b66d6fde8 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/clone.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/clone.ts @@ -19,6 +19,7 @@ import { getRuleExecutionStatusPendingAttributes } from '../../lib/rule_executio import { isDetectionEngineAADRuleType } from '../../saved_objects/migrations/utils'; import { createNewAPIKeySet, createRuleSavedObject } from '../lib'; import { RulesClientContext } from '../types'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; export type CloneArguments = [string, { newId?: string }]; @@ -33,9 +34,13 @@ export async function clone( ruleSavedObject = await withSpan( { name: 'encryptedSavedObjectsClient.getDecryptedAsInternalUser', type: 'rules' }, () => - context.encryptedSavedObjectsClient.getDecryptedAsInternalUser('alert', id, { - namespace: context.namespace, - }) + context.encryptedSavedObjectsClient.getDecryptedAsInternalUser( + RULE_SAVED_OBJECT_TYPE, + id, + { + namespace: context.namespace, + } + ) ); } catch (e) { // We'll skip invalidating the API key since we failed to load the decrypted saved object @@ -45,7 +50,7 @@ export async function clone( // Still attempt to load the object using SOC ruleSavedObject = await withSpan( { name: 'unsecuredSavedObjectsClient.get', type: 'rules' }, - () => context.unsecuredSavedObjectsClient.get('alert', id) + () => context.unsecuredSavedObjectsClient.get(RULE_SAVED_OBJECT_TYPE, id) ); } @@ -80,7 +85,7 @@ export async function clone( context.auditLogger?.log( ruleAuditEvent({ action: RuleAuditAction.CREATE, - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, error, }) ); @@ -125,7 +130,7 @@ export async function clone( ruleAuditEvent({ action: RuleAuditAction.CREATE, outcome: 'unknown', - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, }) ); diff --git a/x-pack/plugins/alerting/server/rules_client/methods/delete.ts b/x-pack/plugins/alerting/server/rules_client/methods/delete.ts index 566945f0357fc..53baa548d783c 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/delete.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/delete.ts @@ -14,6 +14,7 @@ import { ruleAuditEvent, RuleAuditAction } from '../common/audit_events'; import { RulesClientContext } from '../types'; import { untrackRuleAlerts, migrateLegacyActions } from '../lib'; import { RuleAttributes } from '../../data/rule/types'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; export async function deleteRule(context: RulesClientContext, { id }: { id: string }) { return await retryIfConflicts( @@ -31,9 +32,13 @@ async function deleteWithOCC(context: RulesClientContext, { id }: { id: string } try { const decryptedAlert = - await context.encryptedSavedObjectsClient.getDecryptedAsInternalUser('alert', id, { - namespace: context.namespace, - }); + await context.encryptedSavedObjectsClient.getDecryptedAsInternalUser( + RULE_SAVED_OBJECT_TYPE, + id, + { + namespace: context.namespace, + } + ); apiKeyToInvalidate = decryptedAlert.attributes.apiKey; apiKeyCreatedByUser = decryptedAlert.attributes.apiKeyCreatedByUser; taskIdToRemove = decryptedAlert.attributes.scheduledTaskId; @@ -44,7 +49,10 @@ async function deleteWithOCC(context: RulesClientContext, { id }: { id: string } `delete(): Failed to load API key to invalidate on alert ${id}: ${e.message}` ); // Still attempt to load the scheduledTaskId using SOC - const alert = await context.unsecuredSavedObjectsClient.get('alert', id); + const alert = await context.unsecuredSavedObjectsClient.get( + RULE_SAVED_OBJECT_TYPE, + id + ); taskIdToRemove = alert.attributes.scheduledTaskId; attributes = alert.attributes; } @@ -60,7 +68,7 @@ async function deleteWithOCC(context: RulesClientContext, { id }: { id: string } context.auditLogger?.log( ruleAuditEvent({ action: RuleAuditAction.DELETE, - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, error, }) ); @@ -78,10 +86,10 @@ async function deleteWithOCC(context: RulesClientContext, { id }: { id: string } ruleAuditEvent({ action: RuleAuditAction.DELETE, outcome: 'unknown', - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, }) ); - const removeResult = await context.unsecuredSavedObjectsClient.delete('alert', id); + const removeResult = await context.unsecuredSavedObjectsClient.delete(RULE_SAVED_OBJECT_TYPE, id); await Promise.all([ taskIdToRemove ? context.taskManager.removeIfExists(taskIdToRemove) : null, diff --git a/x-pack/plugins/alerting/server/rules_client/methods/disable.ts b/x-pack/plugins/alerting/server/rules_client/methods/disable.ts index d51a5793371f0..38b0dcc7e17d6 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/disable.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/disable.ts @@ -13,6 +13,7 @@ import { ruleAuditEvent, RuleAuditAction } from '../common/audit_events'; import { RulesClientContext } from '../types'; import { untrackRuleAlerts, updateMeta, migrateLegacyActions } from '../lib'; import { RuleAttributes } from '../../data/rule/types'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; export async function disable(context: RulesClientContext, { id }: { id: string }): Promise { return await retryIfConflicts( @@ -29,16 +30,23 @@ async function disableWithOCC(context: RulesClientContext, { id }: { id: string try { const decryptedAlert = - await context.encryptedSavedObjectsClient.getDecryptedAsInternalUser('alert', id, { - namespace: context.namespace, - }); + await context.encryptedSavedObjectsClient.getDecryptedAsInternalUser( + RULE_SAVED_OBJECT_TYPE, + id, + { + namespace: context.namespace, + } + ); attributes = decryptedAlert.attributes; version = decryptedAlert.version; references = decryptedAlert.references; } catch (e) { context.logger.error(`disable(): Failed to load API key of alert ${id}: ${e.message}`); // Still attempt to load the attributes and version using SOC - const alert = await context.unsecuredSavedObjectsClient.get('alert', id); + const alert = await context.unsecuredSavedObjectsClient.get( + RULE_SAVED_OBJECT_TYPE, + id + ); attributes = alert.attributes; version = alert.version; references = alert.references; @@ -55,7 +63,7 @@ async function disableWithOCC(context: RulesClientContext, { id }: { id: string context.auditLogger?.log( ruleAuditEvent({ action: RuleAuditAction.DISABLE, - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, error, }) ); @@ -68,7 +76,7 @@ async function disableWithOCC(context: RulesClientContext, { id }: { id: string ruleAuditEvent({ action: RuleAuditAction.DISABLE, outcome: 'unknown', - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, }) ); @@ -83,7 +91,7 @@ async function disableWithOCC(context: RulesClientContext, { id }: { id: string }); await context.unsecuredSavedObjectsClient.update( - 'alert', + RULE_SAVED_OBJECT_TYPE, id, updateMeta(context, { ...attributes, diff --git a/x-pack/plugins/alerting/server/rules_client/methods/enable.ts b/x-pack/plugins/alerting/server/rules_client/methods/enable.ts index 53df42f012ad8..995e989121802 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/enable.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/enable.ts @@ -16,6 +16,7 @@ import { RulesClientContext } from '../types'; import { updateMeta, createNewAPIKeySet, scheduleTask, migrateLegacyActions } from '../lib'; import { validateScheduleLimit } from '../../application/rule/methods/get_schedule_frequency'; import { getRuleCircuitBreakerErrorMessage } from '../../../common'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; export async function enable(context: RulesClientContext, { id }: { id: string }): Promise { return await retryIfConflicts( @@ -33,9 +34,13 @@ async function enableWithOCC(context: RulesClientContext, { id }: { id: string } try { const decryptedAlert = - await context.encryptedSavedObjectsClient.getDecryptedAsInternalUser('alert', id, { - namespace: context.namespace, - }); + await context.encryptedSavedObjectsClient.getDecryptedAsInternalUser( + RULE_SAVED_OBJECT_TYPE, + id, + { + namespace: context.namespace, + } + ); existingApiKey = decryptedAlert.attributes.apiKey; attributes = decryptedAlert.attributes; version = decryptedAlert.version; @@ -43,7 +48,10 @@ async function enableWithOCC(context: RulesClientContext, { id }: { id: string } } catch (e) { context.logger.error(`enable(): Failed to load API key of alert ${id}: ${e.message}`); // Still attempt to load the attributes and version using SOC - const alert = await context.unsecuredSavedObjectsClient.get('alert', id); + const alert = await context.unsecuredSavedObjectsClient.get( + RULE_SAVED_OBJECT_TYPE, + id + ); attributes = alert.attributes; version = alert.version; references = alert.references; @@ -80,7 +88,7 @@ async function enableWithOCC(context: RulesClientContext, { id }: { id: string } context.auditLogger?.log( ruleAuditEvent({ action: RuleAuditAction.ENABLE, - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, error, }) ); @@ -91,7 +99,7 @@ async function enableWithOCC(context: RulesClientContext, { id }: { id: string } ruleAuditEvent({ action: RuleAuditAction.ENABLE, outcome: 'unknown', - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, }) ); @@ -140,7 +148,7 @@ async function enableWithOCC(context: RulesClientContext, { id }: { id: string } // we call create with overwrite=true if (migratedActions.hasLegacyActions) { await context.unsecuredSavedObjectsClient.create( - 'alert', + RULE_SAVED_OBJECT_TYPE, { ...updateAttributes, actions: migratedActions.resultedActions, @@ -155,9 +163,14 @@ async function enableWithOCC(context: RulesClientContext, { id }: { id: string } } ); } else { - await context.unsecuredSavedObjectsClient.update('alert', id, updateAttributes, { - version, - }); + await context.unsecuredSavedObjectsClient.update( + RULE_SAVED_OBJECT_TYPE, + id, + updateAttributes, + { + version, + } + ); } } catch (e) { throw e; @@ -193,7 +206,7 @@ async function enableWithOCC(context: RulesClientContext, { id }: { id: string } schedule: attributes.schedule as IntervalSchedule, throwOnConflict: false, }); - await context.unsecuredSavedObjectsClient.update('alert', id, { + await context.unsecuredSavedObjectsClient.update(RULE_SAVED_OBJECT_TYPE, id, { scheduledTaskId: scheduledTask.id, }); } else { diff --git a/x-pack/plugins/alerting/server/rules_client/methods/find.ts b/x-pack/plugins/alerting/server/rules_client/methods/find.ts index 537dfef55aff0..7ca51bcb16f19 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/find.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/find.ts @@ -29,6 +29,7 @@ import { alertingAuthorizationFilterOpts } from '../common/constants'; import { getAlertFromRaw } from '../lib/get_alert_from_raw'; import type { IndexType, RulesClientContext } from '../types'; import { formatLegacyActions } from '../lib'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; export interface FindParams { options?: FindOptions; @@ -133,7 +134,7 @@ export async function find( ? nodeBuilder.and([filterKueryNode, authorizationFilter as KueryNode]) : authorizationFilter) ?? filterKueryNode, fields: fields ? includeFieldsRequiredForAuthentication(fields) : fields, - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }); const siemRules: Rule[] = []; @@ -149,7 +150,7 @@ export async function find( context.auditLogger?.log( ruleAuditEvent({ action: RuleAuditAction.FIND, - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, error, }) ); @@ -179,7 +180,7 @@ export async function find( context.auditLogger?.log( ruleAuditEvent({ action: RuleAuditAction.FIND, - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, }) ) ); diff --git a/x-pack/plugins/alerting/server/rules_client/methods/get.ts b/x-pack/plugins/alerting/server/rules_client/methods/get.ts index 8b25f990d3cf1..3fb3a9e2e43c6 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/get.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/get.ts @@ -12,6 +12,7 @@ import { ruleAuditEvent, RuleAuditAction } from '../common/audit_events'; import { getAlertFromRaw } from '../lib/get_alert_from_raw'; import { RulesClientContext } from '../types'; import { formatLegacyActions } from '../lib'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; export interface GetParams { id: string; @@ -29,7 +30,7 @@ export async function get( excludeFromPublicApi = false, }: GetParams ): Promise | SanitizedRuleWithLegacyId> { - const result = await context.unsecuredSavedObjectsClient.get('alert', id); + const result = await context.unsecuredSavedObjectsClient.get(RULE_SAVED_OBJECT_TYPE, id); try { await context.authorization.ensureAuthorized({ ruleTypeId: result.attributes.alertTypeId, @@ -41,7 +42,7 @@ export async function get( context.auditLogger?.log( ruleAuditEvent({ action: RuleAuditAction.GET, - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, error, }) ); @@ -50,7 +51,7 @@ export async function get( context.auditLogger?.log( ruleAuditEvent({ action: RuleAuditAction.GET, - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, }) ); const rule = getAlertFromRaw( diff --git a/x-pack/plugins/alerting/server/rules_client/methods/get_action_error_log.ts b/x-pack/plugins/alerting/server/rules_client/methods/get_action_error_log.ts index ebd1862d6b3f6..9ff65893da3a0 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/get_action_error_log.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/get_action_error_log.ts @@ -20,6 +20,7 @@ import { formatExecutionErrorsResult } from '../../lib/format_execution_log_erro import { parseDate } from '../common'; import { RulesClientContext } from '../types'; import { get } from './get'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; const actionErrorLogDefaultFilter = 'event.provider:actions AND ((event.action:execute AND (event.outcome:failure OR kibana.alerting.status:warning)) OR (event.action:execute-timeout))'; @@ -53,7 +54,7 @@ export async function getActionErrorLog( context.auditLogger?.log( ruleAuditEvent({ action: RuleAuditAction.GET_ACTION_ERROR_LOG, - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, error, }) ); @@ -63,7 +64,7 @@ export async function getActionErrorLog( context.auditLogger?.log( ruleAuditEvent({ action: RuleAuditAction.GET_ACTION_ERROR_LOG, - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, }) ); @@ -76,7 +77,7 @@ export async function getActionErrorLog( try { const errorResult = await eventLogClient.findEventsBySavedObjectIds( - 'alert', + RULE_SAVED_OBJECT_TYPE, [id], { start: parsedDateStart.toISOString(), @@ -130,7 +131,7 @@ export async function getActionErrorLogWithAuth( context.auditLogger?.log( ruleAuditEvent({ action: RuleAuditAction.GET_ACTION_ERROR_LOG, - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, }) ); @@ -143,7 +144,7 @@ export async function getActionErrorLogWithAuth( try { const errorResult = await eventLogClient.findEventsWithAuthFilter( - 'alert', + RULE_SAVED_OBJECT_TYPE, [id], authorizationTuple.filter as KueryNode, namespace, diff --git a/x-pack/plugins/alerting/server/rules_client/methods/get_alert_summary.ts b/x-pack/plugins/alerting/server/rules_client/methods/get_alert_summary.ts index 8d194b850816b..7739b03372007 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/get_alert_summary.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/get_alert_summary.ts @@ -13,6 +13,7 @@ import { parseDuration } from '../../../common/parse_duration'; import { parseDate } from '../common'; import { RulesClientContext } from '../types'; import { get } from './get'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; export interface GetAlertSummaryParams { id: string; @@ -48,7 +49,7 @@ export async function getAlertSummary( try { const [queryResults, executionResults] = await Promise.all([ eventLogClient.findEventsBySavedObjectIds( - 'alert', + RULE_SAVED_OBJECT_TYPE, [id], { page: 1, @@ -62,7 +63,7 @@ export async function getAlertSummary( rule.legacyId !== null ? [rule.legacyId] : undefined ), eventLogClient.findEventsBySavedObjectIds( - 'alert', + RULE_SAVED_OBJECT_TYPE, [id], { page: 1, diff --git a/x-pack/plugins/alerting/server/rules_client/methods/get_execution_kpi.ts b/x-pack/plugins/alerting/server/rules_client/methods/get_execution_kpi.ts index 734df53c9cb29..c063b8f90c614 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/get_execution_kpi.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/get_execution_kpi.ts @@ -20,6 +20,7 @@ import { import { RulesClientContext } from '../types'; import { parseDate } from '../common'; import { get } from './get'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; export interface GetRuleExecutionKPIParams { id: string; @@ -54,7 +55,7 @@ export async function getRuleExecutionKPI( context.auditLogger?.log( ruleAuditEvent({ action: RuleAuditAction.GET_RULE_EXECUTION_KPI, - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, error, }) ); @@ -64,7 +65,7 @@ export async function getRuleExecutionKPI( context.auditLogger?.log( ruleAuditEvent({ action: RuleAuditAction.GET_RULE_EXECUTION_KPI, - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, }) ); @@ -77,7 +78,7 @@ export async function getRuleExecutionKPI( try { const aggResult = await eventLogClient.aggregateEventsBySavedObjectIds( - 'alert', + RULE_SAVED_OBJECT_TYPE, [id], { start: parsedDateStart.toISOString(), @@ -138,7 +139,7 @@ export async function getGlobalExecutionKpiWithAuth( try { const aggResult = await eventLogClient.aggregateEventsWithAuthFilter( - 'alert', + RULE_SAVED_OBJECT_TYPE, authorizationTuple.filter as KueryNode, { start: parsedDateStart.toISOString(), diff --git a/x-pack/plugins/alerting/server/rules_client/methods/get_execution_log.ts b/x-pack/plugins/alerting/server/rules_client/methods/get_execution_log.ts index 006109d71b4b5..f240b257bae6e 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/get_execution_log.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/get_execution_log.ts @@ -22,6 +22,7 @@ import { IExecutionLogResult } from '../../../common'; import { parseDate } from '../common'; import { RulesClientContext } from '../types'; import { get } from './get'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; export interface GetExecutionLogByIdParams { id: string; @@ -62,7 +63,7 @@ export async function getExecutionLogForRule( context.auditLogger?.log( ruleAuditEvent({ action: RuleAuditAction.GET_EXECUTION_LOG, - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, error, }) ); @@ -72,7 +73,7 @@ export async function getExecutionLogForRule( context.auditLogger?.log( ruleAuditEvent({ action: RuleAuditAction.GET_EXECUTION_LOG, - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, }) ); @@ -85,7 +86,7 @@ export async function getExecutionLogForRule( try { const aggResult = await eventLogClient.aggregateEventsBySavedObjectIds( - 'alert', + RULE_SAVED_OBJECT_TYPE, [id], { start: parsedDateStart.toISOString(), @@ -151,7 +152,7 @@ export async function getGlobalExecutionLogWithAuth( try { const aggResult = await eventLogClient.aggregateEventsWithAuthFilter( - 'alert', + RULE_SAVED_OBJECT_TYPE, authorizationTuple.filter as KueryNode, { start: parsedDateStart.toISOString(), diff --git a/x-pack/plugins/alerting/server/rules_client/methods/mute_all.ts b/x-pack/plugins/alerting/server/rules_client/methods/mute_all.ts index 72a693aace7ac..273d794a52dc8 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/mute_all.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/mute_all.ts @@ -8,7 +8,7 @@ import { RawRule } from '../../types'; import { WriteOperations, AlertingAuthorizationEntity } from '../../authorization'; import { retryIfConflicts } from '../../lib/retry_if_conflicts'; -import { partiallyUpdateAlert } from '../../saved_objects'; +import { partiallyUpdateRule, RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; import { ruleAuditEvent, RuleAuditAction } from '../common/audit_events'; import { RulesClientContext } from '../types'; import { updateMetaAttributes } from '../lib'; @@ -25,7 +25,7 @@ export async function muteAll(context: RulesClientContext, { id }: { id: string async function muteAllWithOCC(context: RulesClientContext, { id }: { id: string }) { const { attributes, version } = await context.unsecuredSavedObjectsClient.get( - 'alert', + RULE_SAVED_OBJECT_TYPE, id ); @@ -44,7 +44,7 @@ async function muteAllWithOCC(context: RulesClientContext, { id }: { id: string context.auditLogger?.log( ruleAuditEvent({ action: RuleAuditAction.MUTE, - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, error, }) ); @@ -55,7 +55,7 @@ async function muteAllWithOCC(context: RulesClientContext, { id }: { id: string ruleAuditEvent({ action: RuleAuditAction.MUTE, outcome: 'unknown', - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, }) ); @@ -70,7 +70,7 @@ async function muteAllWithOCC(context: RulesClientContext, { id }: { id: string }); const updateOptions = { version }; - await partiallyUpdateAlert( + await partiallyUpdateRule( context.unsecuredSavedObjectsClient, id, updateAttributes, diff --git a/x-pack/plugins/alerting/server/rules_client/methods/run_soon.ts b/x-pack/plugins/alerting/server/rules_client/methods/run_soon.ts index 106f12ef50f6f..8f2d605f500dc 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/run_soon.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/run_soon.ts @@ -11,9 +11,13 @@ import { Rule } from '../../types'; import { ReadOperations, AlertingAuthorizationEntity } from '../../authorization'; import { ruleAuditEvent, RuleAuditAction } from '../common/audit_events'; import { RulesClientContext } from '../types'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; export async function runSoon(context: RulesClientContext, { id }: { id: string }) { - const { attributes } = await context.unsecuredSavedObjectsClient.get('alert', id); + const { attributes } = await context.unsecuredSavedObjectsClient.get( + RULE_SAVED_OBJECT_TYPE, + id + ); try { await context.authorization.ensureAuthorized({ ruleTypeId: attributes.alertTypeId, @@ -29,7 +33,7 @@ export async function runSoon(context: RulesClientContext, { id }: { id: string context.auditLogger?.log( ruleAuditEvent({ action: RuleAuditAction.RUN_SOON, - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, error, }) ); @@ -40,7 +44,7 @@ export async function runSoon(context: RulesClientContext, { id }: { id: string ruleAuditEvent({ action: RuleAuditAction.RUN_SOON, outcome: 'unknown', - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, }) ); diff --git a/x-pack/plugins/alerting/server/rules_client/methods/unmute_all.ts b/x-pack/plugins/alerting/server/rules_client/methods/unmute_all.ts index 52403e2d8f70e..675387e0a600a 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/unmute_all.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/unmute_all.ts @@ -8,7 +8,7 @@ import { RawRule } from '../../types'; import { WriteOperations, AlertingAuthorizationEntity } from '../../authorization'; import { retryIfConflicts } from '../../lib/retry_if_conflicts'; -import { partiallyUpdateAlert } from '../../saved_objects'; +import { partiallyUpdateRule, RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; import { ruleAuditEvent, RuleAuditAction } from '../common/audit_events'; import { RulesClientContext } from '../types'; import { updateMetaAttributes } from '../lib'; @@ -28,7 +28,7 @@ export async function unmuteAll( async function unmuteAllWithOCC(context: RulesClientContext, { id }: { id: string }) { const { attributes, version } = await context.unsecuredSavedObjectsClient.get( - 'alert', + RULE_SAVED_OBJECT_TYPE, id ); @@ -47,7 +47,7 @@ async function unmuteAllWithOCC(context: RulesClientContext, { id }: { id: strin context.auditLogger?.log( ruleAuditEvent({ action: RuleAuditAction.UNMUTE, - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, error, }) ); @@ -58,7 +58,7 @@ async function unmuteAllWithOCC(context: RulesClientContext, { id }: { id: strin ruleAuditEvent({ action: RuleAuditAction.UNMUTE, outcome: 'unknown', - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, }) ); @@ -73,7 +73,7 @@ async function unmuteAllWithOCC(context: RulesClientContext, { id }: { id: strin }); const updateOptions = { version }; - await partiallyUpdateAlert( + await partiallyUpdateRule( context.unsecuredSavedObjectsClient, id, updateAttributes, diff --git a/x-pack/plugins/alerting/server/rules_client/methods/unmute_instance.ts b/x-pack/plugins/alerting/server/rules_client/methods/unmute_instance.ts index 86c894a7babd0..0b8e422f1a946 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/unmute_instance.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/unmute_instance.ts @@ -12,6 +12,7 @@ import { ruleAuditEvent, RuleAuditAction } from '../common/audit_events'; import { MuteOptions } from '../types'; import { RulesClientContext } from '../types'; import { updateMeta } from '../lib'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; export async function unmuteInstance( context: RulesClientContext, @@ -35,7 +36,7 @@ async function unmuteInstanceWithOCC( } ) { const { attributes, version } = await context.unsecuredSavedObjectsClient.get( - 'alert', + RULE_SAVED_OBJECT_TYPE, alertId ); @@ -53,7 +54,7 @@ async function unmuteInstanceWithOCC( context.auditLogger?.log( ruleAuditEvent({ action: RuleAuditAction.UNMUTE_ALERT, - savedObject: { type: 'alert', id: alertId }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id: alertId }, error, }) ); @@ -64,7 +65,7 @@ async function unmuteInstanceWithOCC( ruleAuditEvent({ action: RuleAuditAction.UNMUTE_ALERT, outcome: 'unknown', - savedObject: { type: 'alert', id: alertId }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id: alertId }, }) ); @@ -73,7 +74,7 @@ async function unmuteInstanceWithOCC( const mutedInstanceIds = attributes.mutedInstanceIds || []; if (!attributes.muteAll && mutedInstanceIds.includes(alertInstanceId)) { await context.unsecuredSavedObjectsClient.update( - 'alert', + RULE_SAVED_OBJECT_TYPE, alertId, updateMeta(context, { updatedBy: await context.getUserName(), diff --git a/x-pack/plugins/alerting/server/rules_client/methods/update.ts b/x-pack/plugins/alerting/server/rules_client/methods/update.ts index e302b02a0e163..8fb0de5f3519c 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/update.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/update.ts @@ -37,6 +37,7 @@ import { validateScheduleLimit, ValidateScheduleLimitResult, } from '../../application/rule/methods/get_schedule_frequency'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; type ShouldIncrementRevision = (params?: RuleTypeParams) => boolean; @@ -80,16 +81,23 @@ async function updateWithOCC( try { alertSavedObject = - await context.encryptedSavedObjectsClient.getDecryptedAsInternalUser('alert', id, { - namespace: context.namespace, - }); + await context.encryptedSavedObjectsClient.getDecryptedAsInternalUser( + RULE_SAVED_OBJECT_TYPE, + id, + { + namespace: context.namespace, + } + ); } catch (e) { // We'll skip invalidating the API key since we failed to load the decrypted saved object context.logger.error( `update(): Failed to load API key to invalidate on alert ${id}: ${e.message}` ); // Still attempt to load the object using SOC - alertSavedObject = await context.unsecuredSavedObjectsClient.get('alert', id); + alertSavedObject = await context.unsecuredSavedObjectsClient.get( + RULE_SAVED_OBJECT_TYPE, + id + ); } const { @@ -127,7 +135,7 @@ async function updateWithOCC( context.auditLogger?.log( ruleAuditEvent({ action: RuleAuditAction.UPDATE, - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, error, }) ); @@ -138,7 +146,7 @@ async function updateWithOCC( ruleAuditEvent({ action: RuleAuditAction.UPDATE, outcome: 'unknown', - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, }) ); @@ -277,7 +285,7 @@ async function updateAlert( try { updatedObject = await context.unsecuredSavedObjectsClient.create( - 'alert', + RULE_SAVED_OBJECT_TYPE, createAttributes, { id, diff --git a/x-pack/plugins/alerting/server/rules_client/methods/update_api_key.ts b/x-pack/plugins/alerting/server/rules_client/methods/update_api_key.ts index d8d78264c448b..ae9367b13963d 100644 --- a/x-pack/plugins/alerting/server/rules_client/methods/update_api_key.ts +++ b/x-pack/plugins/alerting/server/rules_client/methods/update_api_key.ts @@ -12,6 +12,7 @@ import { bulkMarkApiKeysForInvalidation } from '../../invalidate_pending_api_key import { ruleAuditEvent, RuleAuditAction } from '../common/audit_events'; import { createNewAPIKeySet, updateMeta } from '../lib'; import { RulesClientContext } from '../types'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; export async function updateApiKey( context: RulesClientContext, @@ -32,9 +33,13 @@ async function updateApiKeyWithOCC(context: RulesClientContext, { id }: { id: st try { const decryptedAlert = - await context.encryptedSavedObjectsClient.getDecryptedAsInternalUser('alert', id, { - namespace: context.namespace, - }); + await context.encryptedSavedObjectsClient.getDecryptedAsInternalUser( + RULE_SAVED_OBJECT_TYPE, + id, + { + namespace: context.namespace, + } + ); oldApiKeyToInvalidate = decryptedAlert.attributes.apiKey; oldApiKeyCreatedByUser = decryptedAlert.attributes.apiKeyCreatedByUser; attributes = decryptedAlert.attributes; @@ -45,7 +50,10 @@ async function updateApiKeyWithOCC(context: RulesClientContext, { id }: { id: st `updateApiKey(): Failed to load API key to invalidate on alert ${id}: ${e.message}` ); // Still attempt to load the attributes and version using SOC - const alert = await context.unsecuredSavedObjectsClient.get('alert', id); + const alert = await context.unsecuredSavedObjectsClient.get( + RULE_SAVED_OBJECT_TYPE, + id + ); attributes = alert.attributes; version = alert.version; } @@ -64,7 +72,7 @@ async function updateApiKeyWithOCC(context: RulesClientContext, { id }: { id: st context.auditLogger?.log( ruleAuditEvent({ action: RuleAuditAction.UPDATE_API_KEY, - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, error, }) ); @@ -92,14 +100,16 @@ async function updateApiKeyWithOCC(context: RulesClientContext, { id }: { id: st ruleAuditEvent({ action: RuleAuditAction.UPDATE_API_KEY, outcome: 'unknown', - savedObject: { type: 'alert', id }, + savedObject: { type: RULE_SAVED_OBJECT_TYPE, id }, }) ); context.ruleTypeRegistry.ensureRuleTypeEnabled(attributes.alertTypeId); try { - await context.unsecuredSavedObjectsClient.update('alert', id, updateAttributes, { version }); + await context.unsecuredSavedObjectsClient.update(RULE_SAVED_OBJECT_TYPE, id, updateAttributes, { + version, + }); } catch (e) { // Avoid unused API key await bulkMarkApiKeysForInvalidation( diff --git a/x-pack/plugins/alerting/server/rules_client/tests/bulk_enable.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/bulk_enable.test.ts index af03c5908daff..e9317972474cd 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/bulk_enable.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/bulk_enable.test.ts @@ -34,6 +34,7 @@ import { } from './test_helpers'; import { TaskStatus } from '@kbn/task-manager-plugin/server'; import { migrateLegacyActions } from '../lib'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; jest.mock('../lib/siem_legacy_actions/migrate_legacy_actions', () => { return { @@ -767,12 +768,12 @@ describe('bulkEnableRules', () => { expect(auditLogger.log.mock.calls[0][0]?.event?.action).toEqual('rule_enable'); expect(auditLogger.log.mock.calls[0][0]?.event?.outcome).toEqual('unknown'); expect(auditLogger.log.mock.calls[0][0]?.kibana).toEqual({ - saved_object: { id: 'id1', type: 'alert' }, + saved_object: { id: 'id1', type: RULE_SAVED_OBJECT_TYPE }, }); expect(auditLogger.log.mock.calls[1][0]?.event?.action).toEqual('rule_enable'); expect(auditLogger.log.mock.calls[1][0]?.event?.outcome).toEqual('unknown'); expect(auditLogger.log.mock.calls[1][0]?.kibana).toEqual({ - saved_object: { id: 'id2', type: 'alert' }, + saved_object: { id: 'id2', type: RULE_SAVED_OBJECT_TYPE }, }); }); diff --git a/x-pack/plugins/alerting/server/rules_client/tests/clear_expired_snoozes.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/clear_expired_snoozes.test.ts index b64f192045511..59dbfd8c87998 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/clear_expired_snoozes.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/clear_expired_snoozes.test.ts @@ -25,6 +25,7 @@ import { getBeforeSetup, mockedDateString } from './lib'; import { eventLoggerMock } from '@kbn/event-log-plugin/server/event_logger.mock'; import { TaskStatus } from '@kbn/task-manager-plugin/server'; import { RuleSnooze } from '../../types'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; jest.mock('../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation', () => ({ bulkMarkApiKeysForInvalidation: jest.fn(), @@ -113,7 +114,7 @@ describe('clearExpiredSnoozes()', () => { ]); await rulesClient.clearExpiredSnoozes({ rule: { ...attributes, id } }); expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( - 'alert', + RULE_SAVED_OBJECT_TYPE, '1', { updatedAt: '2019-02-12T21:01:22.479Z', @@ -158,7 +159,7 @@ describe('clearExpiredSnoozes()', () => { ]); await rulesClient.clearExpiredSnoozes({ rule: { ...attributes, id } }); expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( - 'alert', + RULE_SAVED_OBJECT_TYPE, '1', { updatedAt: '2019-02-12T21:01:22.479Z', @@ -208,7 +209,7 @@ describe('clearExpiredSnoozes()', () => { function setupTestWithSnoozeSchedule(snoozeSchedule: RuleSnooze) { const rule = { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { name: 'name', consumer: 'myApp', diff --git a/x-pack/plugins/alerting/server/rules_client/tests/delete.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/delete.test.ts index a4e581414744a..dbdfea132e2ef 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/delete.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/delete.test.ts @@ -24,6 +24,7 @@ import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup } from './lib'; import { bulkMarkApiKeysForInvalidation } from '../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation'; import { migrateLegacyActions } from '../lib'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; jest.mock('../lib/siem_legacy_actions/migrate_legacy_actions', () => { return { @@ -84,7 +85,7 @@ describe('delete()', () => { let rulesClient: RulesClient; const existingAlert = { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: 'myType', consumer: 'myApp', @@ -132,7 +133,7 @@ describe('delete()', () => { test('successfully removes an alert', async () => { const result = await rulesClient.delete({ id: '1' }); expect(result).toEqual({ success: true }); - expect(unsecuredSavedObjectsClient.delete).toHaveBeenCalledWith('alert', '1'); + expect(unsecuredSavedObjectsClient.delete).toHaveBeenCalledWith(RULE_SAVED_OBJECT_TYPE, '1'); expect(taskManager.removeIfExists).toHaveBeenCalledWith('task-123'); expect(bulkMarkApiKeysForInvalidation).toHaveBeenCalledTimes(1); expect(bulkMarkApiKeysForInvalidation).toHaveBeenCalledWith( @@ -140,9 +141,13 @@ describe('delete()', () => { expect.any(Object), expect.any(Object) ); - expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { - namespace: 'default', - }); + expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith( + RULE_SAVED_OBJECT_TYPE, + '1', + { + namespace: 'default', + } + ); expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); }); @@ -151,10 +156,10 @@ describe('delete()', () => { const result = await rulesClient.delete({ id: '1' }); expect(result).toEqual({ success: true }); - expect(unsecuredSavedObjectsClient.delete).toHaveBeenCalledWith('alert', '1'); + expect(unsecuredSavedObjectsClient.delete).toHaveBeenCalledWith(RULE_SAVED_OBJECT_TYPE, '1'); expect(taskManager.removeIfExists).toHaveBeenCalledWith('task-123'); expect(unsecuredSavedObjectsClient.create).not.toHaveBeenCalled(); - expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledWith('alert', '1'); + expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledWith(RULE_SAVED_OBJECT_TYPE, '1'); expect(rulesClientParams.logger.error).toHaveBeenCalledWith( 'delete(): Failed to load API key to invalidate on alert 1: Fail' ); @@ -298,7 +303,7 @@ describe('delete()', () => { action: 'rule_delete', outcome: 'unknown', }), - kibana: { saved_object: { id: '1', type: 'alert' } }, + kibana: { saved_object: { id: '1', type: RULE_SAVED_OBJECT_TYPE } }, }) ); }); @@ -316,7 +321,7 @@ describe('delete()', () => { kibana: { saved_object: { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }, }, error: { diff --git a/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts index ca6c242539a9a..01b9d551174cf 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/disable.test.ts @@ -25,6 +25,7 @@ import { eventLoggerMock } from '@kbn/event-log-plugin/server/event_logger.mock' import { TaskStatus } from '@kbn/task-manager-plugin/server'; import { migrateLegacyActions } from '../lib'; import { migrateLegacyActionsMock } from '../lib/siem_legacy_actions/retrieve_migrated_legacy_actions.mock'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; jest.mock('../lib/siem_legacy_actions/migrate_legacy_actions', () => { return { @@ -103,7 +104,7 @@ describe('disable()', () => { let rulesClient: RulesClient; const existingRule = { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { consumer: 'myApp', schedule: { interval: '10s' }, @@ -187,7 +188,7 @@ describe('disable()', () => { action: 'rule_disable', outcome: 'unknown', }), - kibana: { saved_object: { id: '1', type: 'alert' } }, + kibana: { saved_object: { id: '1', type: RULE_SAVED_OBJECT_TYPE } }, }) ); }); @@ -205,7 +206,7 @@ describe('disable()', () => { kibana: { saved_object: { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }, }, error: { @@ -220,11 +221,15 @@ describe('disable()', () => { test('disables an rule', async () => { await rulesClient.disable({ id: '1' }); expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); - expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { - namespace: 'default', - }); + expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith( + RULE_SAVED_OBJECT_TYPE, + '1', + { + namespace: 'default', + } + ); expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( - 'alert', + RULE_SAVED_OBJECT_TYPE, '1', { consumer: 'myApp', @@ -294,11 +299,15 @@ describe('disable()', () => { }); await rulesClient.disable({ id: '1' }); expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); - expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { - namespace: 'default', - }); + expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith( + RULE_SAVED_OBJECT_TYPE, + '1', + { + namespace: 'default', + } + ); expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( - 'alert', + RULE_SAVED_OBJECT_TYPE, '1', { consumer: 'myApp', @@ -339,7 +348,7 @@ describe('disable()', () => { event: { action: 'untracked-instance', category: ['alerts'], - kind: 'alert', + kind: RULE_SAVED_OBJECT_TYPE, }, kibana: { alert: { @@ -359,7 +368,7 @@ describe('disable()', () => { id: '1', namespace: 'default', rel: 'primary', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, type_id: 'myType', }, ], @@ -379,11 +388,15 @@ describe('disable()', () => { taskManager.get.mockRejectedValueOnce(new Error('Fail')); await rulesClient.disable({ id: '1' }); expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); - expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { - namespace: 'default', - }); + expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith( + RULE_SAVED_OBJECT_TYPE, + '1', + { + namespace: 'default', + } + ); expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( - 'alert', + RULE_SAVED_OBJECT_TYPE, '1', { consumer: 'myApp', @@ -428,12 +441,16 @@ describe('disable()', () => { test('falls back when getDecryptedAsInternalUser throws an error', async () => { encryptedSavedObjects.getDecryptedAsInternalUser.mockRejectedValueOnce(new Error('Fail')); await rulesClient.disable({ id: '1' }); - expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledWith('alert', '1'); - expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { - namespace: 'default', - }); + expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledWith(RULE_SAVED_OBJECT_TYPE, '1'); + expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith( + RULE_SAVED_OBJECT_TYPE, + '1', + { + namespace: 'default', + } + ); expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( - 'alert', + RULE_SAVED_OBJECT_TYPE, '1', { consumer: 'myApp', @@ -522,11 +539,15 @@ describe('disable()', () => { }); await rulesClient.disable({ id: '1' }); expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); - expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { - namespace: 'default', - }); + expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith( + RULE_SAVED_OBJECT_TYPE, + '1', + { + namespace: 'default', + } + ); expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( - 'alert', + RULE_SAVED_OBJECT_TYPE, '1', { consumer: 'myApp', @@ -571,11 +592,15 @@ describe('disable()', () => { `"Failed to remove task"` ); expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); - expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { - namespace: 'default', - }); + expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith( + RULE_SAVED_OBJECT_TYPE, + '1', + { + namespace: 'default', + } + ); expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( - 'alert', + RULE_SAVED_OBJECT_TYPE, '1', { consumer: 'myApp', diff --git a/x-pack/plugins/alerting/server/rules_client/tests/enable.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/enable.test.ts index 9ab3a919f01a5..aa587dfb99bfa 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/enable.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/enable.test.ts @@ -24,6 +24,7 @@ import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, setGlobalDate } from './lib'; import { migrateLegacyActions } from '../lib'; import { migrateLegacyActionsMock } from '../lib/siem_legacy_actions/retrieve_migrated_legacy_actions.mock'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; jest.mock('../lib/siem_legacy_actions/migrate_legacy_actions', () => { return { @@ -81,7 +82,7 @@ describe('enable()', () => { const existingRule = { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { name: 'name', consumer: 'myApp', @@ -191,7 +192,7 @@ describe('enable()', () => { action: 'rule_enable', outcome: 'unknown', }), - kibana: { saved_object: { id: '1', type: 'alert' } }, + kibana: { saved_object: { id: '1', type: RULE_SAVED_OBJECT_TYPE } }, }) ); }); @@ -209,7 +210,7 @@ describe('enable()', () => { kibana: { saved_object: { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }, }, error: { @@ -224,12 +225,16 @@ describe('enable()', () => { test('enables a rule', async () => { await rulesClient.enable({ id: '1' }); expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); - expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { - namespace: 'default', - }); + expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith( + RULE_SAVED_OBJECT_TYPE, + '1', + { + namespace: 'default', + } + ); expect(unsecuredSavedObjectsClient.create).not.toBeCalledWith('api_key_pending_invalidation'); expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( - 'alert', + RULE_SAVED_OBJECT_TYPE, '1', { name: 'name', @@ -280,13 +285,17 @@ describe('enable()', () => { }); await rulesClient.enable({ id: '1' }); expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); - expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { - namespace: 'default', - }); + expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith( + RULE_SAVED_OBJECT_TYPE, + '1', + { + namespace: 'default', + } + ); expect(unsecuredSavedObjectsClient.create).not.toBeCalledWith('api_key_pending_invalidation'); expect(rulesClientParams.createAPIKey).toHaveBeenCalledWith('Alerting: myType/name'); expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( - 'alert', + RULE_SAVED_OBJECT_TYPE, '1', { name: 'name', @@ -353,7 +362,7 @@ describe('enable()', () => { await rulesClient.enable({ id: '1' }); expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( - 'alert', + RULE_SAVED_OBJECT_TYPE, '1', { name: 'name', @@ -412,7 +421,7 @@ describe('enable()', () => { encryptedSavedObjects.getDecryptedAsInternalUser.mockRejectedValue(new Error('Fail')); await rulesClient.enable({ id: '1' }); - expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledWith('alert', '1'); + expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledWith(RULE_SAVED_OBJECT_TYPE, '1'); expect(rulesClientParams.logger.error).toHaveBeenCalledWith( 'enable(): Failed to load API key of alert 1: Fail' ); @@ -451,9 +460,13 @@ describe('enable()', () => { test('enables task when scheduledTaskId is defined and task exists', async () => { await rulesClient.enable({ id: '1' }); expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); - expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { - namespace: 'default', - }); + expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith( + RULE_SAVED_OBJECT_TYPE, + '1', + { + namespace: 'default', + } + ); expect(unsecuredSavedObjectsClient.update).toHaveBeenCalled(); expect(taskManager.bulkEnable).toHaveBeenCalledWith(['task-123']); }); @@ -464,9 +477,13 @@ describe('enable()', () => { `"Failed to enable task"` ); expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); - expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { - namespace: 'default', - }); + expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith( + RULE_SAVED_OBJECT_TYPE, + '1', + { + namespace: 'default', + } + ); expect(unsecuredSavedObjectsClient.update).toHaveBeenCalled(); }); @@ -487,9 +504,13 @@ describe('enable()', () => { taskManager.get.mockRejectedValueOnce(new Error('Failed to get task!')); await rulesClient.enable({ id: '1' }); expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); - expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { - namespace: 'default', - }); + expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith( + RULE_SAVED_OBJECT_TYPE, + '1', + { + namespace: 'default', + } + ); expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledTimes(2); expect(taskManager.bulkEnable).not.toHaveBeenCalled(); expect(taskManager.schedule).toHaveBeenCalledWith({ @@ -511,9 +532,14 @@ describe('enable()', () => { }, scope: ['alerting'], }); - expect(unsecuredSavedObjectsClient.update).toHaveBeenNthCalledWith(2, 'alert', '1', { - scheduledTaskId: '1', - }); + expect(unsecuredSavedObjectsClient.update).toHaveBeenNthCalledWith( + 2, + RULE_SAVED_OBJECT_TYPE, + '1', + { + scheduledTaskId: '1', + } + ); }); test('schedules task when scheduledTaskId is not defined', async () => { @@ -536,9 +562,13 @@ describe('enable()', () => { }); await rulesClient.enable({ id: '1' }); expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); - expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { - namespace: 'default', - }); + expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith( + RULE_SAVED_OBJECT_TYPE, + '1', + { + namespace: 'default', + } + ); expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledTimes(2); expect(taskManager.bulkEnable).not.toHaveBeenCalled(); expect(taskManager.schedule).toHaveBeenCalledWith({ @@ -560,9 +590,14 @@ describe('enable()', () => { }, scope: ['alerting'], }); - expect(unsecuredSavedObjectsClient.update).toHaveBeenNthCalledWith(2, 'alert', '1', { - scheduledTaskId: '1', - }); + expect(unsecuredSavedObjectsClient.update).toHaveBeenNthCalledWith( + 2, + RULE_SAVED_OBJECT_TYPE, + '1', + { + scheduledTaskId: '1', + } + ); }); test('schedules task when task with scheduledTaskId exists but is unrecognized', async () => { @@ -582,9 +617,13 @@ describe('enable()', () => { taskManager.get.mockResolvedValue({ ...mockTask, status: TaskStatus.Unrecognized }); await rulesClient.enable({ id: '1' }); expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); - expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { - namespace: 'default', - }); + expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith( + RULE_SAVED_OBJECT_TYPE, + '1', + { + namespace: 'default', + } + ); expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledTimes(2); expect(taskManager.bulkEnable).not.toHaveBeenCalled(); expect(taskManager.removeIfExists).toHaveBeenCalledWith('task-123'); @@ -607,9 +646,14 @@ describe('enable()', () => { }, scope: ['alerting'], }); - expect(unsecuredSavedObjectsClient.update).toHaveBeenNthCalledWith(2, 'alert', '1', { - scheduledTaskId: '1', - }); + expect(unsecuredSavedObjectsClient.update).toHaveBeenNthCalledWith( + 2, + RULE_SAVED_OBJECT_TYPE, + '1', + { + scheduledTaskId: '1', + } + ); }); test('throws error when scheduling task fails', async () => { @@ -637,9 +681,13 @@ describe('enable()', () => { ); await rulesClient.enable({ id: '1' }); expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); - expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { - namespace: 'default', - }); + expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith( + RULE_SAVED_OBJECT_TYPE, + '1', + { + namespace: 'default', + } + ); expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledTimes(2); expect(taskManager.bulkEnable).not.toHaveBeenCalled(); expect(taskManager.schedule).toHaveBeenCalled(); @@ -681,9 +729,14 @@ describe('enable()', () => { expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledTimes(2); expect(taskManager.schedule).toHaveBeenCalled(); expect(taskManager.bulkEnable).not.toHaveBeenCalled(); - expect(unsecuredSavedObjectsClient.update).toHaveBeenNthCalledWith(2, 'alert', '1', { - scheduledTaskId: '1', - }); + expect(unsecuredSavedObjectsClient.update).toHaveBeenNthCalledWith( + 2, + RULE_SAVED_OBJECT_TYPE, + '1', + { + scheduledTaskId: '1', + } + ); }); describe('legacy actions migration for SIEM', () => { @@ -722,7 +775,7 @@ describe('enable()', () => { }); // to mitigate AAD issues, we call create with overwrite=true and actions related props expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledWith( - 'alert', + RULE_SAVED_OBJECT_TYPE, expect.objectContaining({ ...existingDecryptedSiemRule.attributes, actions: ['fake-action-1'], diff --git a/x-pack/plugins/alerting/server/rules_client/tests/find.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/find.test.ts index 3aea832752dfa..a6fa751469625 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/find.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/find.test.ts @@ -26,6 +26,7 @@ import { RegistryRuleType } from '../../rule_type_registry'; import { schema } from '@kbn/config-schema'; import { enabledRule1, enabledRule2, siemRule1, siemRule2 } from './test_helpers'; import { formatLegacyActions } from '../lib'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; jest.mock('../lib/siem_legacy_actions/format_legacy_actions', () => { return { @@ -108,7 +109,7 @@ describe('find()', () => { saved_objects: [ { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: 'myType', schedule: { interval: '10s' }, @@ -221,7 +222,7 @@ describe('find()', () => { saved_objects: [ { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: 'myType', schedule: { interval: '10s' }, @@ -322,7 +323,7 @@ describe('find()', () => { saved_objects: [ { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: 'myType', schedule: { interval: '10s' }, @@ -524,7 +525,7 @@ describe('find()', () => { saved_objects: [ { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: 'myType', schedule: { interval: '10s' }, @@ -555,7 +556,7 @@ describe('find()', () => { }, { id: '2', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: '123', schedule: { interval: '20s' }, @@ -738,7 +739,7 @@ describe('find()', () => { saved_objects: [ { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: 'myType', schedule: { interval: '10s' }, @@ -769,7 +770,7 @@ describe('find()', () => { }, { id: '2', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: '123', schedule: { interval: '20s' }, @@ -854,7 +855,7 @@ describe('find()', () => { saved_objects: [ { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { actions: [], alertTypeId: 'myType', @@ -893,7 +894,7 @@ describe('find()', () => { fields: ['tags', 'alertTypeId', 'consumer'], filter: null, sortField: undefined, - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }); expect(ensureRuleTypeIsAuthorized).toHaveBeenCalledWith('myType', 'myApp', 'rule'); }); @@ -909,7 +910,7 @@ describe('find()', () => { action: 'rule_find', outcome: 'success', }), - kibana: { saved_object: { id: '1', type: 'alert' } }, + kibana: { saved_object: { id: '1', type: RULE_SAVED_OBJECT_TYPE } }, }) ); }); @@ -948,7 +949,7 @@ describe('find()', () => { action: 'rule_find', outcome: 'failure', }), - kibana: { saved_object: { id: '1', type: 'alert' } }, + kibana: { saved_object: { id: '1', type: RULE_SAVED_OBJECT_TYPE } }, error: { code: 'Error', message: 'Unauthorized', diff --git a/x-pack/plugins/alerting/server/rules_client/tests/get.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/get.test.ts index 0d29a3fc402d1..c8eaa3eb319ec 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/get.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/get.test.ts @@ -23,6 +23,7 @@ import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, setGlobalDate } from './lib'; import { RecoveredActionGroup } from '../../../common'; import { formatLegacyActions } from '../lib'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; jest.mock('../lib/siem_legacy_actions/format_legacy_actions', () => { return { @@ -76,7 +77,7 @@ describe('get()', () => { const rulesClient = new RulesClient(rulesClientParams); unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: '123', schedule: { interval: '10s' }, @@ -143,7 +144,7 @@ describe('get()', () => { const rulesClient = new RulesClient(rulesClientParams); unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: '123', schedule: { interval: '10s' }, @@ -224,7 +225,7 @@ describe('get()', () => { const rulesClient = new RulesClient(rulesClientParams); unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: '123', schedule: { interval: '10s' }, @@ -327,7 +328,7 @@ describe('get()', () => { const rulesClient = new RulesClient(rulesClientParams); unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: '123', schedule: { interval: '10s' }, @@ -402,7 +403,7 @@ describe('get()', () => { const rulesClient = new RulesClient(rulesClientParams); unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: '123', schedule: { interval: '10s' }, @@ -455,7 +456,7 @@ describe('get()', () => { const rulesClient = new RulesClient(rulesClientParams); unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: '123', schedule: { interval: '10s' }, @@ -498,7 +499,7 @@ describe('get()', () => { beforeEach(() => { unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: 'myType', consumer: 'myApp', @@ -561,7 +562,7 @@ describe('get()', () => { beforeEach(() => { unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: '123', schedule: { interval: '10s' }, @@ -583,7 +584,7 @@ describe('get()', () => { action: 'rule_get', outcome: 'success', }), - kibana: { saved_object: { id: '1', type: 'alert' } }, + kibana: { saved_object: { id: '1', type: RULE_SAVED_OBJECT_TYPE } }, }) ); }); @@ -602,7 +603,7 @@ describe('get()', () => { kibana: { saved_object: { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }, }, error: { @@ -617,7 +618,7 @@ describe('get()', () => { describe('legacy actions migration for SIEM', () => { const rule = { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: '123', schedule: { interval: '10s' }, diff --git a/x-pack/plugins/alerting/server/rules_client/tests/get_action_error_log.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/get_action_error_log.test.ts index 0fe46a07e4e7f..d6c8f0a0df3ec 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/get_action_error_log.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/get_action_error_log.test.ts @@ -25,6 +25,7 @@ import { SavedObject } from '@kbn/core/server'; import { RawRule } from '../../types'; import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, mockedDateString, setGlobalDate } from './lib'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; const taskManager = taskManagerMock.createStart(); const ruleTypeRegistry = ruleTypeRegistryMock.create(); @@ -74,7 +75,7 @@ const RuleIntervalSeconds = 1; const BaseRuleSavedObject: SavedObject = { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, name: 'rule-name', @@ -139,7 +140,7 @@ const findResults = { }, { rel: 'primary', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', type_id: 'example.always-firing', }, @@ -188,7 +189,7 @@ const findResults = { }, { rel: 'primary', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', type_id: 'example.always-firing', }, @@ -237,7 +238,7 @@ const findResults = { }, { rel: 'primary', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', type_id: 'example.always-firing', }, @@ -286,7 +287,7 @@ const findResults = { }, { rel: 'primary', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', type_id: 'example.always-firing', }, @@ -335,7 +336,7 @@ const findResults = { }, { rel: 'primary', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, id: 'a348a740-9e2c-11ec-bd64-774ed95c43ef', type_id: 'example.always-firing', }, @@ -450,7 +451,7 @@ describe('getActionErrorLog()', () => { expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledTimes(1); expect(eventLogClient.findEventsBySavedObjectIds).toHaveBeenCalledTimes(1); expect(eventLogClient.findEventsBySavedObjectIds.mock.calls[0]).toEqual([ - 'alert', + RULE_SAVED_OBJECT_TYPE, ['1'], { page: 1, @@ -480,7 +481,7 @@ describe('getActionErrorLog()', () => { expect(eventLogClient.findEventsBySavedObjectIds).toHaveBeenCalledTimes(1); expect(eventLogClient.findEventsBySavedObjectIds.mock.calls[0]).toEqual([ - 'alert', + RULE_SAVED_OBJECT_TYPE, ['1'], { page: 3, @@ -554,7 +555,7 @@ describe('getActionErrorLog()', () => { action: 'rule_get_action_error_log', outcome: 'success', }), - kibana: { saved_object: { id: '1', type: 'alert' } }, + kibana: { saved_object: { id: '1', type: RULE_SAVED_OBJECT_TYPE } }, }) ); }); @@ -576,7 +577,7 @@ describe('getActionErrorLog()', () => { kibana: { saved_object: { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }, }, error: { diff --git a/x-pack/plugins/alerting/server/rules_client/tests/get_alert_state.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/get_alert_state.test.ts index dee5cb2ab9a81..671dcc59c886f 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/get_alert_state.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/get_alert_state.test.ts @@ -20,6 +20,7 @@ import { actionsAuthorizationMock } from '@kbn/actions-plugin/server/mocks'; import { AlertingAuthorization } from '../../authorization/alerting_authorization'; import { ActionsAuthorization } from '@kbn/actions-plugin/server'; import { getBeforeSetup } from './lib'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; const taskManager = taskManagerMock.createStart(); const ruleTypeRegistry = ruleTypeRegistryMock.create(); @@ -64,7 +65,7 @@ describe('getAlertState()', () => { const rulesClient = new RulesClient(rulesClientParams); unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: '123', schedule: { interval: '10s' }, @@ -121,7 +122,7 @@ describe('getAlertState()', () => { unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: '123', schedule: { interval: '10s' }, @@ -176,7 +177,7 @@ describe('getAlertState()', () => { beforeEach(() => { unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: 'myType', consumer: 'myApp', diff --git a/x-pack/plugins/alerting/server/rules_client/tests/get_alert_summary.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/get_alert_summary.test.ts index 1521dfdefced3..ba0e2d7a1a485 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/get_alert_summary.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/get_alert_summary.test.ts @@ -25,6 +25,7 @@ import { SavedObject } from '@kbn/core/server'; import { EventsFactory } from '../../lib/alert_summary_from_event_log.test'; import { RawRule } from '../../types'; import { getBeforeSetup, mockedDateString, setGlobalDate } from './lib'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; const taskManager = taskManagerMock.createStart(); const ruleTypeRegistry = ruleTypeRegistryMock.create(); @@ -78,7 +79,7 @@ const RuleIntervalSeconds = 1; const BaseRuleSavedObject: SavedObject = { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, name: 'rule-name', diff --git a/x-pack/plugins/alerting/server/rules_client/tests/get_execution_log.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/get_execution_log.test.ts index f1d2dbcacf8fd..c4419ef8386a5 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/get_execution_log.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/get_execution_log.test.ts @@ -26,6 +26,7 @@ import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, mockedDateString, setGlobalDate } from './lib'; import { getExecutionLogAggregation } from '../../lib/get_execution_log_aggregation'; import { fromKueryExpression } from '@kbn/es-query'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; const taskManager = taskManagerMock.createStart(); const ruleTypeRegistry = ruleTypeRegistryMock.create(); @@ -75,7 +76,7 @@ const RuleIntervalSeconds = 1; const BaseRuleSavedObject: SavedObject = { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, name: 'rule-name', @@ -444,7 +445,7 @@ describe('getExecutionLogForRule()', () => { expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledTimes(1); expect(eventLogClient.aggregateEventsBySavedObjectIds).toHaveBeenCalledTimes(1); expect(eventLogClient.aggregateEventsBySavedObjectIds.mock.calls[0]).toEqual([ - 'alert', + RULE_SAVED_OBJECT_TYPE, ['1'], { aggs: getExecutionLogAggregation({ @@ -470,7 +471,7 @@ describe('getExecutionLogForRule()', () => { expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledTimes(1); expect(eventLogClient.aggregateEventsBySavedObjectIds).toHaveBeenCalledTimes(1); expect(eventLogClient.aggregateEventsBySavedObjectIds.mock.calls[0]).toEqual([ - 'alert', + RULE_SAVED_OBJECT_TYPE, ['1'], { aggs: getExecutionLogAggregation({ @@ -496,7 +497,7 @@ describe('getExecutionLogForRule()', () => { expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledTimes(1); expect(eventLogClient.aggregateEventsBySavedObjectIds).toHaveBeenCalledTimes(1); expect(eventLogClient.aggregateEventsBySavedObjectIds.mock.calls[0]).toEqual([ - 'alert', + RULE_SAVED_OBJECT_TYPE, ['1'], { aggs: getExecutionLogAggregation({ @@ -522,7 +523,7 @@ describe('getExecutionLogForRule()', () => { expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledTimes(1); expect(eventLogClient.aggregateEventsBySavedObjectIds).toHaveBeenCalledTimes(1); expect(eventLogClient.aggregateEventsBySavedObjectIds.mock.calls[0]).toEqual([ - 'alert', + RULE_SAVED_OBJECT_TYPE, ['1'], { aggs: getExecutionLogAggregation({ @@ -662,7 +663,7 @@ describe('getExecutionLogForRule()', () => { action: 'rule_get_execution_log', outcome: 'success', }), - kibana: { saved_object: { id: '1', type: 'alert' } }, + kibana: { saved_object: { id: '1', type: RULE_SAVED_OBJECT_TYPE } }, }) ); }); @@ -684,7 +685,7 @@ describe('getExecutionLogForRule()', () => { kibana: { saved_object: { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }, }, error: { diff --git a/x-pack/plugins/alerting/server/rules_client/tests/lib.ts b/x-pack/plugins/alerting/server/rules_client/tests/lib.ts index 12da7211fdc12..20c0759e1d628 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/lib.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/lib.ts @@ -13,6 +13,7 @@ import { TaskStatus } from '@kbn/task-manager-plugin/server'; import { ConstructorOptions } from '../rules_client'; import { RuleTypeRegistry } from '../../rule_type_registry'; import { RecoveredActionGroup } from '../../../common'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; export const mockedDateString = '2019-02-12T21:01:22.479Z'; @@ -69,7 +70,7 @@ export function getBeforeSetup( enabled: false, }); taskManager.bulkRemove.mockResolvedValue({ - statuses: [{ id: 'taskId', type: 'alert', success: true }], + statuses: [{ id: 'taskId', type: RULE_SAVED_OBJECT_TYPE, success: true }], }); const actionsClient = actionsClientMock.create(); diff --git a/x-pack/plugins/alerting/server/rules_client/tests/mute_all.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/mute_all.test.ts index 2b6e2793aa648..cf585cc67a417 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/mute_all.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/mute_all.test.ts @@ -20,6 +20,7 @@ import { AlertingAuthorization } from '../../authorization/alerting_authorizatio import { ActionsAuthorization } from '@kbn/actions-plugin/server'; import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, setGlobalDate } from './lib'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; const taskManager = taskManagerMock.createStart(); const ruleTypeRegistry = ruleTypeRegistryMock.create(); @@ -67,7 +68,7 @@ describe('muteAll()', () => { const rulesClient = new RulesClient(rulesClientParams); unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { actions: [ { @@ -88,7 +89,7 @@ describe('muteAll()', () => { await rulesClient.muteAll({ id: '1' }); expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( - 'alert', + RULE_SAVED_OBJECT_TYPE, '1', { muteAll: true, @@ -107,7 +108,7 @@ describe('muteAll()', () => { beforeEach(() => { unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { actions: [ { @@ -171,7 +172,7 @@ describe('muteAll()', () => { const rulesClient = new RulesClient({ ...rulesClientParams, auditLogger }); unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { actions: [ { @@ -196,7 +197,7 @@ describe('muteAll()', () => { action: 'rule_mute', outcome: 'unknown', }), - kibana: { saved_object: { id: '1', type: 'alert' } }, + kibana: { saved_object: { id: '1', type: RULE_SAVED_OBJECT_TYPE } }, }) ); }); @@ -205,7 +206,7 @@ describe('muteAll()', () => { const rulesClient = new RulesClient({ ...rulesClientParams, auditLogger }); unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { actions: [ { @@ -235,7 +236,7 @@ describe('muteAll()', () => { kibana: { saved_object: { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }, }, error: { diff --git a/x-pack/plugins/alerting/server/rules_client/tests/mute_instance.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/mute_instance.test.ts index 4d7f1a52699cc..3aae21df3133d 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/mute_instance.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/mute_instance.test.ts @@ -20,6 +20,7 @@ import { AlertingAuthorization } from '../../authorization/alerting_authorizatio import { ActionsAuthorization } from '@kbn/actions-plugin/server'; import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, setGlobalDate } from './lib'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; const taskManager = taskManagerMock.createStart(); const ruleTypeRegistry = ruleTypeRegistryMock.create(); @@ -67,7 +68,7 @@ describe('muteInstance()', () => { const rulesClient = new RulesClient(rulesClientParams); unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { actions: [], schedule: { interval: '10s' }, @@ -82,7 +83,7 @@ describe('muteInstance()', () => { await rulesClient.muteInstance({ alertId: '1', alertInstanceId: '2' }); expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( - 'alert', + RULE_SAVED_OBJECT_TYPE, '1', { mutedInstanceIds: ['2'], @@ -99,7 +100,7 @@ describe('muteInstance()', () => { const rulesClient = new RulesClient(rulesClientParams); unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { actions: [], schedule: { interval: '10s' }, @@ -119,7 +120,7 @@ describe('muteInstance()', () => { const rulesClient = new RulesClient(rulesClientParams); unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { actions: [], schedule: { interval: '10s' }, @@ -140,7 +141,7 @@ describe('muteInstance()', () => { beforeEach(() => { unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { actions: [ { @@ -204,7 +205,7 @@ describe('muteInstance()', () => { const rulesClient = new RulesClient({ ...rulesClientParams, auditLogger }); unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { actions: [], schedule: { interval: '10s' }, @@ -223,7 +224,7 @@ describe('muteInstance()', () => { action: 'rule_alert_mute', outcome: 'unknown', }), - kibana: { saved_object: { id: '1', type: 'alert' } }, + kibana: { saved_object: { id: '1', type: RULE_SAVED_OBJECT_TYPE } }, }) ); }); @@ -232,7 +233,7 @@ describe('muteInstance()', () => { const rulesClient = new RulesClient({ ...rulesClientParams, auditLogger }); unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { actions: [], schedule: { interval: '10s' }, @@ -258,7 +259,7 @@ describe('muteInstance()', () => { kibana: { saved_object: { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }, }, error: { diff --git a/x-pack/plugins/alerting/server/rules_client/tests/resolve.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/resolve.test.ts index 70c6388c41ff4..13ab4778f82bc 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/resolve.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/resolve.test.ts @@ -23,6 +23,7 @@ import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, setGlobalDate } from './lib'; import { RecoveredActionGroup } from '../../../common'; import { formatLegacyActions } from '../lib'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; jest.mock('../lib/siem_legacy_actions/format_legacy_actions', () => { return { @@ -77,7 +78,7 @@ describe('resolve()', () => { unsecuredSavedObjectsClient.resolve.mockResolvedValueOnce({ saved_object: { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: '123', schedule: { interval: '10s' }, @@ -160,7 +161,7 @@ describe('resolve()', () => { unsecuredSavedObjectsClient.resolve.mockResolvedValueOnce({ saved_object: { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { legacyId: 'some-legacy-id', alertTypeId: '123', @@ -243,7 +244,7 @@ describe('resolve()', () => { unsecuredSavedObjectsClient.resolve.mockResolvedValueOnce({ saved_object: { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: '123', schedule: { interval: '10s' }, @@ -333,7 +334,7 @@ describe('resolve()', () => { unsecuredSavedObjectsClient.resolve.mockResolvedValueOnce({ saved_object: { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: '123', schedule: { interval: '10s' }, @@ -395,7 +396,7 @@ describe('resolve()', () => { unsecuredSavedObjectsClient.resolve.mockResolvedValueOnce({ saved_object: { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: '123', schedule: { interval: '10s' }, @@ -442,7 +443,7 @@ describe('resolve()', () => { unsecuredSavedObjectsClient.resolve.mockResolvedValueOnce({ saved_object: { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: 'myType', consumer: 'myApp', @@ -514,7 +515,7 @@ describe('resolve()', () => { unsecuredSavedObjectsClient.resolve.mockResolvedValueOnce({ saved_object: { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: '123', schedule: { interval: '10s' }, @@ -544,7 +545,7 @@ describe('resolve()', () => { action: 'rule_resolve', outcome: 'success', }), - kibana: { saved_object: { id: '1', type: 'alert' } }, + kibana: { saved_object: { id: '1', type: RULE_SAVED_OBJECT_TYPE } }, }) ); }); @@ -563,7 +564,7 @@ describe('resolve()', () => { kibana: { saved_object: { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }, }, error: { @@ -578,7 +579,7 @@ describe('resolve()', () => { describe('legacy actions migration for SIEM', () => { const rule = { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: '123', schedule: { interval: '10s' }, diff --git a/x-pack/plugins/alerting/server/rules_client/tests/run_soon.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/run_soon.test.ts index 9040096eba8b0..b156838704e0b 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/run_soon.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/run_soon.test.ts @@ -21,6 +21,7 @@ import { ActionsAuthorization } from '@kbn/actions-plugin/server'; import { TaskStatus } from '@kbn/task-manager-plugin/server'; import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, setGlobalDate } from './lib'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; const taskManager = taskManagerMock.createStart(); const ruleTypeRegistry = ruleTypeRegistryMock.create(); @@ -64,7 +65,7 @@ describe('runSoon()', () => { const existingRule = { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { name: 'name', consumer: 'myApp', @@ -154,7 +155,7 @@ describe('runSoon()', () => { action: 'rule_run_soon', outcome: 'unknown', }), - kibana: { saved_object: { id: '1', type: 'alert' } }, + kibana: { saved_object: { id: '1', type: RULE_SAVED_OBJECT_TYPE } }, }) ); }); @@ -172,7 +173,7 @@ describe('runSoon()', () => { kibana: { saved_object: { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }, }, error: { diff --git a/x-pack/plugins/alerting/server/rules_client/tests/test_helpers.ts b/x-pack/plugins/alerting/server/rules_client/tests/test_helpers.ts index 8b2db34686577..f060187775a8e 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/test_helpers.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/test_helpers.ts @@ -7,6 +7,7 @@ import { AlertConsumers } from '@kbn/rule-data-utils'; import type { SavedObject } from '@kbn/core-saved-objects-server'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; export const savedObjectWith500Error = { id: 'id2', @@ -30,7 +31,7 @@ export const savedObjectWith409Error = { export const defaultRule = { id: 'id1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { name: 'fakeName', consumer: 'fakeConsumer', @@ -44,7 +45,7 @@ export const defaultRule = { export const defaultRuleForBulkDelete = { id: 'id1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { tags: ['ups'], params: { param: 1 }, diff --git a/x-pack/plugins/alerting/server/rules_client/tests/unmute_all.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/unmute_all.test.ts index c8cb134b129d7..f8329f20fc432 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/unmute_all.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/unmute_all.test.ts @@ -20,6 +20,7 @@ import { AlertingAuthorization } from '../../authorization/alerting_authorizatio import { ActionsAuthorization } from '@kbn/actions-plugin/server'; import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, setGlobalDate } from './lib'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; const taskManager = taskManagerMock.createStart(); const ruleTypeRegistry = ruleTypeRegistryMock.create(); @@ -67,7 +68,7 @@ describe('unmuteAll()', () => { const rulesClient = new RulesClient(rulesClientParams); unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { actions: [ { @@ -88,7 +89,7 @@ describe('unmuteAll()', () => { await rulesClient.unmuteAll({ id: '1' }); expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( - 'alert', + RULE_SAVED_OBJECT_TYPE, '1', { muteAll: false, @@ -107,7 +108,7 @@ describe('unmuteAll()', () => { beforeEach(() => { unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { actions: [ { @@ -171,7 +172,7 @@ describe('unmuteAll()', () => { const rulesClient = new RulesClient({ ...rulesClientParams, auditLogger }); unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { actions: [ { @@ -196,7 +197,7 @@ describe('unmuteAll()', () => { action: 'rule_unmute', outcome: 'unknown', }), - kibana: { saved_object: { id: '1', type: 'alert' } }, + kibana: { saved_object: { id: '1', type: RULE_SAVED_OBJECT_TYPE } }, }) ); }); @@ -205,7 +206,7 @@ describe('unmuteAll()', () => { const rulesClient = new RulesClient({ ...rulesClientParams, auditLogger }); unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { actions: [ { @@ -235,7 +236,7 @@ describe('unmuteAll()', () => { kibana: { saved_object: { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }, }, error: { diff --git a/x-pack/plugins/alerting/server/rules_client/tests/unmute_instance.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/unmute_instance.test.ts index 4df037b8728f0..8d9232c4381c3 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/unmute_instance.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/unmute_instance.test.ts @@ -20,6 +20,7 @@ import { AlertingAuthorization } from '../../authorization/alerting_authorizatio import { ActionsAuthorization } from '@kbn/actions-plugin/server'; import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, setGlobalDate } from './lib'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; const taskManager = taskManagerMock.createStart(); const ruleTypeRegistry = ruleTypeRegistryMock.create(); @@ -67,7 +68,7 @@ describe('unmuteInstance()', () => { const rulesClient = new RulesClient(rulesClientParams); unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { actions: [], schedule: { interval: '10s' }, @@ -82,7 +83,7 @@ describe('unmuteInstance()', () => { await rulesClient.unmuteInstance({ alertId: '1', alertInstanceId: '2' }); expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( - 'alert', + RULE_SAVED_OBJECT_TYPE, '1', { mutedInstanceIds: [], @@ -97,7 +98,7 @@ describe('unmuteInstance()', () => { const rulesClient = new RulesClient(rulesClientParams); unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { actions: [], schedule: { interval: '10s' }, @@ -117,7 +118,7 @@ describe('unmuteInstance()', () => { const rulesClient = new RulesClient(rulesClientParams); unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { actions: [], schedule: { interval: '10s' }, @@ -138,7 +139,7 @@ describe('unmuteInstance()', () => { beforeEach(() => { unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { actions: [ { @@ -202,7 +203,7 @@ describe('unmuteInstance()', () => { const rulesClient = new RulesClient({ ...rulesClientParams, auditLogger }); unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { actions: [], schedule: { interval: '10s' }, @@ -221,7 +222,7 @@ describe('unmuteInstance()', () => { action: 'rule_alert_unmute', outcome: 'unknown', }), - kibana: { saved_object: { id: '1', type: 'alert' } }, + kibana: { saved_object: { id: '1', type: RULE_SAVED_OBJECT_TYPE } }, }) ); }); @@ -230,7 +231,7 @@ describe('unmuteInstance()', () => { const rulesClient = new RulesClient({ ...rulesClientParams, auditLogger }); unsecuredSavedObjectsClient.get.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { actions: [], schedule: { interval: '10s' }, @@ -256,7 +257,7 @@ describe('unmuteInstance()', () => { kibana: { saved_object: { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }, }, error: { diff --git a/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts index c151e4f75e99e..aa03a71d93e14 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/update.test.ts @@ -28,6 +28,7 @@ import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, setGlobalDate } from './lib'; import { bulkMarkApiKeysForInvalidation } from '../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation'; import { migrateLegacyActions } from '../lib'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; jest.mock('../lib/siem_legacy_actions/migrate_legacy_actions', () => { return { @@ -106,7 +107,7 @@ describe('update()', () => { let actionsClient: jest.Mocked; const existingAlert = { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, tags: ['foo'], @@ -238,7 +239,7 @@ describe('update()', () => { ]); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, schedule: { interval: '1m' }, @@ -376,14 +377,18 @@ describe('update()', () => { "updatedAt": 2019-02-12T21:01:22.479Z, } `); - expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { - namespace: 'default', - }); + expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith( + RULE_SAVED_OBJECT_TYPE, + '1', + { + namespace: 'default', + } + ); expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledTimes(1); expect(bulkMarkApiKeysForInvalidationMock).toHaveBeenCalledTimes(1); expect(unsecuredSavedObjectsClient.create.mock.calls[0]).toHaveLength(3); - expect(unsecuredSavedObjectsClient.create.mock.calls[0][0]).toEqual('alert'); + expect(unsecuredSavedObjectsClient.create.mock.calls[0][0]).toEqual(RULE_SAVED_OBJECT_TYPE); expect(unsecuredSavedObjectsClient.create.mock.calls[0][1]).toMatchInlineSnapshot(` Object { "actions": Array [ @@ -537,7 +542,7 @@ describe('update()', () => { actionsClient.isPreconfigured.mockReturnValueOnce(true); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, schedule: { interval: '1m' }, @@ -628,7 +633,7 @@ describe('update()', () => { expect(unsecuredSavedObjectsClient.create).toHaveBeenNthCalledWith( 1, - 'alert', + RULE_SAVED_OBJECT_TYPE, { actions: [ { @@ -728,9 +733,13 @@ describe('update()', () => { "updatedAt": 2019-02-12T21:01:22.479Z, } `); - expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { - namespace: 'default', - }); + expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith( + RULE_SAVED_OBJECT_TYPE, + '1', + { + namespace: 'default', + } + ); expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); expect(actionsClient.isPreconfigured).toHaveBeenCalledTimes(3); }); @@ -789,7 +798,7 @@ describe('update()', () => { actionsClient.isSystemAction.mockReturnValueOnce(true); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, schedule: { interval: '1m' }, @@ -872,7 +881,7 @@ describe('update()', () => { expect(unsecuredSavedObjectsClient.create).toHaveBeenNthCalledWith( 1, - 'alert', + RULE_SAVED_OBJECT_TYPE, { actions: [ { @@ -964,9 +973,13 @@ describe('update()', () => { "updatedAt": 2019-02-12T21:01:22.479Z, } `); - expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { - namespace: 'default', - }); + expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith( + RULE_SAVED_OBJECT_TYPE, + '1', + { + namespace: 'default', + } + ); expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); expect(actionsClient.isSystemAction).toHaveBeenCalledTimes(3); }); @@ -1017,7 +1030,7 @@ describe('update()', () => { })); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, schedule: { interval: '1m' }, @@ -1077,7 +1090,7 @@ describe('update()', () => { expect(extractReferencesFn).toHaveBeenCalledWith(ruleParams); expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledWith( - 'alert', + RULE_SAVED_OBJECT_TYPE, { actions: [ { @@ -1161,7 +1174,7 @@ describe('update()', () => { }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, schedule: { interval: '1m' }, @@ -1254,7 +1267,7 @@ describe('update()', () => { expect.any(Object) ); expect(unsecuredSavedObjectsClient.create.mock.calls[0]).toHaveLength(3); - expect(unsecuredSavedObjectsClient.create.mock.calls[0][0]).toEqual('alert'); + expect(unsecuredSavedObjectsClient.create.mock.calls[0][0]).toEqual(RULE_SAVED_OBJECT_TYPE); expect(unsecuredSavedObjectsClient.create.mock.calls[0][1]).toMatchInlineSnapshot(` Object { "actions": Array [ @@ -1321,7 +1334,7 @@ describe('update()', () => { }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: false, schedule: { interval: '1m' }, @@ -1407,7 +1420,7 @@ describe('update()', () => { `); expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledTimes(1); expect(unsecuredSavedObjectsClient.create.mock.calls[0]).toHaveLength(3); - expect(unsecuredSavedObjectsClient.create.mock.calls[0][0]).toEqual('alert'); + expect(unsecuredSavedObjectsClient.create.mock.calls[0][0]).toEqual(RULE_SAVED_OBJECT_TYPE); expect(unsecuredSavedObjectsClient.create.mock.calls[0][1]).toMatchInlineSnapshot(` Object { "actions": Array [ @@ -1563,7 +1576,7 @@ describe('update()', () => { it('should trim alert name in the API key name', async () => { unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: false, name: ' my alert name ', @@ -1613,7 +1626,7 @@ describe('update()', () => { it('swallows error when invalidate API key throws', async () => { unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, schedule: { interval: '1m' }, @@ -1721,7 +1734,7 @@ describe('update()', () => { ]); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, schedule: { interval: '1m' }, @@ -1840,7 +1853,7 @@ describe('update()', () => { ], }, }); - expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledWith('alert', '1'); + expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledWith(RULE_SAVED_OBJECT_TYPE, '1'); expect(rulesClientParams.logger.error).toHaveBeenCalledWith( 'update(): Failed to load API key to invalidate on alert 1: Fail' ); @@ -1920,7 +1933,7 @@ describe('update()', () => { }); encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValueOnce({ id: alertId, - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { actions: [], enabled: true, @@ -1947,7 +1960,7 @@ describe('update()', () => { }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: alertId, - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, schedule: updatedSchedule, @@ -2374,7 +2387,7 @@ describe('update()', () => { actionsClient.isPreconfigured.mockReturnValueOnce(true); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, schedule: { interval: '1m' }, @@ -2436,7 +2449,7 @@ describe('update()', () => { expect(unsecuredSavedObjectsClient.create).toHaveBeenNthCalledWith( 1, - 'alert', + RULE_SAVED_OBJECT_TYPE, { actions: [ { @@ -2502,9 +2515,13 @@ describe('update()', () => { "updatedAt": 2019-02-12T21:01:22.479Z, } `); - expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { - namespace: 'default', - }); + expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith( + RULE_SAVED_OBJECT_TYPE, + '1', + { + namespace: 'default', + } + ); expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); expect(actionsClient.isPreconfigured).toHaveBeenCalledTimes(1); }); @@ -2549,7 +2566,7 @@ describe('update()', () => { ]); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, schedule: { interval: '1m' }, @@ -2700,7 +2717,7 @@ describe('update()', () => { beforeEach(() => { unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { alertTypeId: 'myType', consumer: 'myApp', @@ -2779,7 +2796,7 @@ describe('update()', () => { beforeEach(() => { unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, schedule: { interval: '1m' }, @@ -2817,7 +2834,7 @@ describe('update()', () => { action: 'rule_update', outcome: 'unknown', }), - kibana: { saved_object: { id: '1', type: 'alert' } }, + kibana: { saved_object: { id: '1', type: RULE_SAVED_OBJECT_TYPE } }, }) ); }); @@ -2850,7 +2867,7 @@ describe('update()', () => { kibana: { saved_object: { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }, }, error: { @@ -2885,7 +2902,7 @@ describe('update()', () => { ]); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, schedule: { interval: '1m' }, @@ -2980,7 +2997,7 @@ describe('update()', () => { }, }); expect(unsecuredSavedObjectsClient.create).toHaveBeenCalledWith( - 'alert', + RULE_SAVED_OBJECT_TYPE, { actions: [ { @@ -3031,7 +3048,7 @@ describe('update()', () => { beforeEach(() => { unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, schedule: { interval: '1m' }, @@ -3093,7 +3110,7 @@ describe('update()', () => { }); unsecuredSavedObjectsClient.create.mockResolvedValueOnce({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, schedule: { interval: '1m' }, @@ -3190,7 +3207,7 @@ describe('update()', () => { expect(bulkMarkApiKeysForInvalidationMock).not.toHaveBeenCalled(); expect(unsecuredSavedObjectsClient.create.mock.calls[0]).toHaveLength(3); - expect(unsecuredSavedObjectsClient.create.mock.calls[0][0]).toEqual('alert'); + expect(unsecuredSavedObjectsClient.create.mock.calls[0][0]).toEqual(RULE_SAVED_OBJECT_TYPE); expect(unsecuredSavedObjectsClient.create.mock.calls[0][1]).toMatchInlineSnapshot(` Object { "actions": Array [ diff --git a/x-pack/plugins/alerting/server/rules_client/tests/update_api_key.test.ts b/x-pack/plugins/alerting/server/rules_client/tests/update_api_key.test.ts index f9cd570afa430..de54ab9f8d5f1 100644 --- a/x-pack/plugins/alerting/server/rules_client/tests/update_api_key.test.ts +++ b/x-pack/plugins/alerting/server/rules_client/tests/update_api_key.test.ts @@ -21,6 +21,7 @@ import { ActionsAuthorization } from '@kbn/actions-plugin/server'; import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks'; import { getBeforeSetup, setGlobalDate } from './lib'; import { bulkMarkApiKeysForInvalidation } from '../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation'; +import { RULE_SAVED_OBJECT_TYPE } from '../../saved_objects'; jest.mock('../../invalidate_pending_api_keys/bulk_mark_api_keys_for_invalidation', () => ({ bulkMarkApiKeysForInvalidation: jest.fn(), @@ -73,7 +74,7 @@ describe('updateApiKey()', () => { let rulesClient: RulesClient; const existingAlert = { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { revision: 0, schedule: { interval: '10s' }, @@ -117,11 +118,15 @@ describe('updateApiKey()', () => { }); await rulesClient.updateApiKey({ id: '1' }); expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); - expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { - namespace: 'default', - }); + expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith( + RULE_SAVED_OBJECT_TYPE, + '1', + { + namespace: 'default', + } + ); expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( - 'alert', + RULE_SAVED_OBJECT_TYPE, '1', { schedule: { interval: '10s' }, @@ -174,11 +179,15 @@ describe('updateApiKey()', () => { }); await rulesClient.updateApiKey({ id: '1' }); expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); - expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { - namespace: 'default', - }); + expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith( + RULE_SAVED_OBJECT_TYPE, + '1', + { + namespace: 'default', + } + ); expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( - 'alert', + RULE_SAVED_OBJECT_TYPE, '1', { schedule: { interval: '10s' }, @@ -226,11 +235,15 @@ describe('updateApiKey()', () => { }); await rulesClient.updateApiKey({ id: '1' }); expect(unsecuredSavedObjectsClient.get).not.toHaveBeenCalled(); - expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { - namespace: 'default', - }); + expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith( + RULE_SAVED_OBJECT_TYPE, + '1', + { + namespace: 'default', + } + ); expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( - 'alert', + RULE_SAVED_OBJECT_TYPE, '1', { schedule: { interval: '10s' }, @@ -282,12 +295,16 @@ describe('updateApiKey()', () => { encryptedSavedObjects.getDecryptedAsInternalUser.mockRejectedValueOnce(new Error('Fail')); await rulesClient.updateApiKey({ id: '1' }); - expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledWith('alert', '1'); - expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith('alert', '1', { - namespace: 'default', - }); + expect(unsecuredSavedObjectsClient.get).toHaveBeenCalledWith(RULE_SAVED_OBJECT_TYPE, '1'); + expect(encryptedSavedObjects.getDecryptedAsInternalUser).toHaveBeenCalledWith( + RULE_SAVED_OBJECT_TYPE, + '1', + { + namespace: 'default', + } + ); expect(unsecuredSavedObjectsClient.update).toHaveBeenCalledWith( - 'alert', + RULE_SAVED_OBJECT_TYPE, '1', { schedule: { interval: '10s' }, @@ -401,7 +418,7 @@ describe('updateApiKey()', () => { action: 'rule_update_api_key', outcome: 'unknown', }), - kibana: { saved_object: { id: '1', type: 'alert' } }, + kibana: { saved_object: { id: '1', type: RULE_SAVED_OBJECT_TYPE } }, }) ); }); @@ -419,7 +436,7 @@ describe('updateApiKey()', () => { kibana: { saved_object: { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }, }, error: { diff --git a/x-pack/plugins/alerting/server/rules_client_conflict_retries.test.ts b/x-pack/plugins/alerting/server/rules_client_conflict_retries.test.ts index d36c4a72d2bd2..821a04601c62d 100644 --- a/x-pack/plugins/alerting/server/rules_client_conflict_retries.test.ts +++ b/x-pack/plugins/alerting/server/rules_client_conflict_retries.test.ts @@ -24,6 +24,7 @@ import { SavedObjectsErrorHelpers } from '@kbn/core/server'; import { RetryForConflictsAttempts } from './lib/retry_if_conflicts'; import { TaskStatus } from '@kbn/task-manager-plugin/server/task'; import { RecoveredActionGroup } from '../common'; +import { RULE_SAVED_OBJECT_TYPE } from './saved_objects'; jest.mock('./application/rule/methods/get_schedule_frequency', () => ({ validateScheduleLimit: jest.fn(), @@ -227,7 +228,7 @@ function expectSuccess( // tests to run when the method is expected to fail function expectConflict(success: boolean, err: Error, method: 'update' | 'create' = 'update') { const conflictErrorMessage = SavedObjectsErrorHelpers.createConflictError( - 'alert', + RULE_SAVED_OBJECT_TYPE, MockAlertId ).message; @@ -249,7 +250,7 @@ function mockSavedObjectUpdateConflictErrorTimes(times: number) { // default success value const mockUpdateValue = { id: MockAlertId, - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { actions: [], scheduledTaskId: 'scheduled-task-id', @@ -263,10 +264,10 @@ function mockSavedObjectUpdateConflictErrorTimes(times: number) { // queue up specified number of errors before a success call for (let i = 0; i < times; i++) { unsecuredSavedObjectsClient.update.mockRejectedValueOnce( - SavedObjectsErrorHelpers.createConflictError('alert', MockAlertId) + SavedObjectsErrorHelpers.createConflictError(RULE_SAVED_OBJECT_TYPE, MockAlertId) ); unsecuredSavedObjectsClient.create.mockRejectedValueOnce( - SavedObjectsErrorHelpers.createConflictError('alert', MockAlertId) + SavedObjectsErrorHelpers.createConflictError(RULE_SAVED_OBJECT_TYPE, MockAlertId) ); } } @@ -276,9 +277,9 @@ function setupRawAlertMocks( overrides: Record = {}, attributeOverrides: Record = {} ) { - const rawAlert = { + const rawRule = { id: MockAlertId, - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, tags: ['foo'], @@ -297,10 +298,10 @@ function setupRawAlertMocks( version: '123', ...overrides, }; - const decryptedRawAlert = { - ...rawAlert, + const decryptedRawRule = { + ...rawRule, attributes: { - ...rawAlert.attributes, + ...rawRule.attributes, apiKey: Buffer.from('123:abc').toString('base64'), }, }; @@ -310,11 +311,11 @@ function setupRawAlertMocks( // splitting this out as it's easier to set a breakpoint :-) unsecuredSavedObjectsClient.get.mockImplementation(async () => { - return cloneDeep(rawAlert); + return cloneDeep(rawRule); }); encryptedSavedObjects.getDecryptedAsInternalUser.mockImplementation(async () => { - return cloneDeep(decryptedRawAlert); + return cloneDeep(decryptedRawRule); }); } diff --git a/x-pack/plugins/alerting/server/rules_client_factory.test.ts b/x-pack/plugins/alerting/server/rules_client_factory.test.ts index 0532a48be01e1..76f4943172a4b 100644 --- a/x-pack/plugins/alerting/server/rules_client_factory.test.ts +++ b/x-pack/plugins/alerting/server/rules_client_factory.test.ts @@ -26,6 +26,7 @@ import { AlertingAuthorization } from './authorization'; import { AlertingAuthorizationClientFactory } from './alerting_authorization_client_factory'; import { SECURITY_EXTENSION_ID } from '@kbn/core-saved-objects-server'; import { mockRouter } from '@kbn/core-http-router-server-mocks'; +import { RULE_SAVED_OBJECT_TYPE } from './saved_objects'; jest.mock('./rules_client'); jest.mock('./authorization/alerting_authorization'); @@ -85,7 +86,7 @@ test('creates a rules client with proper constructor arguments when security is expect(savedObjectsService.getScopedClient).toHaveBeenCalledWith(request, { excludedExtensions: [SECURITY_EXTENSION_ID], - includedHiddenTypes: ['alert', 'api_key_pending_invalidation'], + includedHiddenTypes: [RULE_SAVED_OBJECT_TYPE, 'api_key_pending_invalidation'], }); expect(alertingAuthorizationClientFactory.create).toHaveBeenCalledWith(request); @@ -133,7 +134,7 @@ test('creates a rules client with proper constructor arguments', async () => { expect(savedObjectsService.getScopedClient).toHaveBeenCalledWith(request, { excludedExtensions: [SECURITY_EXTENSION_ID], - includedHiddenTypes: ['alert', 'api_key_pending_invalidation'], + includedHiddenTypes: [RULE_SAVED_OBJECT_TYPE, 'api_key_pending_invalidation'], }); expect(alertingAuthorizationClientFactory.create).toHaveBeenCalledWith(request); diff --git a/x-pack/plugins/alerting/server/rules_client_factory.ts b/x-pack/plugins/alerting/server/rules_client_factory.ts index 6f2930429256e..6b637eed5cd1f 100644 --- a/x-pack/plugins/alerting/server/rules_client_factory.ts +++ b/x-pack/plugins/alerting/server/rules_client_factory.ts @@ -28,6 +28,7 @@ import { AlertingAuthorizationClientFactory } from './alerting_authorization_cli import { AlertingRulesConfig } from './config'; import { GetAlertIndicesAlias } from './lib'; import { AlertsService } from './alerts_service/alerts_service'; +import { RULE_SAVED_OBJECT_TYPE } from './saved_objects'; export interface RulesClientFactoryOpts { logger: Logger; taskManager: TaskManagerStartContract; @@ -113,7 +114,7 @@ export class RulesClientFactory { maxScheduledPerMinute: this.maxScheduledPerMinute, unsecuredSavedObjectsClient: savedObjects.getScopedClient(request, { excludedExtensions: [SECURITY_EXTENSION_ID], - includedHiddenTypes: ['alert', 'api_key_pending_invalidation'], + includedHiddenTypes: [RULE_SAVED_OBJECT_TYPE, 'api_key_pending_invalidation'], }), authorization: this.authorization.create(request), actionsAuthorization: actions.getActionsAuthorizationWithRequest(request), diff --git a/x-pack/plugins/alerting/server/saved_objects/get_import_warnings.test.ts b/x-pack/plugins/alerting/server/saved_objects/get_import_warnings.test.ts index 1eff02d732231..85c35693b2b6a 100644 --- a/x-pack/plugins/alerting/server/saved_objects/get_import_warnings.test.ts +++ b/x-pack/plugins/alerting/server/saved_objects/get_import_warnings.test.ts @@ -6,6 +6,7 @@ */ import { SavedObject } from '@kbn/core/server'; +import { RULE_SAVED_OBJECT_TYPE } from '.'; import { RawRule } from '../types'; import { getImportWarnings } from './get_import_warnings'; @@ -14,7 +15,7 @@ describe('getImportWarnings', () => { const savedObjectRules = [ { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, name: 'rule-name1', @@ -43,7 +44,7 @@ describe('getImportWarnings', () => { }, { id: '2', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, name: 'rule-name2', diff --git a/x-pack/plugins/alerting/server/saved_objects/index.ts b/x-pack/plugins/alerting/server/saved_objects/index.ts index 9006bf1cab1b6..385a5dd25d6bf 100644 --- a/x-pack/plugins/alerting/server/saved_objects/index.ts +++ b/x-pack/plugins/alerting/server/saved_objects/index.ts @@ -23,7 +23,7 @@ import { RawRule } from '../types'; import { getImportWarnings } from './get_import_warnings'; import { isRuleExportable } from './is_rule_exportable'; import { RuleTypeRegistry } from '../rule_type_registry'; -export { partiallyUpdateAlert } from './partially_update_alert'; +export { partiallyUpdateRule } from './partially_update_rule'; export { getLatestRuleVersion, getMinimumCompatibleVersion } from './rule_model_versions'; import { RULES_SETTINGS_SAVED_OBJECT_TYPE, @@ -31,10 +31,12 @@ import { } from '../../common'; import { ruleModelVersions } from './rule_model_versions'; +export const RULE_SAVED_OBJECT_TYPE = 'alert'; + // Use caution when removing items from this array! Any field which has // ever existed in the rule SO must be included in this array to prevent // decryption failures during migration. -export const AlertAttributesExcludedFromAAD = [ +export const RuleAttributesExcludedFromAAD = [ 'scheduledTaskId', 'muteAll', 'mutedInstanceIds', @@ -51,11 +53,11 @@ export const AlertAttributesExcludedFromAAD = [ 'running', ]; -// useful for Pick which is a +// useful for Pick which is a // type which is a subset of RawAlert with just attributes excluded from AAD -// useful for Pick -export type AlertAttributesExcludedFromAADType = +// useful for Pick +export type RuleAttributesExcludedFromAADType = | 'scheduledTaskId' | 'muteAll' | 'mutedInstanceIds' @@ -80,7 +82,7 @@ export function setupSavedObjects( getSearchSourceMigrations: () => MigrateFunctionsObject ) { savedObjects.registerType({ - name: 'alert', + name: RULE_SAVED_OBJECT_TYPE, indexPattern: ALERTING_CASES_SAVED_OBJECT_INDEX, hidden: true, namespaceType: 'multiple-isolated', @@ -146,9 +148,9 @@ export function setupSavedObjects( // Encrypted attributes encryptedSavedObjects.registerType({ - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributesToEncrypt: new Set(['apiKey']), - attributesToExcludeFromAAD: new Set(AlertAttributesExcludedFromAAD), + attributesToExcludeFromAAD: new Set(RuleAttributesExcludedFromAAD), }); // Encrypted attributes diff --git a/x-pack/plugins/alerting/server/saved_objects/is_rule_exportable.test.ts b/x-pack/plugins/alerting/server/saved_objects/is_rule_exportable.test.ts index bf330be87257a..7ed188915c869 100644 --- a/x-pack/plugins/alerting/server/saved_objects/is_rule_exportable.test.ts +++ b/x-pack/plugins/alerting/server/saved_objects/is_rule_exportable.test.ts @@ -16,6 +16,7 @@ import { isRuleExportable } from './is_rule_exportable'; import { inMemoryMetricsMock } from '../monitoring/in_memory_metrics.mock'; import { loggingSystemMock } from '@kbn/core/server/mocks'; import { AlertingConfig } from '../config'; +import { RULE_SAVED_OBJECT_TYPE } from '.'; let ruleTypeRegistryParams: ConstructorOptions; let logger: MockedLogger; @@ -67,7 +68,7 @@ describe('isRuleExportable', () => { isRuleExportable( { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, name: 'rule-name', @@ -127,7 +128,7 @@ describe('isRuleExportable', () => { isRuleExportable( { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, name: 'rule-name', @@ -190,7 +191,7 @@ describe('isRuleExportable', () => { isRuleExportable( { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, name: 'rule-name', diff --git a/x-pack/plugins/alerting/server/saved_objects/migrations/7.16/index.ts b/x-pack/plugins/alerting/server/saved_objects/migrations/7.16/index.ts index bf2870eb613bb..3984f3e5fb96e 100644 --- a/x-pack/plugins/alerting/server/saved_objects/migrations/7.16/index.ts +++ b/x-pack/plugins/alerting/server/saved_objects/migrations/7.16/index.ts @@ -9,6 +9,7 @@ import { SavedObjectAttribute, SavedObjectReference } from '@kbn/core-saved-obje import { SavedObjectUnsanitizedDoc } from '@kbn/core-saved-objects-server'; import { EncryptedSavedObjectsPluginSetup } from '@kbn/encrypted-saved-objects-plugin/server'; import { isString } from 'lodash/fp'; +import { RULE_SAVED_OBJECT_TYPE } from '../..'; import { RawRule, RawRuleAction } from '../../../types'; import { extractRefsFromGeoContainmentAlert } from '../../geo_containment/migrations'; import { createEsoMigration, isSecuritySolutionLegacyNotification, pipeMigrations } from '../utils'; @@ -126,7 +127,7 @@ function addRuleIdsToLegacyNotificationReferences( } else { const existingReferences = references ?? []; const existingReferenceFound = existingReferences.find((reference) => { - return reference.id === ruleAlertId && reference.type === 'alert'; + return reference.id === ruleAlertId && reference.type === RULE_SAVED_OBJECT_TYPE; }); if (existingReferenceFound) { // skip this if the references already exists for some uncommon reason so we do not add an additional one. @@ -135,7 +136,7 @@ function addRuleIdsToLegacyNotificationReferences( const savedObjectReference: SavedObjectReference = { id: ruleAlertId, name: 'param:alert_0', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }; const newReferences = [...existingReferences, savedObjectReference]; return { ...doc, references: newReferences }; diff --git a/x-pack/plugins/alerting/server/saved_objects/migrations/index.test.ts b/x-pack/plugins/alerting/server/saved_objects/migrations/index.test.ts index 93b8c647f7154..566911f00171d 100644 --- a/x-pack/plugins/alerting/server/saved_objects/migrations/index.test.ts +++ b/x-pack/plugins/alerting/server/saved_objects/migrations/index.test.ts @@ -15,6 +15,7 @@ import { migrationMocks } from '@kbn/core/server/mocks'; import { SavedObjectsUtils } from '@kbn/core-saved-objects-utils-server'; import { RuleType, ruleTypeMappings } from '@kbn/securitysolution-rules'; import { isAnyActionSupportIncidents } from './7.11'; +import { RULE_SAVED_OBJECT_TYPE } from '..'; const migrationContext = migrationMocks.createContext(); const encryptedSavedObjectsSetup = encryptedSavedObjectsMock.createSetup(); @@ -1811,7 +1812,7 @@ describe('successful migrations', () => { { name: 'param:alert_0', id: '123', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }, ], }); @@ -1867,7 +1868,7 @@ describe('successful migrations', () => { { name: 'param:alert_0', id: '123', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }, ], }; @@ -1882,7 +1883,7 @@ describe('successful migrations', () => { { name: 'param:alert_0', id: '123', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }, ], }); @@ -1923,7 +1924,7 @@ describe('successful migrations', () => { { name: 'param:alert_0', id: '123', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }, ], }); @@ -1944,7 +1945,7 @@ describe('successful migrations', () => { { name: 'param:alert_0', id: '123', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }, ], }; @@ -1959,7 +1960,7 @@ describe('successful migrations', () => { { name: 'param:alert_0', id: '123', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }, ], }); @@ -2002,7 +2003,7 @@ describe('successful migrations', () => { { name: 'param:alert_0', id: '123', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }, ], }; @@ -2017,7 +2018,7 @@ describe('successful migrations', () => { { name: 'param:alert_0', id: '123', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }, ], }); @@ -3208,6 +3209,6 @@ function getMockData( references: [], updated_at: withSavedObjectUpdatedAt ? getUpdatedAt() : undefined, id: uuidv4(), - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }; } diff --git a/x-pack/plugins/alerting/server/saved_objects/partially_update_alert.test.ts b/x-pack/plugins/alerting/server/saved_objects/partially_update_rule.test.ts similarity index 61% rename from x-pack/plugins/alerting/server/saved_objects/partially_update_alert.test.ts rename to x-pack/plugins/alerting/server/saved_objects/partially_update_rule.test.ts index 7560632d12e5a..2cdcd14d2dea5 100644 --- a/x-pack/plugins/alerting/server/saved_objects/partially_update_alert.test.ts +++ b/x-pack/plugins/alerting/server/saved_objects/partially_update_rule.test.ts @@ -11,14 +11,15 @@ import { SavedObjectsErrorHelpers, } from '@kbn/core/server'; -import { partiallyUpdateAlert, PartiallyUpdateableAlertAttributes } from './partially_update_alert'; +import { partiallyUpdateRule, PartiallyUpdateableRuleAttributes } from './partially_update_rule'; import { savedObjectsClientMock } from '@kbn/core/server/mocks'; +import { RULE_SAVED_OBJECT_TYPE } from '.'; const MockSavedObjectsClientContract = savedObjectsClientMock.create(); const MockISavedObjectsRepository = MockSavedObjectsClientContract as unknown as jest.Mocked; -describe('partially_update_alert', () => { +describe('partially_update_rule', () => { beforeEach(() => { jest.resetAllMocks(); }); @@ -28,52 +29,77 @@ describe('partially_update_alert', () => { test('should work with no options', async () => { soClient.update.mockResolvedValueOnce(MockUpdateValue); - await partiallyUpdateAlert(soClient, MockAlertId, DefaultAttributes); - expect(soClient.update).toHaveBeenCalledWith('alert', MockAlertId, DefaultAttributes, {}); + await partiallyUpdateRule(soClient, MockRuleId, DefaultAttributes); + expect(soClient.update).toHaveBeenCalledWith( + RULE_SAVED_OBJECT_TYPE, + MockRuleId, + DefaultAttributes, + {} + ); }); test('should work with extraneous attributes ', async () => { - const attributes = InvalidAttributes as unknown as PartiallyUpdateableAlertAttributes; + const attributes = InvalidAttributes as unknown as PartiallyUpdateableRuleAttributes; soClient.update.mockResolvedValueOnce(MockUpdateValue); - await partiallyUpdateAlert(soClient, MockAlertId, attributes); - expect(soClient.update).toHaveBeenCalledWith('alert', MockAlertId, DefaultAttributes, {}); + await partiallyUpdateRule(soClient, MockRuleId, attributes); + expect(soClient.update).toHaveBeenCalledWith( + RULE_SAVED_OBJECT_TYPE, + MockRuleId, + DefaultAttributes, + {} + ); }); test('should handle SO errors', async () => { soClient.update.mockRejectedValueOnce(new Error('wops')); await expect( - partiallyUpdateAlert(soClient, MockAlertId, DefaultAttributes) + partiallyUpdateRule(soClient, MockRuleId, DefaultAttributes) ).rejects.toThrowError('wops'); }); test('should handle the version option', async () => { soClient.update.mockResolvedValueOnce(MockUpdateValue); - await partiallyUpdateAlert(soClient, MockAlertId, DefaultAttributes, { version: '1.2.3' }); - expect(soClient.update).toHaveBeenCalledWith('alert', MockAlertId, DefaultAttributes, { - version: '1.2.3', - }); + await partiallyUpdateRule(soClient, MockRuleId, DefaultAttributes, { version: '1.2.3' }); + expect(soClient.update).toHaveBeenCalledWith( + RULE_SAVED_OBJECT_TYPE, + MockRuleId, + DefaultAttributes, + { + version: '1.2.3', + } + ); }); test('should handle the ignore404 option', async () => { const err = SavedObjectsErrorHelpers.createGenericNotFoundError(); soClient.update.mockRejectedValueOnce(err); - await partiallyUpdateAlert(soClient, MockAlertId, DefaultAttributes, { ignore404: true }); - expect(soClient.update).toHaveBeenCalledWith('alert', MockAlertId, DefaultAttributes, {}); + await partiallyUpdateRule(soClient, MockRuleId, DefaultAttributes, { ignore404: true }); + expect(soClient.update).toHaveBeenCalledWith( + RULE_SAVED_OBJECT_TYPE, + MockRuleId, + DefaultAttributes, + {} + ); }); test('should handle the namespace option', async () => { soClient.update.mockResolvedValueOnce(MockUpdateValue); - await partiallyUpdateAlert(soClient, MockAlertId, DefaultAttributes, { - namespace: 'bat.cave', - }); - expect(soClient.update).toHaveBeenCalledWith('alert', MockAlertId, DefaultAttributes, { + await partiallyUpdateRule(soClient, MockRuleId, DefaultAttributes, { namespace: 'bat.cave', }); + expect(soClient.update).toHaveBeenCalledWith( + RULE_SAVED_OBJECT_TYPE, + MockRuleId, + DefaultAttributes, + { + namespace: 'bat.cave', + } + ); }); }); }); @@ -100,11 +126,11 @@ const DefaultAttributes = { const InvalidAttributes = { ...DefaultAttributes, foo: 'bar' }; -const MockAlertId = 'alert-id'; +const MockRuleId = 'rule-id'; const MockUpdateValue = { - id: MockAlertId, - type: 'alert', + id: MockRuleId, + type: RULE_SAVED_OBJECT_TYPE, attributes: { actions: [], scheduledTaskId: 'scheduled-task-id', diff --git a/x-pack/plugins/alerting/server/saved_objects/partially_update_alert.ts b/x-pack/plugins/alerting/server/saved_objects/partially_update_rule.ts similarity index 63% rename from x-pack/plugins/alerting/server/saved_objects/partially_update_alert.ts rename to x-pack/plugins/alerting/server/saved_objects/partially_update_rule.ts index 2c275a5f602fc..8d79fe300c7e9 100644 --- a/x-pack/plugins/alerting/server/saved_objects/partially_update_alert.ts +++ b/x-pack/plugins/alerting/server/saved_objects/partially_update_rule.ts @@ -13,13 +13,17 @@ import { } from '@kbn/core/server'; import { RawRule } from '../types'; -import { AlertAttributesExcludedFromAAD, AlertAttributesExcludedFromAADType } from '.'; +import { + RuleAttributesExcludedFromAAD, + RuleAttributesExcludedFromAADType, + RULE_SAVED_OBJECT_TYPE, +} from '.'; -export type PartiallyUpdateableAlertAttributes = Partial< - Pick +export type PartiallyUpdateableRuleAttributes = Partial< + Pick >; -export interface PartiallyUpdateAlertSavedObjectOptions { +interface PartiallyUpdateRuleSavedObjectOptions { refresh?: SavedObjectsUpdateOptions['refresh']; version?: string; ignore404?: boolean; @@ -29,16 +33,16 @@ export interface PartiallyUpdateAlertSavedObjectOptions { // typed this way so we can send a SavedObjectClient or SavedObjectRepository type SavedObjectClientForUpdate = Pick; -// direct, partial update to an alert saved object via scoped SavedObjectsClient +// direct, partial update to a rule saved object via scoped SavedObjectsClient // using namespace set in the client -export async function partiallyUpdateAlert( +export async function partiallyUpdateRule( savedObjectsClient: SavedObjectClientForUpdate, id: string, - attributes: PartiallyUpdateableAlertAttributes, - options: PartiallyUpdateAlertSavedObjectOptions = {} + attributes: PartiallyUpdateableRuleAttributes, + options: PartiallyUpdateRuleSavedObjectOptions = {} ): Promise { // ensure we only have the valid attributes excluded from AAD - const attributeUpdates = pick(attributes, AlertAttributesExcludedFromAAD); + const attributeUpdates = pick(attributes, RuleAttributesExcludedFromAAD); const updateOptions: SavedObjectsUpdateOptions = pick( options, 'namespace', @@ -47,7 +51,12 @@ export async function partiallyUpdateAlert( ); try { - await savedObjectsClient.update('alert', id, attributeUpdates, updateOptions); + await savedObjectsClient.update( + RULE_SAVED_OBJECT_TYPE, + id, + attributeUpdates, + updateOptions + ); } catch (err) { if (options?.ignore404 && SavedObjectsErrorHelpers.isNotFoundError(err)) { return; diff --git a/x-pack/plugins/alerting/server/saved_objects/transform_rule_for_export.test.ts b/x-pack/plugins/alerting/server/saved_objects/transform_rule_for_export.test.ts index 1269a9a776307..7b11ab75f868d 100644 --- a/x-pack/plugins/alerting/server/saved_objects/transform_rule_for_export.test.ts +++ b/x-pack/plugins/alerting/server/saved_objects/transform_rule_for_export.test.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { RULE_SAVED_OBJECT_TYPE } from '.'; import { transformRulesForExport } from './transform_rule_for_export'; jest.mock('../lib/rule_execution_status', () => ({ getRuleExecutionStatusPendingAttributes: () => ({ @@ -18,7 +19,7 @@ describe('transform rule for export', () => { const mockRules = [ { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: true, name: 'rule-name', @@ -51,7 +52,7 @@ describe('transform rule for export', () => { }, { id: '2', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, attributes: { enabled: false, name: 'disabled-rule', 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 2fbb7132e0a6f..c08fb5490fbb9 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 @@ -36,6 +36,7 @@ import { mockAAD } from './fixtures'; import { schema } from '@kbn/config-schema'; import { alertsClientMock } from '../alerts_client/alerts_client.mock'; import { ExecutionResponseType } from '@kbn/actions-plugin/server/create_execute_function'; +import { RULE_SAVED_OBJECT_TYPE } from '../saved_objects'; jest.mock('./inject_action_params', () => ({ injectActionParams: jest.fn(), @@ -359,13 +360,13 @@ describe('Execution Handler', () => { }, source: asSavedObjectExecutionSource({ id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }), relatedSavedObjects: [ { id: '1', namespace: 'test1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, typeId: 'test', }, ], @@ -1683,8 +1684,10 @@ describe('Execution Handler', () => { executionId: '5f6aa57d-3e22-484e-bae8-cbed868f4d28', id: '1', params: {}, - relatedSavedObjects: [{ id: '1', namespace: 'test1', type: 'alert', typeId: 'test' }], - source: { source: { id: '1', type: 'alert' }, type: 'SAVED_OBJECT' }, + relatedSavedObjects: [ + { id: '1', namespace: 'test1', type: RULE_SAVED_OBJECT_TYPE, typeId: 'test' }, + ], + source: { source: { id: '1', type: RULE_SAVED_OBJECT_TYPE }, type: 'SAVED_OBJECT' }, spaceId: 'test1', }, ]); 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 e5fbe92238a0c..29f56b630eaf8 100644 --- a/x-pack/plugins/alerting/server/task_runner/execution_handler.ts +++ b/x-pack/plugins/alerting/server/task_runner/execution_handler.ts @@ -51,6 +51,7 @@ import { isSummaryActionOnInterval, isSummaryActionThrottled, } from './rule_action_helper'; +import { RULE_SAVED_OBJECT_TYPE } from '../saved_objects'; enum Reasons { MUTED = 'muted', @@ -554,13 +555,13 @@ export class ExecutionHandler< consumer: ruleConsumer, source: asSavedObjectExecutionSource({ id: ruleId, - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }), executionId, relatedSavedObjects: [ { id: ruleId, - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, namespace: namespace.namespace, typeId: this.ruleType.id, }, diff --git a/x-pack/plugins/alerting/server/task_runner/fixtures.ts b/x-pack/plugins/alerting/server/task_runner/fixtures.ts index c1c4b442de13e..b975f21304013 100644 --- a/x-pack/plugins/alerting/server/task_runner/fixtures.ts +++ b/x-pack/plugins/alerting/server/task_runner/fixtures.ts @@ -20,6 +20,7 @@ import { getDefaultMonitoring } from '../lib/monitoring'; import { UntypedNormalizedRuleType } from '../rule_type_registry'; import { EVENT_LOG_ACTIONS } from '../plugin'; import { RawRule } from '../types'; +import { RULE_SAVED_OBJECT_TYPE } from '../saved_objects'; interface GeneratorParams { [key: string]: string | number | boolean | undefined | object[] | boolean[] | object; @@ -81,7 +82,7 @@ export const generateSavedObjectParams = ({ history?: RuleMonitoring['run']['history']; alertsCount?: Record; }) => [ - 'alert', + RULE_SAVED_OBJECT_TYPE, '1', { monitoring: { @@ -213,7 +214,7 @@ export const mockedRuleTypeSavedObject: Rule = { export const mockedRawRuleSO: SavedObject = { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, references: [], attributes: { legacyId: '1', @@ -419,14 +420,14 @@ export const generateEnqueueFunctionInput = ({ { id: '1', namespace: undefined, - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, typeId: RULE_TYPE_ID, }, ], source: { source: { id: '1', - type: 'alert', + type: RULE_SAVED_OBJECT_TYPE, }, type: 'SAVED_OBJECT', }, diff --git a/x-pack/plugins/alerting/server/task_runner/rule_loader.test.ts b/x-pack/plugins/alerting/server/task_runner/rule_loader.test.ts index 5371e56ff07b2..380d436c95e65 100644 --- a/x-pack/plugins/alerting/server/task_runner/rule_loader.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/rule_loader.test.ts @@ -18,6 +18,7 @@ import { MONITORING_HISTORY_LIMIT, RuleExecutionStatusErrorReasons } from '../.. import { ErrorWithReason, getReasonFromError } from '../lib/error_with_reason'; import { alertingEventLoggerMock } from '../lib/alerting_event_logger/alerting_event_logger.mock'; import { mockedRawRuleSO, mockedRule } from './fixtures'; +import { RULE_SAVED_OBJECT_TYPE } from '../saved_objects'; // create mocks const rulesClient = rulesClientMock.create(); @@ -199,7 +200,7 @@ describe('rule_loader', () => { expect(contextMock.spaceIdToNamespace.mock.calls[0]).toEqual(['default']); const esoArgs = encryptedSavedObjects.getDecryptedAsInternalUser.mock.calls[0]; - expect(esoArgs).toEqual(['alert', ruleId, { namespace: undefined }]); + expect(esoArgs).toEqual([RULE_SAVED_OBJECT_TYPE, ruleId, { namespace: undefined }]); }); test('succeeds with non-default space', async () => { @@ -218,7 +219,7 @@ describe('rule_loader', () => { }); const esoArgs = encryptedSavedObjects.getDecryptedAsInternalUser.mock.calls[0]; - expect(esoArgs).toEqual(['alert', ruleId, { namespace: spaceId }]); + expect(esoArgs).toEqual([RULE_SAVED_OBJECT_TYPE, ruleId, { namespace: spaceId }]); }); test('fails', async () => { diff --git a/x-pack/plugins/alerting/server/task_runner/rule_loader.ts b/x-pack/plugins/alerting/server/task_runner/rule_loader.ts index f6bb71aef7453..fb037b802ec9b 100644 --- a/x-pack/plugins/alerting/server/task_runner/rule_loader.ts +++ b/x-pack/plugins/alerting/server/task_runner/rule_loader.ts @@ -24,6 +24,7 @@ import { } from '../types'; import { MONITORING_HISTORY_LIMIT, RuleTypeParams } from '../../common'; import { AlertingEventLogger } from '../lib/alerting_event_logger/alerting_event_logger'; +import { RULE_SAVED_OBJECT_TYPE } from '../saved_objects'; export interface RuleData extends LoadedIndirectParams { indirectParams: RawRule; @@ -114,7 +115,7 @@ export async function getRuleAttributes( const namespace = context.spaceIdToNamespace(spaceId); const rawRule = await context.encryptedSavedObjectsClient.getDecryptedAsInternalUser( - 'alert', + RULE_SAVED_OBJECT_TYPE, ruleId, { namespace } ); diff --git a/x-pack/plugins/alerting/server/task_runner/running_handler.test.ts b/x-pack/plugins/alerting/server/task_runner/running_handler.test.ts index ce3a3c6680e7b..3ebb122b3a19a 100644 --- a/x-pack/plugins/alerting/server/task_runner/running_handler.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/running_handler.test.ts @@ -6,11 +6,11 @@ */ import { ISavedObjectsRepository, Logger } from '@kbn/core/server'; -import { partiallyUpdateAlert } from '../saved_objects/partially_update_alert'; +import { partiallyUpdateRule } from '../saved_objects/partially_update_rule'; import { RunningHandler } from './running_handler'; -jest.mock('../saved_objects/partially_update_alert', () => ({ - partiallyUpdateAlert: jest.fn(), +jest.mock('../saved_objects/partially_update_rule', () => ({ + partiallyUpdateRule: jest.fn(), })); describe('isRunning handler', () => { @@ -20,7 +20,7 @@ describe('isRunning handler', () => { } as unknown as Logger; const ruleTypeId = 'myType'; beforeEach(() => { - (partiallyUpdateAlert as jest.Mock).mockClear(); + (partiallyUpdateRule as jest.Mock).mockClear(); (logger.error as jest.Mock).mockClear(); jest.useFakeTimers(); }); @@ -29,23 +29,23 @@ describe('isRunning handler', () => { }); test('Should resolve if nothing got started', async () => { - (partiallyUpdateAlert as jest.Mock).mockImplementation(() => Promise.resolve('resolve')); + (partiallyUpdateRule as jest.Mock).mockImplementation(() => Promise.resolve('resolve')); const runHandler = new RunningHandler(soClient, logger, ruleTypeId); const resp = await runHandler.waitFor(); - expect(partiallyUpdateAlert).toHaveBeenCalledTimes(0); + expect(partiallyUpdateRule).toHaveBeenCalledTimes(0); expect(logger.error).toHaveBeenCalledTimes(0); expect(resp).toBe(undefined); }); - test('Should return the promise from partiallyUpdateAlert when the update isRunning has been a success', async () => { - (partiallyUpdateAlert as jest.Mock).mockImplementation(() => Promise.resolve('resolve')); + test('Should return the promise from partiallyUpdateRule when the update isRunning has been a success', async () => { + (partiallyUpdateRule as jest.Mock).mockImplementation(() => Promise.resolve('resolve')); const runHandler = new RunningHandler(soClient, logger, ruleTypeId); runHandler.start('9876543210'); jest.runAllTimers(); const resp = await runHandler.waitFor(); - expect(partiallyUpdateAlert).toHaveBeenCalledTimes(1); - expect((partiallyUpdateAlert as jest.Mock).mock.calls[0]).toMatchInlineSnapshot(` + expect(partiallyUpdateRule).toHaveBeenCalledTimes(1); + expect((partiallyUpdateRule as jest.Mock).mock.calls[0]).toMatchInlineSnapshot(` Array [ [MockFunction], "9876543210", @@ -64,15 +64,13 @@ describe('isRunning handler', () => { }); test('Should reject when the update isRunning has been a failure', async () => { - (partiallyUpdateAlert as jest.Mock).mockImplementation(() => - Promise.reject(new Error('error')) - ); + (partiallyUpdateRule as jest.Mock).mockImplementation(() => Promise.reject(new Error('error'))); const runHandler = new RunningHandler(soClient, logger, ruleTypeId); runHandler.start('9876543210'); jest.runAllTimers(); await expect(runHandler.waitFor()).rejects.toThrow(); - expect(partiallyUpdateAlert).toHaveBeenCalledTimes(1); + expect(partiallyUpdateRule).toHaveBeenCalledTimes(1); expect(logger.error).toHaveBeenCalledTimes(1); }); }); diff --git a/x-pack/plugins/alerting/server/task_runner/running_handler.ts b/x-pack/plugins/alerting/server/task_runner/running_handler.ts index 118f13ab6eed9..1602e9421ecef 100644 --- a/x-pack/plugins/alerting/server/task_runner/running_handler.ts +++ b/x-pack/plugins/alerting/server/task_runner/running_handler.ts @@ -6,7 +6,7 @@ */ import { ISavedObjectsRepository, Logger } from '@kbn/core/server'; -import { partiallyUpdateAlert } from '../saved_objects/partially_update_alert'; +import { partiallyUpdateRule } from '../saved_objects/partially_update_rule'; const TIME_TO_WAIT = 2000; @@ -45,7 +45,7 @@ export class RunningHandler { private setRunning(ruleId: string, namespace?: string) { this.isUpdating = true; - this.runningPromise = partiallyUpdateAlert( + this.runningPromise = partiallyUpdateRule( this.client, ruleId, { running: true }, 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 ee55378d08795..23d5d5f576ce3 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 @@ -82,6 +82,7 @@ import { alertsServiceMock } from '../alerts_service/alerts_service.mock'; import { getMockMaintenanceWindow } from '../data/maintenance_window/test_helpers'; import { alertsClientMock } from '../alerts_client/alerts_client.mock'; import { MaintenanceWindow } from '../application/maintenance_window/types'; +import { RULE_SAVED_OBJECT_TYPE } from '../saved_objects'; jest.mock('uuid', () => ({ v4: () => '5f6aa57d-3e22-484e-bae8-cbed868f4d28', @@ -2101,7 +2102,10 @@ describe('Task Runner', () => { test('reschedules for smaller interval if es connectivity error encountered and schedule interval is greater than connectivity retry', async () => { rulesClient.getAlertFromRaw.mockImplementation(() => { - throw SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError('alert', '1'); + throw SavedObjectsErrorHelpers.createGenericNotFoundEsUnavailableError( + RULE_SAVED_OBJECT_TYPE, + '1' + ); }); const taskRunner = new TaskRunner({ 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 4a1245f29ee82..532dd7b1e12ba 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner.ts @@ -43,7 +43,7 @@ import { import { asErr, asOk, isErr, isOk, map, resolveErr, Result } from '../lib/result_type'; import { taskInstanceToAlertTaskInstance } from './alert_task_instance'; import { isAlertSavedObjectNotFoundError, isEsUnavailableError } from '../lib/is_alerting_error'; -import { partiallyUpdateAlert } from '../saved_objects'; +import { partiallyUpdateRule, RULE_SAVED_OBJECT_TYPE } from '../saved_objects'; import { AlertInstanceContext, AlertInstanceState, @@ -221,7 +221,7 @@ export class TaskRunner< // eslint-disable-next-line no-empty } catch {} try { - await partiallyUpdateAlert( + await partiallyUpdateRule( client, ruleId, { ...attributes, running: false }, @@ -473,7 +473,7 @@ export class TaskRunner< }; const savedObjectsClient = this.context.savedObjects.getScopedClient(fakeRequest, { - includedHiddenTypes: ['alert', 'action'], + includedHiddenTypes: [RULE_SAVED_OBJECT_TYPE, 'action'], }); const dataViews = await this.context.dataViews.dataViewsServiceFactory( diff --git a/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts b/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts index 46783154a6a4a..0ce2f758ade39 100644 --- a/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts +++ b/x-pack/plugins/alerting/server/task_runner/task_runner_cancel.test.ts @@ -58,6 +58,7 @@ import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; import { rulesSettingsClientMock } from '../rules_settings_client.mock'; import { maintenanceWindowClientMock } from '../maintenance_window_client.mock'; import { alertsServiceMock } from '../alerts_service/alerts_service.mock'; +import { RULE_SAVED_OBJECT_TYPE } from '../saved_objects'; jest.mock('uuid', () => ({ v4: () => '5f6aa57d-3e22-484e-bae8-cbed868f4d28', @@ -222,7 +223,7 @@ describe('Task Runner Cancel', () => { expect( taskRunnerFactoryInitializerParams.internalSavedObjectsRepository.update ).toHaveBeenCalledWith( - 'alert', + RULE_SAVED_OBJECT_TYPE, '1', { executionStatus: { diff --git a/x-pack/plugins/alerting/server/usage/task.ts b/x-pack/plugins/alerting/server/usage/task.ts index e7008056677d0..507970d436d6f 100644 --- a/x-pack/plugins/alerting/server/usage/task.ts +++ b/x-pack/plugins/alerting/server/usage/task.ts @@ -20,6 +20,7 @@ import { getExecutionTimeoutsPerDayCount, } from './lib/get_telemetry_from_event_log'; import { stateSchemaByVersion, emptyState, type LatestTaskStateSchema } from './task_state'; +import { RULE_SAVED_OBJECT_TYPE } from '../saved_objects'; export const TELEMETRY_TASK_TYPE = 'alerting_telemetry'; @@ -90,7 +91,7 @@ export function telemetryTaskRunner( const getAlertIndex = () => core .getStartServices() - .then(([coreStart]) => coreStart.savedObjects.getIndexForType('alert')); + .then(([coreStart]) => coreStart.savedObjects.getIndexForType(RULE_SAVED_OBJECT_TYPE)); return { async run() { diff --git a/x-pack/plugins/apm/common/__snapshots__/apm_telemetry.test.ts.snap b/x-pack/plugins/apm/common/__snapshots__/apm_telemetry.test.ts.snap index 79ecdc077ea6f..a372355d1db7c 100644 --- a/x-pack/plugins/apm/common/__snapshots__/apm_telemetry.test.ts.snap +++ b/x-pack/plugins/apm/common/__snapshots__/apm_telemetry.test.ts.snap @@ -151,6 +151,12 @@ exports[`APM telemetry helpers getApmTelemetry generates a JSON object with the "description": "Total number of services utilizing the opentelemetry/swift agent within the last day" } }, + "opentelemetry/android": { + "type": "long", + "_meta": { + "description": "Total number of services utilizing the opentelemetry/android agent within the last day" + } + }, "opentelemetry/webjs": { "type": "long", "_meta": { diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/service_groups/service_groups.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/service_groups/service_groups.cy.ts index bc7e7d331cceb..731149159b681 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/service_groups/service_groups.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/service_groups/service_groups.cy.ts @@ -82,7 +82,7 @@ describe('Service groups', () => { it('creates a service group', () => { cy.getByTestSubj('apmCreateServiceGroupButton').click(); - cy.getByTestSubj('apmGroupNameInput').type('go services'); + cy.getByTestSubj('apmGroupNameInput').type('go services{enter}'); cy.contains('Select services').click(); cy.getByTestSubj('headerFilterKuerybar').type('agent.name:"go"{enter}'); cy.contains('synth-go-1'); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/service_overview/alerts_table.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/service_overview/alerts_table.cy.ts index 0bce81a620a51..19c07645fb37f 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/service_overview/alerts_table.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/service_overview/alerts_table.cy.ts @@ -38,7 +38,9 @@ describe('Errors table', () => { it('Alerts table with the search bar is populated', () => { cy.visitKibana(serviceOverviewHref); cy.contains('opbeans-java'); - cy.contains('All'); + cy.get( + '[data-test-subj="environmentFilter"] [data-test-subj="comboBoxSearchInput"]' + ).should('have.value', 'All'); cy.contains('Active'); cy.contains('Recovered'); cy.getByTestSubj('globalQueryBar').should('exist'); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/service_overview/service_overview.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/service_overview/service_overview.cy.ts index 35184a59880d7..7508612d0acc5 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/service_overview/service_overview.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/service_overview/service_overview.cy.ts @@ -226,9 +226,9 @@ describe('Service Overview', () => { 'suggestionsRequest' ); - cy.getByTestSubj('environmentFilter').find('input').type('production', { - force: true, - }); + cy.getByTestSubj('environmentFilter') + .find('input') + .type('{selectall}production', { force: true }); cy.expectAPIsToHaveBeenCalledWith({ apisIntercepted: ['@suggestionsRequest'], diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/settings/agent_configurations.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/settings/agent_configurations.cy.ts index e0ca47df79996..1d9d34d7b9a2d 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/settings/agent_configurations.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/settings/agent_configurations.cy.ts @@ -107,7 +107,10 @@ describe('Agent configuration', () => { cy.contains('Create configuration'); cy.contains('Edit').click(); cy.wait('@serviceEnvironmentApi'); - cy.contains('production'); + cy.getByTestSubj('serviceEnviromentComboBox') + .find('input') + .invoke('val') + .should('contain', 'production'); }); it('displays All label when selecting all option', () => { cy.intercept( @@ -129,6 +132,9 @@ describe('Agent configuration', () => { cy.contains('Environment All'); cy.contains('Edit').click(); cy.wait('@serviceEnvironmentApi'); - cy.contains('All'); + cy.getByTestSubj('serviceEnviromentComboBox') + .find('input') + .invoke('val') + .should('contain', 'All'); }); }); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/storage_explorer/storage_explorer.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/storage_explorer/storage_explorer.cy.ts index bfa0aaa79d987..11025b29dd0e7 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/storage_explorer/storage_explorer.cy.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/storage_explorer/storage_explorer.cy.ts @@ -130,7 +130,7 @@ describe('Storage Explorer', () => { it('with the correct environment when changing the environment', () => { cy.wait(mainAliasNames); - cy.getByTestSubj('environmentFilter').type('production'); + cy.getByTestSubj('environmentFilter').type('{selectall}production'); cy.contains('button', 'production').click({ force: true }); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/transaction_details/generate_span_stacktrace_data.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/transaction_details/generate_span_stacktrace_data.ts new file mode 100644 index 0000000000000..85eeb2cd456ef --- /dev/null +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/transaction_details/generate_span_stacktrace_data.ts @@ -0,0 +1,106 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { apm, timerange } from '@kbn/apm-synthtrace-client'; +import { synthtrace } from '../../../synthtrace'; + +function getAPMGeneratedStacktrace() { + const apmGeneratedStacktrace = apm + .service({ + name: 'apm-generated', + environment: 'production', + agentName: 'java', + }) + .instance('instance a'); + + return Array.from( + timerange( + new Date('2022-01-01T00:00:00.000Z'), + new Date('2022-01-01T00:01:00.000Z') + ) + .interval('1m') + .rate(1) + .generator((timestamp) => { + return apmGeneratedStacktrace + .transaction({ transactionName: `Transaction A` }) + .defaults({ + 'service.language.name': 'java', + }) + .timestamp(timestamp) + .duration(1000) + .success() + .children( + apmGeneratedStacktrace + .span({ + spanName: `Span A`, + spanType: 'internal', + 'span.stacktrace': [ + { + library_frame: false, + exclude_from_grouping: false, + filename: 'OutputBuffer.java', + classname: 'org.apache.catalina.connector.OutputBuffer', + line: { number: 825 }, + module: 'org.apache.catalina.connector', + function: 'flushByteBuffer', + }, + ], + }) + .timestamp(timestamp + 50) + .duration(100) + .failure() + ); + }) + ); +} + +function getOtelGeneratedStacktrace() { + const apmGeneratedStacktrace = apm + .service({ + name: 'otel-generated', + environment: 'production', + agentName: 'java', + }) + .instance('instance a'); + + return Array.from( + timerange( + new Date('2022-01-01T00:00:00.000Z'), + new Date('2022-01-01T00:01:00.000Z') + ) + .interval('1m') + .rate(1) + .generator((timestamp) => { + return apmGeneratedStacktrace + .transaction({ transactionName: `Transaction A` }) + .timestamp(timestamp) + .duration(1000) + .defaults({ + 'service.language.name': 'java', + }) + .success() + .children( + apmGeneratedStacktrace + .span({ + spanName: `Span A`, + spanType: 'internal', + 'code.stacktrace': + 'java.lang.Throwable\n\tat co.elastic.otel.ElasticSpanProcessor.captureStackTrace(ElasticSpanProcessor.java:81)', + }) + .timestamp(timestamp + 50) + .duration(100) + .failure() + ); + }) + ); +} + +export function generateSpanStacktraceData() { + const apmGeneratedStacktrace = getAPMGeneratedStacktrace(); + const otelGeneratedStacktrace = getOtelGeneratedStacktrace(); + + synthtrace.index([...apmGeneratedStacktrace, ...otelGeneratedStacktrace]); +} diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/e2e/transaction_details/span_stacktrace.cy.ts b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/transaction_details/span_stacktrace.cy.ts new file mode 100644 index 0000000000000..dbc4a1072ffa0 --- /dev/null +++ b/x-pack/plugins/apm/ftr_e2e/cypress/e2e/transaction_details/span_stacktrace.cy.ts @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import url from 'url'; +import { synthtrace } from '../../../synthtrace'; +import { generateSpanStacktraceData } from './generate_span_stacktrace_data'; + +const start = '2022-01-01T00:00:00.000Z'; +const end = '2022-01-01T00:15:00.000Z'; + +function getServiceInventoryUrl({ serviceName }: { serviceName: string }) { + return url.format({ + pathname: `/app/apm/services/${serviceName}`, + query: { + rangeFrom: start, + rangeTo: end, + environment: 'ENVIRONMENT_ALL', + kuery: '', + serviceGroup: '', + transactionType: 'request', + comparisonEnabled: true, + offset: '1d', + }, + }); +} + +describe('Span stacktrace', () => { + beforeEach(() => { + cy.loginAsViewerUser(); + }); + describe('span flyout', () => { + before(() => { + generateSpanStacktraceData(); + }); + + after(() => { + synthtrace.clean(); + }); + it('Shows APM agent generated stacktrace', () => { + cy.visitKibana(getServiceInventoryUrl({ serviceName: 'apm-generated' })); + cy.contains('Transaction A').click(); + cy.contains('Span A').click(); + cy.getByTestSubj('spanStacktraceTab').click(); + cy.contains( + 'at org.apache.catalina.connector.OutputBuffer.flushByteBuffer(OutputBuffer.java:825)' + ); + }); + + it('Shows Otel generated stacktrace', () => { + cy.visitKibana(getServiceInventoryUrl({ serviceName: 'otel-generated' })); + cy.contains('Transaction A').click(); + cy.contains('Span A').click(); + cy.getByTestSubj('spanStacktraceTab').click(); + cy.contains( + `java.lang.Throwable at co.elastic.otel.ElasticSpanProcessor.captureStackTrace(ElasticSpanProcessor.java:81)` + ); + }); + }); +}); diff --git a/x-pack/plugins/apm/ftr_e2e/cypress/support/commands.ts b/x-pack/plugins/apm/ftr_e2e/cypress/support/commands.ts index da8b1d418f3a6..f4341fe938092 100644 --- a/x-pack/plugins/apm/ftr_e2e/cypress/support/commands.ts +++ b/x-pack/plugins/apm/ftr_e2e/cypress/support/commands.ts @@ -94,14 +94,16 @@ Cypress.Commands.add( .nextAll() .find('[data-test-subj="superDatePickerAbsoluteDateInput"]') .clear({ force: true }) - .type(moment(start).format(format), { force: true }); + .type(moment(start).format(format), { force: true }) + .type('{enter}'); cy.getByTestSubj('superDatePickerendDatePopoverButton').click(); cy.contains('End date') .nextAll() .find('[data-test-subj="superDatePickerAbsoluteDateInput"]') .clear({ force: true }) - .type(moment(end).format(format), { force: true }); + .type(moment(end).format(format), { force: true }) + .type('{enter}'); } ); diff --git a/x-pack/plugins/apm/public/assistant_functions/get_apm_services_list.ts b/x-pack/plugins/apm/public/assistant_functions/get_apm_services_list.ts deleted file mode 100644 index 9047f768449e5..0000000000000 --- a/x-pack/plugins/apm/public/assistant_functions/get_apm_services_list.ts +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; -import type { RegisterFunctionDefinition } from '@kbn/observability-ai-assistant-plugin/common/types'; -import { ServiceHealthStatus } from '../../common/service_health_status'; -import { callApmApi } from '../services/rest/create_call_apm_api'; -import { NON_EMPTY_STRING } from '../utils/non_empty_string_ref'; - -export function registerGetApmServicesListFunction({ - registerFunction, -}: { - registerFunction: RegisterFunctionDefinition; -}) { - registerFunction( - { - name: 'get_apm_services_list', - contexts: ['apm'], - description: `Gets a list of services`, - descriptionForUser: i18n.translate( - 'xpack.apm.observabilityAiAssistant.functions.registerGetApmServicesList.descriptionForUser', - { - defaultMessage: `Gets the list of monitored services, their health status, and alerts.`, - } - ), - parameters: { - type: 'object', - additionalProperties: false, - properties: { - 'service.environment': { - ...NON_EMPTY_STRING, - description: - 'Optionally filter the services by the environments that they are running in', - }, - start: { - ...NON_EMPTY_STRING, - description: - 'The start of the time range, in Elasticsearch date math, like `now`.', - }, - end: { - ...NON_EMPTY_STRING, - description: - 'The end of the time range, in Elasticsearch date math, like `now-24h`.', - }, - healthStatus: { - type: 'array', - description: 'Filter service list by health status', - additionalProperties: false, - additionalItems: false, - items: { - type: 'string', - enum: [ - ServiceHealthStatus.unknown, - ServiceHealthStatus.healthy, - ServiceHealthStatus.warning, - ServiceHealthStatus.critical, - ], - }, - }, - }, - required: ['start', 'end'], - } as const, - }, - async ({ arguments: args }, signal) => { - return callApmApi('POST /internal/apm/assistant/get_services_list', { - signal, - params: { - body: args, - }, - }); - } - ); -} diff --git a/x-pack/plugins/apm/public/assistant_functions/get_apm_timeseries.tsx b/x-pack/plugins/apm/public/assistant_functions/get_apm_timeseries.tsx index 56269d884ad84..445610321cbb8 100644 --- a/x-pack/plugins/apm/public/assistant_functions/get_apm_timeseries.tsx +++ b/x-pack/plugins/apm/public/assistant_functions/get_apm_timeseries.tsx @@ -5,294 +5,164 @@ * 2.0. */ import { EuiFlexGroup, EuiFlexItem, EuiText } from '@elastic/eui'; -import type { RegisterFunctionDefinition } from '@kbn/observability-ai-assistant-plugin/common/types'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import type { + RegisterRenderFunctionDefinition, + RenderFunction, +} from '@kbn/observability-ai-assistant-plugin/public/types'; + import { groupBy } from 'lodash'; import React from 'react'; -import { i18n } from '@kbn/i18n'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { FETCH_STATUS } from '../hooks/use_fetcher'; -import { callApmApi } from '../services/rest/create_call_apm_api'; -import { getTimeZone } from '../components/shared/charts/helper/timezone'; -import { TimeseriesChart } from '../components/shared/charts/timeseries_chart'; -import { ChartPointerEventContextProvider } from '../context/chart_pointer_event/chart_pointer_event_context'; -import { ApmThemeProvider } from '../components/routing/app_root'; -import { Coordinate, TimeSeries } from '../../typings/timeseries'; -import { - ChartType, - getTimeSeriesColor, -} from '../components/shared/charts/helper/get_timeseries_color'; import { LatencyAggregationType } from '../../common/latency_aggregation_types'; import { asPercent, asTransactionRate, getDurationFormatter, } from '../../common/utils/formatters'; +import type { + GetApmTimeseriesFunctionArguments, + GetApmTimeseriesFunctionResponse, +} from '../../server/assistant_functions/get_apm_timeseries'; +import { Coordinate, TimeSeries } from '../../typings/timeseries'; +import { ApmThemeProvider } from '../components/routing/app_root'; +import { + ChartType, + getTimeSeriesColor, +} from '../components/shared/charts/helper/get_timeseries_color'; +import { getTimeZone } from '../components/shared/charts/helper/timezone'; +import { TimeseriesChart } from '../components/shared/charts/timeseries_chart'; import { getMaxY, getResponseTimeTickFormatter, } from '../components/shared/charts/transaction_charts/helper'; -import { NON_EMPTY_STRING } from '../utils/non_empty_string_ref'; +import { ChartPointerEventContextProvider } from '../context/chart_pointer_event/chart_pointer_event_context'; +import { FETCH_STATUS } from '../hooks/use_fetcher'; export function registerGetApmTimeseriesFunction({ - registerFunction, + registerRenderFunction, }: { - registerFunction: RegisterFunctionDefinition; + registerRenderFunction: RegisterRenderFunctionDefinition; }) { - registerFunction( - { - contexts: ['apm'], - name: 'get_apm_timeseries', - descriptionForUser: i18n.translate( - 'xpack.apm.observabilityAiAssistant.functions.registerGetApmTimeseries.descriptionForUser', - { - defaultMessage: `Display different APM metrics, like throughput, failure rate, or latency, for any service or all services, or any or all of its dependencies, both as a timeseries and as a single statistic. Additionally, the function will return any changes, such as spikes, step and trend changes, or dips. You can also use it to compare data by requesting two different time ranges, or for instance two different service versions`, - } - ), - description: `Visualise and analyse different APM metrics, like throughput, failure rate, or latency, for any service or all services, or any or all of its dependencies, both as a timeseries and as a single statistic. A visualisation will be displayed above your reply - DO NOT attempt to display or generate an image yourself, or any other placeholder. Additionally, the function will return any changes, such as spikes, step and trend changes, or dips. You can also use it to compare data by requesting two different time ranges, or for instance two different service versions.`, - parameters: { - type: 'object', - properties: { - start: { - type: 'string', - description: - 'The start of the time range, in Elasticsearch date math, like `now`.', - }, - end: { - type: 'string', - description: - 'The end of the time range, in Elasticsearch date math, like `now-24h`.', - }, - stats: { - type: 'array', - items: { - type: 'object', - properties: { - timeseries: { - description: 'The metric to be displayed', - oneOf: [ - { - type: 'object', - properties: { - name: { - type: 'string', - enum: [ - 'transaction_throughput', - 'transaction_failure_rate', - ], - }, - 'transaction.type': { - type: 'string', - description: 'The transaction type', - }, - }, - required: ['name'], - }, - { - type: 'object', - properties: { - name: { - type: 'string', - enum: [ - 'exit_span_throughput', - 'exit_span_failure_rate', - 'exit_span_latency', - ], - }, - 'span.destination.service.resource': { - type: 'string', - description: - 'The name of the downstream dependency for the service', - }, - }, - required: ['name'], - }, - { - type: 'object', - properties: { - name: { - type: 'string', - const: 'error_event_rate', - }, - }, - required: ['name'], - }, - { - type: 'object', - properties: { - name: { - type: 'string', - const: 'transaction_latency', - }, - 'transaction.type': { - type: 'string', - }, - function: { - type: 'string', - enum: ['avg', 'p95', 'p99'], - }, - }, - required: ['name', 'function'], - }, - ], - }, - 'service.name': { - ...NON_EMPTY_STRING, - description: 'The name of the service', - }, - 'service.environment': { - description: - 'The environment that the service is running in. If undefined, all environments will be included. Only use this if you have confirmed the environment that the service is running in.', - }, - filter: { - type: 'string', - description: - 'a KQL query to filter the data by. If no filter should be applied, leave it empty.', - }, - title: { - type: 'string', - description: - 'A unique, human readable, concise title for this specific group series.', - }, - offset: { - type: 'string', - description: - 'The offset. Right: 15m. 8h. 1d. Wrong: -15m. -8h. -1d.', - }, - }, - required: ['service.name', 'timeseries', 'title'], - }, - }, - }, - required: ['stats', 'start', 'end'], - } as const, - }, - async ({ arguments: { stats, start, end } }, signal) => { - const response = await callApmApi( - 'POST /internal/apm/assistant/get_apm_timeseries', - { - signal, - params: { - body: { stats: stats as any, start, end }, - }, - } - ); - - return response; - }, - ({ arguments: args, response }) => { - const groupedSeries = groupBy(response.data, (series) => series.group); - - const { - services: { uiSettings }, - } = useKibana(); - - const timeZone = getTimeZone(uiSettings); - - return ( - - - - {Object.values(groupedSeries).map((groupSeries) => { - const groupId = groupSeries[0].group; - - const maxY = getMaxY(groupSeries); - const latencyFormatter = getDurationFormatter(maxY, 10, 1000); - - let yLabelFormat: (value: number) => string; - - const firstStat = groupSeries[0].stat; - - switch (firstStat.timeseries.name) { - case 'transaction_throughput': - case 'exit_span_throughput': - case 'error_event_rate': - yLabelFormat = asTransactionRate; - break; - - case 'transaction_latency': - case 'exit_span_latency': - yLabelFormat = - getResponseTimeTickFormatter(latencyFormatter); - break; - - case 'transaction_failure_rate': - case 'exit_span_failure_rate': - yLabelFormat = (y) => asPercent(y || 0, 100); - break; - } - - const timeseries: Array> = - groupSeries.map((series): TimeSeries => { - let chartType: ChartType; - - const data = series.data; - - switch (series.stat.timeseries.name) { - case 'transaction_throughput': - case 'exit_span_throughput': - chartType = ChartType.THROUGHPUT; - break; - - case 'transaction_failure_rate': - case 'exit_span_failure_rate': - chartType = ChartType.FAILED_TRANSACTION_RATE; - break; - - case 'transaction_latency': - if ( - series.stat.timeseries.function === - LatencyAggregationType.p99 - ) { - chartType = ChartType.LATENCY_P99; - } else if ( - series.stat.timeseries.function === - LatencyAggregationType.p95 - ) { - chartType = ChartType.LATENCY_P95; - } else { - chartType = ChartType.LATENCY_AVG; - } - break; - - case 'exit_span_latency': + registerRenderFunction('get_apm_timeseries', (parameters) => { + const { response } = parameters as Parameters< + RenderFunction< + GetApmTimeseriesFunctionArguments, + GetApmTimeseriesFunctionResponse + > + >[0]; + + const groupedSeries = groupBy(response.data, (series) => series.group); + + const { + services: { uiSettings }, + } = useKibana(); + + const timeZone = getTimeZone(uiSettings); + + return ( + + + + {Object.values(groupedSeries).map((groupSeries) => { + const groupId = groupSeries[0].group; + + const maxY = getMaxY(groupSeries); + const latencyFormatter = getDurationFormatter(maxY, 10, 1000); + + let yLabelFormat: (value: number) => string; + + const firstStat = groupSeries[0].stat; + + switch (firstStat.timeseries.name) { + case 'transaction_throughput': + case 'exit_span_throughput': + case 'error_event_rate': + yLabelFormat = asTransactionRate; + break; + + case 'transaction_latency': + case 'exit_span_latency': + yLabelFormat = getResponseTimeTickFormatter(latencyFormatter); + break; + + case 'transaction_failure_rate': + case 'exit_span_failure_rate': + yLabelFormat = (y) => asPercent(y || 0, 100); + break; + } + + const timeseries: Array> = groupSeries.map( + (series): TimeSeries => { + let chartType: ChartType; + + const data = series.data; + + switch (series.stat.timeseries.name) { + case 'transaction_throughput': + case 'exit_span_throughput': + chartType = ChartType.THROUGHPUT; + break; + + case 'transaction_failure_rate': + case 'exit_span_failure_rate': + chartType = ChartType.FAILED_TRANSACTION_RATE; + break; + + case 'transaction_latency': + if ( + series.stat.timeseries.function === + LatencyAggregationType.p99 + ) { + chartType = ChartType.LATENCY_P99; + } else if ( + series.stat.timeseries.function === + LatencyAggregationType.p95 + ) { + chartType = ChartType.LATENCY_P95; + } else { chartType = ChartType.LATENCY_AVG; - break; - - case 'error_event_rate': - chartType = ChartType.ERROR_OCCURRENCES; - break; - } - - return { - title: series.id, - type: 'line', - color: getTimeSeriesColor(chartType!).currentPeriodColor, - data, - }; - }); - - return ( - - - - {groupId} - - - - - ); - })} - - - - ); - } - ); + } + break; + + case 'exit_span_latency': + chartType = ChartType.LATENCY_AVG; + break; + + case 'error_event_rate': + chartType = ChartType.ERROR_OCCURRENCES; + break; + } + + return { + title: series.id, + type: 'line', + color: getTimeSeriesColor(chartType!).currentPeriodColor, + data, + }; + } + ); + + return ( + + + + {groupId} + + + + + ); + })} + + + + ); + }); } diff --git a/x-pack/plugins/apm/public/assistant_functions/index.ts b/x-pack/plugins/apm/public/assistant_functions/index.ts index 128091cf2472d..773d2fbfba27f 100644 --- a/x-pack/plugins/apm/public/assistant_functions/index.ts +++ b/x-pack/plugins/apm/public/assistant_functions/index.ts @@ -5,152 +5,15 @@ * 2.0. */ -import type { CoreStart } from '@kbn/core/public'; -import type { - RegisterContextDefinition, - RegisterFunctionDefinition, -} from '@kbn/observability-ai-assistant-plugin/common/types'; -import type { ApmPluginStartDeps } from '../plugin'; -import { - createCallApmApi, - callApmApi, -} from '../services/rest/create_call_apm_api'; -import { registerGetApmCorrelationsFunction } from './get_apm_correlations'; -import { registerGetApmDownstreamDependenciesFunction } from './get_apm_downstream_dependencies'; -import { registerGetApmErrorDocumentFunction } from './get_apm_error_document'; -import { registerGetApmServicesListFunction } from './get_apm_services_list'; -import { registerGetApmServiceSummaryFunction } from './get_apm_service_summary'; +import { RegisterRenderFunctionDefinition } from '@kbn/observability-ai-assistant-plugin/public/types'; import { registerGetApmTimeseriesFunction } from './get_apm_timeseries'; export async function registerAssistantFunctions({ - pluginsStart, - coreStart, - registerContext, - registerFunction, - signal, + registerRenderFunction, }: { - pluginsStart: ApmPluginStartDeps; - coreStart: CoreStart; - registerContext: RegisterContextDefinition; - registerFunction: RegisterFunctionDefinition; - signal: AbortSignal; + registerRenderFunction: RegisterRenderFunctionDefinition; }) { - createCallApmApi(coreStart); - - const response = await callApmApi('GET /internal/apm/has_data', { - signal, - }); - - if (!response.hasData) { - return; - } - registerGetApmTimeseriesFunction({ - registerFunction, - }); - - registerGetApmErrorDocumentFunction({ - registerFunction, - }); - - registerGetApmCorrelationsFunction({ - registerFunction, - }); - - registerGetApmDownstreamDependenciesFunction({ - registerFunction, - }); - - registerGetApmServiceSummaryFunction({ - registerFunction, - }); - - registerGetApmServicesListFunction({ - registerFunction, - }); - - registerContext({ - name: 'apm', - description: ` - When analyzing APM data, prefer the APM specific functions over the generic Lens, - Elasticsearch or Kibana ones, unless those are explicitly requested by the user. - - When requesting metrics for a service, make sure you also know what environment - it is running in. Metrics aggregated over multiple environments are useless. - - There are four important data types in Elastic APM. Each of them have the - following fields: - - service.name: the name of the service - - service.node.name: the id of the service instance (often the hostname) - - service.environment: the environment (often production, development) - - agent.name: the name of the agent (go, java, etc) - - The four data types are transactions, exit spans, error events, and application - metrics. - - Transactions have three metrics: throughput, failure rate, and latency. The - fields are: - - - transaction.type: often request or page-load (the main transaction types), - but can also be worker, or route-change. - - transaction.name: The name of the transaction group, often something like - 'GET /api/product/:productId' - - transaction.result: The result. Used to capture HTTP response codes - (2xx,3xx,4xx,5xx) for request transactions. - - event.outcome: whether the transaction was succesful or not. success, - failure, or unknown. - - Exit spans have three metrics: throughput, failure rate and latency. The fields - are: - - span.type: db, external - - span.subtype: the type of database (redis, postgres) or protocol (http, grpc) - - span.destination.service.resource: the address of the destination of the call - - event.outcome: whether the transaction was succesful or not. success, - failure, or unknown. - - Error events have one metric, error event rate. The fields are: - - error.grouping_name: a human readable keyword that identifies the error group - - For transaction metrics we also collect anomalies. These are scored 0 (low) to - 100 (critical). - - For root cause analysis, locate a change point in the relevant metrics for a - service or downstream dependency. You can locate a change point by using a - sliding window, e.g. start with a small time range, like 30m, and make it - bigger until you identify a change point. It's very important to identify a - change point. If you don't have a change point, ask the user for next steps. - You can also use an anomaly or a deployment as a change point. Then, compare - data before the change with data after the change. You can either use the - groupBy parameter in get_apm_chart to get the most occuring values in a certain - data set, or you can use correlations to see for which field and value the - frequency has changed when comparing the foreground set to the background set. - This is useful when comparing data from before the change point with after the - change point. For instance, you might see a specific error pop up more often - after the change point. - - When comparing anomalies and changes in timeseries, first, zoom in to a smaller - time window, at least 30 minutes before and 30 minutes after the change - occured. E.g., if the anomaly occured at 2023-07-05T08:15:00.000Z, request a - time window that starts at 2023-07-05T07:45:00.000Z and ends at - 2023-07-05T08:45:00.000Z. When comparing changes in different timeseries and - anomalies to determine a correlation, make sure to compare the timestamps. If - in doubt, rate the likelihood of them being related, given the time difference, - between 1 and 10. If below 5, assume it's not related. Mention this likelihood - (and the time difference) to the user. - - Your goal is to help the user determine the root cause of an issue quickly and - transparently. If you see a change or - anomaly in a metric for a service, try to find similar changes in the metrics - for the traffic to its downstream dependencies, by comparing transaction - metrics to span metrics. To inspect the traffic from one service to a - downstream dependency, first get the downstream dependencies for a service, - then get the span metrics from that service (\`service.name\`) to its - downstream dependency (\`span.destination.service.resource\`). For instance, - for an anomaly in throughput, first inspect \`transaction_throughput\` for - \`service.name\`. Then, inspect \`exit_span_throughput\` for its downstream - dependencies, by grouping by \`span.destination.service.resource\`. Repeat this - process over the next service its downstream dependencies until you identify a - root cause. If you can not find any similar changes, use correlations or - grouping to find attributes that could be causes for the change.`, + registerRenderFunction, }); } diff --git a/x-pack/plugins/apm/public/components/app/error_group_details/distribution/index.tsx b/x-pack/plugins/apm/public/components/app/error_group_details/distribution/index.tsx index 2c4c9eec27e26..b79f30505e5db 100644 --- a/x-pack/plugins/apm/public/components/app/error_group_details/distribution/index.tsx +++ b/x-pack/plugins/apm/public/components/app/error_group_details/distribution/index.tsx @@ -14,12 +14,10 @@ import { ScaleType, Settings, Tooltip, + LIGHT_THEME, + DARK_THEME, } from '@elastic/charts'; import { EuiTitle } from '@elastic/eui'; -import { - EUI_CHARTS_THEME_DARK, - EUI_CHARTS_THEME_LIGHT, -} from '@elastic/eui/dist/eui_charts_theme'; import { i18n } from '@kbn/i18n'; import React from 'react'; import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; @@ -101,11 +99,7 @@ export function ErrorDistribution({ distribution, title, fetchStatus }: Props) { showLegend showLegendExtra legendPosition={Position.Bottom} - theme={ - theme.darkMode - ? EUI_CHARTS_THEME_DARK.theme - : EUI_CHARTS_THEME_LIGHT.theme - } + theme={theme.darkMode ? DARK_THEME : LIGHT_THEME} locale={i18n.getLocale()} /> - diff --git a/x-pack/plugins/apm/public/components/app/mobile/charts/mobile_errors_and_crashes_treemap/index.tsx b/x-pack/plugins/apm/public/components/app/mobile/charts/mobile_errors_and_crashes_treemap/index.tsx index a30495b703eb7..014a1abb62910 100644 --- a/x-pack/plugins/apm/public/components/app/mobile/charts/mobile_errors_and_crashes_treemap/index.tsx +++ b/x-pack/plugins/apm/public/components/app/mobile/charts/mobile_errors_and_crashes_treemap/index.tsx @@ -6,10 +6,10 @@ */ import React, { useState } from 'react'; -import { EuiSpacer } from '@elastic/eui'; +import { EuiPanel, EuiProgress, EuiSpacer } from '@elastic/eui'; import { TreemapSelect, TreemapTypes } from './treemap_select'; import { TreemapChart } from '../../../../shared/charts/treemap_chart'; -import { useFetcher } from '../../../../../hooks/use_fetcher'; +import { useFetcher, isPending } from '../../../../../hooks/use_fetcher'; import { DEVICE_MODEL_IDENTIFIER, SERVICE_VERSION, @@ -62,7 +62,10 @@ export function MobileErrorsAndCrashesTreemap({ [environment, kuery, serviceName, start, end, selectedTreemap] ); return ( - <> + + {isPending(status) && ( + + )} - + ); } diff --git a/x-pack/plugins/apm/public/components/app/mobile/charts/mobile_http_error_rate/index.tsx b/x-pack/plugins/apm/public/components/app/mobile/charts/mobile_http_error_rate/index.tsx index f26fe41a0ecab..218d4a67ede40 100644 --- a/x-pack/plugins/apm/public/components/app/mobile/charts/mobile_http_error_rate/index.tsx +++ b/x-pack/plugins/apm/public/components/app/mobile/charts/mobile_http_error_rate/index.tsx @@ -11,13 +11,13 @@ import { EuiIconTip, EuiFlexItem, EuiFlexGroup, + EuiProgress, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; import { getComparisonChartTheme } from '../../../../shared/time_comparison/get_comparison_chart_theme'; import { TimeseriesChartWithContext } from '../../../../shared/charts/timeseries_chart_with_context'; - -import { useFetcher } from '../../../../../hooks/use_fetcher'; +import { isPending, useFetcher } from '../../../../../hooks/use_fetcher'; import { ChartType, @@ -100,7 +100,10 @@ export function HttpErrorRateChart({ ]; return ( - + + {isPending(status) && ( + + )} diff --git a/x-pack/plugins/apm/public/components/app/mobile/errors_and_crashes_group_details/crash_group_details/index.tsx b/x-pack/plugins/apm/public/components/app/mobile/errors_and_crashes_group_details/crash_group_details/index.tsx index 4c80fe922973e..a50cb7f9423ca 100644 --- a/x-pack/plugins/apm/public/components/app/mobile/errors_and_crashes_group_details/crash_group_details/index.tsx +++ b/x-pack/plugins/apm/public/components/app/mobile/errors_and_crashes_group_details/crash_group_details/index.tsx @@ -9,7 +9,6 @@ import { EuiBadge, EuiFlexGroup, EuiFlexItem, - EuiPanel, EuiSpacer, EuiTitle, } from '@elastic/eui'; @@ -219,46 +218,39 @@ export function CrashGroupDetails() { return ( <> - - - - - + + - - - + - - - - + + diff --git a/x-pack/plugins/apm/public/components/app/mobile/errors_and_crashes_group_details/error_group_details/index.tsx b/x-pack/plugins/apm/public/components/app/mobile/errors_and_crashes_group_details/error_group_details/index.tsx index eb71f5c04ea37..ea03b6cc58ac8 100644 --- a/x-pack/plugins/apm/public/components/app/mobile/errors_and_crashes_group_details/error_group_details/index.tsx +++ b/x-pack/plugins/apm/public/components/app/mobile/errors_and_crashes_group_details/error_group_details/index.tsx @@ -9,7 +9,6 @@ import { EuiBadge, EuiFlexGroup, EuiFlexItem, - EuiPanel, EuiSpacer, EuiTitle, } from '@elastic/eui'; @@ -218,48 +217,41 @@ export function ErrorGroupDetails() { return ( <> - - - - - + + - - - + - - - - + + diff --git a/x-pack/plugins/apm/public/components/app/mobile/errors_and_crashes_group_details/shared/distribution/index.tsx b/x-pack/plugins/apm/public/components/app/mobile/errors_and_crashes_group_details/shared/distribution/index.tsx index 78e39d3f2f8f6..07ce24e212fea 100644 --- a/x-pack/plugins/apm/public/components/app/mobile/errors_and_crashes_group_details/shared/distribution/index.tsx +++ b/x-pack/plugins/apm/public/components/app/mobile/errors_and_crashes_group_details/shared/distribution/index.tsx @@ -5,11 +5,18 @@ * 2.0. */ -import { EuiTitle, EuiIconTip, EuiFlexItem, EuiFlexGroup } from '@elastic/eui'; +import { + EuiTitle, + EuiIconTip, + EuiFlexItem, + EuiFlexGroup, + EuiPanel, + EuiProgress, +} from '@elastic/eui'; import React from 'react'; import { TimeseriesChartWithContext } from '../../../../../shared/charts/timeseries_chart_with_context'; import { useLegacyUrlParams } from '../../../../../../context/url_params_context/use_url_params'; -import { FETCH_STATUS } from '../../../../../../hooks/use_fetcher'; +import { FETCH_STATUS, isPending } from '../../../../../../hooks/use_fetcher'; import { usePreviousPeriodLabel } from '../../../../../../hooks/use_previous_period_text'; import { APIReturnType } from '../../../../../../services/rest/create_call_apm_api'; import { getComparisonChartTheme } from '../../../../../shared/time_comparison/get_comparison_chart_theme'; @@ -66,8 +73,11 @@ export function ErrorDistribution({ const comparisonChartTheme = getComparisonChartTheme(); return ( - <> - + + {isPending(fetchStatus) && ( + + )} +

{title}

@@ -87,6 +97,6 @@ export function ErrorDistribution({ timeseries={timeseries} customTheme={comparisonChartTheme} /> - +
); } diff --git a/x-pack/plugins/apm/public/components/app/mobile/errors_and_crashes_overview/crashes_overview.tsx b/x-pack/plugins/apm/public/components/app/mobile/errors_and_crashes_overview/crashes_overview.tsx index 9ebd3a75e44bb..d12e9525d1de0 100644 --- a/x-pack/plugins/apm/public/components/app/mobile/errors_and_crashes_overview/crashes_overview.tsx +++ b/x-pack/plugins/apm/public/components/app/mobile/errors_and_crashes_overview/crashes_overview.tsx @@ -201,37 +201,33 @@ export function MobileCrashesOverview() { - - - +
- - - +
diff --git a/x-pack/plugins/apm/public/components/app/mobile/errors_and_crashes_overview/errors_overview.tsx b/x-pack/plugins/apm/public/components/app/mobile/errors_and_crashes_overview/errors_overview.tsx index 56d3ee35fc32c..67214733bece4 100644 --- a/x-pack/plugins/apm/public/components/app/mobile/errors_and_crashes_overview/errors_overview.tsx +++ b/x-pack/plugins/apm/public/components/app/mobile/errors_and_crashes_overview/errors_overview.tsx @@ -197,23 +197,21 @@ export function MobileErrorsOverview() { - - - + - - - + diff --git a/x-pack/plugins/apm/public/components/app/mobile/errors_and_crashes_overview/index.tsx b/x-pack/plugins/apm/public/components/app/mobile/errors_and_crashes_overview/index.tsx index 539a0efc01709..9d2c9802c37ab 100644 --- a/x-pack/plugins/apm/public/components/app/mobile/errors_and_crashes_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/mobile/errors_and_crashes_overview/index.tsx @@ -5,7 +5,7 @@ * 2.0. */ import React from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { useHistory } from 'react-router-dom'; import { Tabs, MobileErrorTabIds } from './tabs/tabs'; import { useApmParams } from '../../../../hooks/use_apm_params'; @@ -18,7 +18,6 @@ export function MobileErrorCrashesOverview() { const history = useHistory(); return ( - { diff --git a/x-pack/plugins/apm/public/components/app/mobile/search_bar.tsx b/x-pack/plugins/apm/public/components/app/mobile/search_bar.tsx index 5c22e2dfb9974..df86277555a8b 100644 --- a/x-pack/plugins/apm/public/components/app/mobile/search_bar.tsx +++ b/x-pack/plugins/apm/public/components/app/mobile/search_bar.tsx @@ -62,7 +62,7 @@ export function MobileSearchBar({ )} - + )} + ); } diff --git a/x-pack/plugins/apm/public/components/app/mobile/service_overview/geo_map/embedded_map.test.tsx b/x-pack/plugins/apm/public/components/app/mobile/service_overview/geo_map/embedded_map.test.tsx index 88e94ef82a61c..82bbd07ba8588 100644 --- a/x-pack/plugins/apm/public/components/app/mobile/service_overview/geo_map/embedded_map.test.tsx +++ b/x-pack/plugins/apm/public/components/app/mobile/service_overview/geo_map/embedded_map.test.tsx @@ -31,6 +31,12 @@ describe('Embedded Map', () => { }), })); + const mockSpaces = { + getActiveSpace: jest + .fn() + .mockImplementation(() => ({ id: 'mockSpaceId' })), + }; + const { findByTestId } = render( { ]} > - + { const setLayerList = async () => { - if (embeddable && !isErrorEmbeddable(embeddable)) { + if (embeddable && !isErrorEmbeddable(embeddable) && dataViewId) { const layerList = await getLayerList({ selectedMap, maps, dataViewId }); await Promise.all([ embeddable.setLayerList(layerList), 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 b4c68a14b07c3..28cecf554142a 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 @@ -9,7 +9,6 @@ import React from 'react'; import { EuiFlexGroupProps, EuiFlexGroup, - EuiHorizontalRule, EuiFlexItem, EuiLink, EuiPanel, @@ -106,9 +105,6 @@ export function MobileServiceOverview() { > - - - - + - + + {metrics.map((metric, key) => ( diff --git a/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/index.tsx b/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/index.tsx index ce06e04683af9..1f120feda97fd 100644 --- a/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/mobile/transaction_overview/index.tsx @@ -5,12 +5,7 @@ * 2.0. */ -import { - EuiFlexItem, - EuiHorizontalRule, - EuiPanel, - EuiSpacer, -} from '@elastic/eui'; +import { EuiFlexItem, EuiPanel, EuiSpacer } from '@elastic/eui'; import React from 'react'; import { useHistory } from 'react-router-dom'; import { useApmServiceContext } from '../../../../context/apm_service/use_apm_service_context'; @@ -62,9 +57,6 @@ export function MobileTransactionOverview() { return ( <> - - - ), @@ -99,12 +101,23 @@ export function ProfilingOverview() { endIndex={10} dataSource={preferred?.source} kuery={kuery} + rangeFrom={rangeFrom} + rangeTo={rangeTo} /> ), }, ]; - }, [end, environment, kuery, preferred?.source, serviceName, start]); + }, [ + end, + environment, + kuery, + preferred?.source, + rangeFrom, + rangeTo, + serviceName, + start, + ]); if (isLoading) { return ( diff --git a/x-pack/plugins/apm/public/components/app/profiling_overview/profiling_flamegraph.tsx b/x-pack/plugins/apm/public/components/app/profiling_overview/profiling_flamegraph.tsx index 8dcda14783a8f..bbae54abdf708 100644 --- a/x-pack/plugins/apm/public/components/app/profiling_overview/profiling_flamegraph.tsx +++ b/x-pack/plugins/apm/public/components/app/profiling_overview/profiling_flamegraph.tsx @@ -40,6 +40,8 @@ interface Props { ApmDocumentType.TransactionMetric | ApmDocumentType.TransactionEvent >; kuery: string; + rangeFrom: string; + rangeTo: string; } export function ProfilingFlamegraph({ @@ -49,6 +51,8 @@ export function ProfilingFlamegraph({ environment, dataSource, kuery, + rangeFrom, + rangeTo, }: Props) { const { profilingLocators } = useProfilingPlugin(); @@ -93,6 +97,8 @@ export function ProfilingFlamegraph({ data-test-subj="apmProfilingFlamegraphGoToFlamegraphLink" href={profilingLocators?.flamegraphLocator.getRedirectUrl({ kuery: mergeKueries([`(${hostNamesKueryFormat})`, kuery]), + rangeFrom, + rangeTo, })} > {i18n.translate('xpack.apm.profiling.flamegraph.link', { diff --git a/x-pack/plugins/apm/public/components/app/profiling_overview/profiling_top_functions.tsx b/x-pack/plugins/apm/public/components/app/profiling_overview/profiling_top_functions.tsx index 7b428802fbfc1..0462af188d3f9 100644 --- a/x-pack/plugins/apm/public/components/app/profiling_overview/profiling_top_functions.tsx +++ b/x-pack/plugins/apm/public/components/app/profiling_overview/profiling_top_functions.tsx @@ -31,6 +31,8 @@ interface Props { ApmDocumentType.TransactionMetric | ApmDocumentType.TransactionEvent >; kuery: string; + rangeFrom: string; + rangeTo: string; } export function ProfilingTopNFunctions({ @@ -42,6 +44,8 @@ export function ProfilingTopNFunctions({ endIndex, dataSource, kuery, + rangeFrom, + rangeTo, }: Props) { const { profilingLocators } = useProfilingPlugin(); @@ -97,6 +101,8 @@ export function ProfilingTopNFunctions({ data-test-subj="apmProfilingTopNFunctionsGoToUniversalProfilingFlamegraphLink" href={profilingLocators?.topNFunctionsLocator.getRedirectUrl({ kuery: mergeKueries([`(${hostNamesKueryFormat})`, kuery]), + rangeFrom, + rangeTo, })} > {i18n.translate('xpack.apm.profiling.topnFunctions.link', { diff --git a/x-pack/plugins/apm/public/components/app/storage_explorer/services_table/storage_details_per_service.tsx b/x-pack/plugins/apm/public/components/app/storage_explorer/services_table/storage_details_per_service.tsx index 96c28caa3134d..62a4aa6a1f38c 100644 --- a/x-pack/plugins/apm/public/components/app/storage_explorer/services_table/storage_details_per_service.tsx +++ b/x-pack/plugins/apm/public/components/app/storage_explorer/services_table/storage_details_per_service.tsx @@ -17,7 +17,7 @@ import { EuiFlexGrid, EuiSpacer, } from '@elastic/eui'; -import { useChartTheme } from '@kbn/observability-shared-plugin/public'; +import { useChartThemes } from '@kbn/observability-shared-plugin/public'; import { Chart, Partition, @@ -80,7 +80,7 @@ export function StorageDetailsPerService({ indexLifecyclePhase, }: Props) { const { core } = useApmPluginContext(); - const chartTheme = useChartTheme(); + const chartThemes = useChartThemes(); const router = useApmRouter(); const { euiTheme } = useEuiTheme(); @@ -202,8 +202,9 @@ export function StorageDetailsPerService({ emptySizeRatio: 0.3, }, }, - ...chartTheme, + ...chartThemes.theme, ]} + baseTheme={chartThemes.baseTheme} locale={i18n.getLocale()} showLegend /> diff --git a/x-pack/plugins/apm/public/components/app/storage_explorer/storage_chart.tsx b/x-pack/plugins/apm/public/components/app/storage_explorer/storage_chart.tsx index e0a3eec1eb228..98f130c71d461 100644 --- a/x-pack/plugins/apm/public/components/app/storage_explorer/storage_chart.tsx +++ b/x-pack/plugins/apm/public/components/app/storage_explorer/storage_chart.tsx @@ -15,7 +15,7 @@ import { ScaleType, Settings, } from '@elastic/charts'; -import { useChartTheme } from '@kbn/observability-shared-plugin/public'; +import { useChartThemes } from '@kbn/observability-shared-plugin/public'; import { i18n } from '@kbn/i18n'; import { useProgressiveFetcher } from '../../../hooks/use_progressive_fetcher'; import { useTimeRange } from '../../../hooks/use_time_range'; @@ -29,7 +29,7 @@ import { asDynamicBytes } from '../../../../common/utils/formatters'; export function StorageChart() { const { core } = useApmPluginContext(); - const chartTheme = useChartTheme(); + const chartThemes = useChartThemes(); const euiPaletteColorBlindRotations = 3; const groupedPalette = euiPaletteColorBlind({ @@ -99,8 +99,9 @@ export function StorageChart() { area: { opacity: 1 }, }, }, - ...chartTheme, + ...chartThemes.theme, ]} + baseTheme={chartThemes.baseTheme} showLegend legendPosition={Position.Right} locale={i18n.getLocale()} diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/index.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/index.tsx index abd6aee61f407..aa2ea7388ffd6 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/index.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/index.tsx @@ -17,7 +17,7 @@ import { WaterfallLegendType, } from './waterfall/waterfall_helpers/waterfall_helpers'; import { WaterfallLegends } from './waterfall_legends'; -import { MissingTransactionWarning } from './waterfall/missing_transaction_warning'; +import { OrphanTraceItemsWarning } from './waterfall/orphan_trace_items_warning'; interface Props { waterfallItemId?: string; @@ -39,7 +39,7 @@ export function WaterfallContainer({ if (!waterfall) { return null; } - const { legends, items, hasOrphanTraceItems } = waterfall; + const { legends, items, orphanTraceItemsCount } = waterfall; // Service colors are needed to color the dot in the error popover const serviceLegends = legends.filter( @@ -116,9 +116,11 @@ export function WaterfallContainer({ type={colorBy} /> - {hasOrphanTraceItems ? ( + {orphanTraceItemsCount > 0 ? ( - + ) : null} diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/missing_transaction_warning.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/orphan_trace_items_warning.tsx similarity index 71% rename from x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/missing_transaction_warning.tsx rename to x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/orphan_trace_items_warning.tsx index 62d1d05bca6c2..c3554e2c84c40 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/missing_transaction_warning.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/orphan_trace_items_warning.tsx @@ -9,7 +9,11 @@ import React from 'react'; import { EuiBadge, EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -export function MissingTransactionWarning() { +export function OrphanTraceItemsWarning({ + orphanTraceItemsCount, +}: { + orphanTraceItemsCount: number; +}) { return ( ), }, - ...(!isEmpty(stackframes) + ...(!isEmpty(stackframes) || !isEmpty(plaintextStacktrace) ? [ { id: 'stack-trace', + 'data-test-subj': 'spanStacktraceTab', name: i18n.translate( 'xpack.apm.transactionDetails.spanFlyout.stackTraceTabLabel', { @@ -245,10 +248,17 @@ function SpanFlyoutBody({ content: ( - + {stackframes ? ( + + ) : ( + + )} ), }, diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/waterfall_helpers/waterfall_helpers.test.ts b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/waterfall_helpers/waterfall_helpers.test.ts index 208102b415e57..6c55d7917e12b 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/waterfall_helpers/waterfall_helpers.test.ts +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/waterfall_helpers/waterfall_helpers.test.ts @@ -16,7 +16,7 @@ import { IWaterfallTransaction, IWaterfallError, IWaterfallSpanOrTransaction, - getHasOrphanTraceItems, + getOrphanTraceItemsCount, } from './waterfall_helpers'; import { APMError } from '../../../../../../../../typings/es_schemas/ui/apm_error'; import { @@ -719,7 +719,7 @@ describe('waterfall_helpers', () => { }); }); - describe('getHasOrphanTraceItems', () => { + describe('getOrphanTraceItemsCount', () => { const myTransactionItem = { processor: { event: 'transaction' }, trace: { id: 'myTrace' }, @@ -728,7 +728,7 @@ describe('waterfall_helpers', () => { }, } as WaterfallTransaction; - it('should return false if there are no orphan items', () => { + it('should return missing items count: 0 if there are no orphan items', () => { const traceItems: Array = [ myTransactionItem, { @@ -741,10 +741,10 @@ describe('waterfall_helpers', () => { }, } as WaterfallSpan, ]; - expect(getHasOrphanTraceItems(traceItems)).toBe(false); + expect(getOrphanTraceItemsCount(traceItems)).toBe(0); }); - it('should return true if there are orphan items', () => { + it('should return missing items count if there are orphan items', () => { const traceItems: Array = [ myTransactionItem, { @@ -757,7 +757,7 @@ describe('waterfall_helpers', () => { }, } as WaterfallSpan, ]; - expect(getHasOrphanTraceItems(traceItems)).toBe(true); + expect(getOrphanTraceItemsCount(traceItems)).toBe(1); }); }); }); diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/waterfall_helpers/waterfall_helpers.ts b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/waterfall_helpers/waterfall_helpers.ts index b197859ff1083..7152cecc3f805 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/waterfall_helpers/waterfall_helpers.ts +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall/waterfall_helpers/waterfall_helpers.ts @@ -48,7 +48,7 @@ export interface IWaterfall { totalErrorsCount: number; traceDocsTotal: number; maxTraceItems: number; - hasOrphanTraceItems: boolean; + orphanTraceItemsCount: number; } interface IWaterfallItemBase { @@ -416,7 +416,7 @@ function getErrorCountByParentId( }, {}); } -export const getHasOrphanTraceItems = ( +export const getOrphanTraceItemsCount = ( traceDocs: Array ) => { const waterfallItemsIds = new Set( @@ -427,9 +427,13 @@ export const getHasOrphanTraceItems = ( ) ); - return traceDocs.some( - (item) => item.parent?.id && !waterfallItemsIds.has(item.parent.id) - ); + let missingTraceItemsCounter = 0; + traceDocs.some((item) => { + if (item.parent?.id && !waterfallItemsIds.has(item.parent.id)) { + missingTraceItemsCounter++; + } + }); + return missingTraceItemsCounter; }; export function getWaterfall(apiResponse: TraceAPIResponse): IWaterfall { @@ -446,7 +450,7 @@ export function getWaterfall(apiResponse: TraceAPIResponse): IWaterfall { totalErrorsCount: 0, traceDocsTotal: 0, maxTraceItems: 0, - hasOrphanTraceItems: false, + orphanTraceItemsCount: 0, }; } @@ -482,7 +486,7 @@ export function getWaterfall(apiResponse: TraceAPIResponse): IWaterfall { const duration = getWaterfallDuration(items); const legends = getLegends(items); - const hasOrphanTraceItems = getHasOrphanTraceItems(traceItems.traceDocs); + const orphanTraceItemsCount = getOrphanTraceItemsCount(traceItems.traceDocs); return { entryWaterfallTransaction, @@ -498,6 +502,6 @@ export function getWaterfall(apiResponse: TraceAPIResponse): IWaterfall { totalErrorsCount: traceItems.errorDocs.length, traceDocsTotal: traceItems.traceDocsTotal, maxTraceItems: traceItems.maxTraceItems, - hasOrphanTraceItems, + orphanTraceItemsCount, }; } diff --git a/x-pack/plugins/apm/public/components/routing/app_root/apm_header_action_menu/index.tsx b/x-pack/plugins/apm/public/components/routing/app_root/apm_header_action_menu/index.tsx index fefcbcaf3bb54..9be079fe43c52 100644 --- a/x-pack/plugins/apm/public/components/routing/app_root/apm_header_action_menu/index.tsx +++ b/x-pack/plugins/apm/public/components/routing/app_root/apm_header_action_menu/index.tsx @@ -14,7 +14,6 @@ import { import { apmLabsButton } from '@kbn/observability-plugin/common'; import { i18n } from '@kbn/i18n'; import React from 'react'; -import { ObservabilityAIAssistantActionMenuItem } from '@kbn/observability-ai-assistant-plugin/public'; import { getAlertingCapabilities } from '../../../alerting/utils/get_alerting_capabilities'; import { getLegacyApmHref } from '../../../shared/links/apm/apm_link'; import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; @@ -24,7 +23,12 @@ import { InspectorHeaderLink } from './inspector_header_link'; import { Labs } from './labs'; export function ApmHeaderActionMenu() { - const { core, plugins, config } = useApmPluginContext(); + const { + core, + plugins, + config, + observabilityAIAssistant: { ObservabilityAIAssistantActionMenuItem }, + } = useApmPluginContext(); const { search } = window.location; const { application, http } = core; const { basePath } = http; @@ -97,7 +101,9 @@ export function ApmHeaderActionMenu() { })} - + {ObservabilityAIAssistantActionMenuItem ? ( + + ) : null} ); } diff --git a/x-pack/plugins/apm/public/components/routing/app_root/index.tsx b/x-pack/plugins/apm/public/components/routing/app_root/index.tsx index 8021fe3c6c88e..85df672363ce1 100644 --- a/x-pack/plugins/apm/public/components/routing/app_root/index.tsx +++ b/x-pack/plugins/apm/public/components/routing/app_root/index.tsx @@ -12,7 +12,6 @@ import { } from '@kbn/kibana-react-plugin/public'; import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; import { Storage } from '@kbn/kibana-utils-plugin/public'; -import { ObservabilityAIAssistantProvider } from '@kbn/observability-ai-assistant-plugin/public'; import { HeaderMenuPortal, InspectorContextProvider, @@ -75,48 +74,44 @@ export function ApmAppRoot({ services={{ ...core, ...pluginsStart, storage, ...apmServices }} > - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + diff --git a/x-pack/plugins/apm/public/components/shared/charts/breakdown_chart/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/breakdown_chart/index.tsx index c215c50c9e327..89e409aacd2f9 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/breakdown_chart/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/breakdown_chart/index.tsx @@ -25,7 +25,7 @@ import { i18n } from '@kbn/i18n'; import moment from 'moment'; import React from 'react'; import { useHistory } from 'react-router-dom'; -import { useChartTheme } from '@kbn/observability-shared-plugin/public'; +import { useChartThemes } from '@kbn/observability-shared-plugin/public'; import { Annotation } from '../../../../../common/annotations'; import { asAbsoluteDateTime, @@ -70,7 +70,7 @@ export function BreakdownChart({ id, }: Props) { const history = useHistory(); - const chartTheme = useChartTheme(); + const chartThemes = useChartThemes(); const { core } = useApmPluginContext(); const { chartRef, updatePointerEvent } = useChartPointerEventContext(); const { @@ -115,7 +115,8 @@ export function BreakdownChart({ showLegend showLegendExtra legendPosition={Position.Bottom} - theme={chartTheme} + theme={chartThemes.theme} + baseTheme={chartThemes.baseTheme} xDomain={{ min, max }} flatLegend onPointerUpdate={updatePointerEvent} diff --git a/x-pack/plugins/apm/public/components/shared/charts/duration_distribution_chart/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/duration_distribution_chart/index.tsx index ac1b4251e83f8..9d34e2b8b3280 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/duration_distribution_chart/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/duration_distribution_chart/index.tsx @@ -29,7 +29,7 @@ import { euiPaletteColorBlind } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { useChartTheme } from '@kbn/observability-shared-plugin/public'; +import { useChartThemes } from '@kbn/observability-shared-plugin/public'; import { ProcessorEvent } from '@kbn/observability-plugin/common'; import { getDurationFormatter } from '../../../../../common/utils/formatters'; @@ -111,7 +111,7 @@ export function DurationDistributionChart({ status, eventType, }: DurationDistributionChartProps) { - const chartTheme = useChartTheme(); + const chartThemes = useChartThemes(); const euiTheme = useTheme(); const markerPercentile = DEFAULT_PERCENTILE_THRESHOLD; @@ -202,8 +202,9 @@ export function DurationDistributionChart({ }, }, }, - ...chartTheme, + ...chartThemes.theme, ]} + baseTheme={chartThemes.baseTheme} showLegend={true} legendPosition={Position.Bottom} onBrushEnd={onChartSelection} diff --git a/x-pack/plugins/apm/public/components/shared/charts/instances_latency_distribution_chart/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/instances_latency_distribution_chart/index.tsx index 02ab62585643a..c7f5ab023713c 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/instances_latency_distribution_chart/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/instances_latency_distribution_chart/index.tsx @@ -23,7 +23,7 @@ import { EuiPanel, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; import { useHistory } from 'react-router-dom'; -import { useChartTheme } from '@kbn/observability-shared-plugin/public'; +import { useChartThemes } from '@kbn/observability-shared-plugin/public'; import { usePreviousPeriodLabel } from '../../../../hooks/use_previous_period_text'; import { SERVICE_NODE_NAME } from '../../../../../common/es_fields/apm'; import { @@ -58,7 +58,7 @@ export function InstancesLatencyDistributionChart({ const hasData = items.length > 0; const theme = useTheme(); - const chartTheme = useChartTheme(); + const chartThemes = useChartThemes(); const maxLatency = Math.max(...items.map((item) => item.latency ?? 0)); const latencyFormatter = getDurationFormatter(maxLatency); @@ -129,7 +129,8 @@ export function InstancesLatencyDistributionChart({ legendPosition={Position.Bottom} onElementClick={handleElementClick} showLegend - theme={chartTheme} + theme={chartThemes.theme} + baseTheme={chartThemes.baseTheme} xDomain={xDomain} locale={i18n.getLocale()} /> diff --git a/x-pack/plugins/apm/public/components/shared/charts/spark_plot/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/spark_plot/index.tsx index 3375a8186e3f1..f7baf95c488ad 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/spark_plot/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/spark_plot/index.tsx @@ -23,7 +23,7 @@ import { EuiLoadingChart, } from '@elastic/eui'; import React from 'react'; -import { useChartTheme } from '@kbn/observability-shared-plugin/public'; +import { useChartThemes } from '@kbn/observability-shared-plugin/public'; import { i18n } from '@kbn/i18n'; import { Coordinate } from '../../../../../typings/timeseries'; import { useTheme } from '../../../../hooks/use_theme'; @@ -103,7 +103,7 @@ function SparkPlotItem({ comparisonSeriesColor?: string; }) { const theme = useTheme(); - const defaultChartTheme = useChartTheme(); + const defaultChartThemes = useChartThemes(); const comparisonChartTheme = getComparisonChartTheme(); const hasComparisonSeries = !!comparisonSeries?.length; @@ -142,7 +142,8 @@ function SparkPlotItem({ return ( diff --git a/x-pack/plugins/apm/public/components/shared/charts/timeseries_chart.tsx b/x-pack/plugins/apm/public/components/shared/charts/timeseries_chart.tsx index 1fd876d61b3d3..cc8f85f347ddd 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/timeseries_chart.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/timeseries_chart.tsx @@ -28,7 +28,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiIcon, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { ReactElement } from 'react'; import { useHistory } from 'react-router-dom'; -import { useChartTheme } from '@kbn/observability-shared-plugin/public'; +import { useChartThemes } from '@kbn/observability-shared-plugin/public'; import { isExpectedBoundsComparison } from '../time_comparison/get_comparison_options'; import { useChartPointerEventContext } from '../../../context/chart_pointer_event/use_chart_pointer_event_context'; @@ -74,7 +74,7 @@ export function TimeseriesChart({ const history = useHistory(); const { chartRef, updatePointerEvent } = useChartPointerEventContext(); const theme = useTheme(); - const chartTheme = useChartTheme(); + const chartThemes = useChartThemes(); const anomalyChartTimeseries = getChartAnomalyTimeseries({ anomalyTimeseries, theme, @@ -189,8 +189,9 @@ export function TimeseriesChart({ line: { visible: false }, }, }, - ...chartTheme, + ...chartThemes.theme, ]} + baseTheme={chartThemes.baseTheme} onPointerUpdate={updatePointerEvent} externalPointerEvents={{ tooltip: { visible: true }, diff --git a/x-pack/plugins/apm/public/components/shared/charts/transaction_charts/index.tsx b/x-pack/plugins/apm/public/components/shared/charts/transaction_charts/index.tsx index 3096a8541b84d..4d1a8544bb87f 100644 --- a/x-pack/plugins/apm/public/components/shared/charts/transaction_charts/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/charts/transaction_charts/index.tsx @@ -24,6 +24,7 @@ import { FailedTransactionRateChart } from '../failed_transaction_rate_chart'; import { TopErrors } from '../../../app/transaction_details/top_errors'; import { useBreakpoints } from '../../../../hooks/use_breakpoints'; import { + isMobileAgentName, isOpenTelemetryAgentName, isRumAgentName, } from '../../../../../common/agent_name'; @@ -55,6 +56,7 @@ export function TransactionCharts({ const { agentName } = useApmServiceContext(); const isOpenTelemetryAgent = isOpenTelemetryAgentName(agentName as AgentName); const isRumAgent = isRumAgentName(agentName as AgentName); + const isMobileAgent = isMobileAgentName(agentName as AgentName); const rowDirection = isLarge ? 'column' : 'row'; const latencyChart = ( @@ -112,7 +114,7 @@ export function TransactionCharts({ {latencyChart} {serviceOverviewThroughputChart} - {coldStartRateOrBreakdownChart} + {!isMobileAgent && coldStartRateOrBreakdownChart} {}} locale={i18n.getLocale()} /> diff --git a/x-pack/plugins/apm/public/components/shared/links/discover_links/discover_link.tsx b/x-pack/plugins/apm/public/components/shared/links/discover_links/discover_link.tsx index 1f066d1322eb6..987fe5b23de52 100644 --- a/x-pack/plugins/apm/public/components/shared/links/discover_links/discover_link.tsx +++ b/x-pack/plugins/apm/public/components/shared/links/discover_links/discover_link.tsx @@ -66,6 +66,10 @@ export function DiscoverLink({ query = {}, ...rest }: Props) { const location = useLocation(); const dataViewId = useDataViewId(); + if (!dataViewId) { + return null; + } + const href = getDiscoverHref({ basePath: core.http.basePath, query, diff --git a/x-pack/plugins/apm/public/components/shared/links/discover_links/discover_links.integration.test.tsx b/x-pack/plugins/apm/public/components/shared/links/discover_links/discover_links.integration.test.tsx index f4d95a87df5dc..4d79e5e8f2796 100644 --- a/x-pack/plugins/apm/public/components/shared/links/discover_links/discover_links.integration.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/links/discover_links/discover_links.integration.test.tsx @@ -35,7 +35,7 @@ describe('DiscoverLinks', () => { ); expect(href).toMatchInlineSnapshot( - `"/basepath/app/discover#/?_g=(refreshInterval:(pause:!t,value:0),time:(from:now/w,to:now))&_a=(index:apm_static_data_view_id_default,interval:auto,query:(language:kuery,query:'processor.event:\\"transaction\\" AND transaction.id:\\"8b60bd32ecc6e150\\" AND trace.id:\\"8b60bd32ecc6e1506735a8b6cfcf175c\\"'))"` + `"/basepath/app/discover#/?_g=(refreshInterval:(pause:!t,value:0),time:(from:now/w,to:now))&_a=(index:apm_static_data_view_id_mockSpaceId,interval:auto,query:(language:kuery,query:'processor.event:\\"transaction\\" AND transaction.id:\\"8b60bd32ecc6e150\\" AND trace.id:\\"8b60bd32ecc6e1506735a8b6cfcf175c\\"'))"` ); }); @@ -55,7 +55,7 @@ describe('DiscoverLinks', () => { ); expect(href).toMatchInlineSnapshot( - `"/basepath/app/discover#/?_g=(refreshInterval:(pause:!t,value:0),time:(from:now/w,to:now))&_a=(index:apm_static_data_view_id_default,interval:auto,query:(language:kuery,query:'span.id:\\"test-span-id\\"'))"` + `"/basepath/app/discover#/?_g=(refreshInterval:(pause:!t,value:0),time:(from:now/w,to:now))&_a=(index:apm_static_data_view_id_mockSpaceId,interval:auto,query:(language:kuery,query:'span.id:\\"test-span-id\\"'))"` ); }); @@ -77,7 +77,7 @@ describe('DiscoverLinks', () => { ); expect(href).toMatchInlineSnapshot( - `"/basepath/app/discover#/?_g=(refreshInterval:(pause:!t,value:0),time:(from:now/w,to:now))&_a=(index:apm_static_data_view_id_default,interval:auto,query:(language:kuery,query:'service.name:\\"service-name\\" AND error.grouping_key:\\"grouping-key\\"'),sort:('@timestamp':desc))"` + `"/basepath/app/discover#/?_g=(refreshInterval:(pause:!t,value:0),time:(from:now/w,to:now))&_a=(index:apm_static_data_view_id_mockSpaceId,interval:auto,query:(language:kuery,query:'service.name:\\"service-name\\" AND error.grouping_key:\\"grouping-key\\"'),sort:('@timestamp':desc))"` ); }); @@ -100,7 +100,7 @@ describe('DiscoverLinks', () => { ); expect(href).toMatchInlineSnapshot( - `"/basepath/app/discover#/?_g=(refreshInterval:(pause:!t,value:0),time:(from:now/w,to:now))&_a=(index:apm_static_data_view_id_default,interval:auto,query:(language:kuery,query:'service.name:\\"service-name\\" AND error.grouping_key:\\"grouping-key\\" AND some:kuery-string'),sort:('@timestamp':desc))"` + `"/basepath/app/discover#/?_g=(refreshInterval:(pause:!t,value:0),time:(from:now/w,to:now))&_a=(index:apm_static_data_view_id_mockSpaceId,interval:auto,query:(language:kuery,query:'service.name:\\"service-name\\" AND error.grouping_key:\\"grouping-key\\" AND some:kuery-string'),sort:('@timestamp':desc))"` ); }); }); diff --git a/x-pack/plugins/apm/public/components/shared/search_bar/search_bar.tsx b/x-pack/plugins/apm/public/components/shared/search_bar/search_bar.tsx index 0b3d8ea7b7113..a4e8fd5c9d1a1 100644 --- a/x-pack/plugins/apm/public/components/shared/search_bar/search_bar.tsx +++ b/x-pack/plugins/apm/public/components/shared/search_bar/search_bar.tsx @@ -12,8 +12,6 @@ import { } from '@elastic/eui'; import React from 'react'; import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; -import { isMobileAgentName } from '../../../../common/agent_name'; -import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; import { useBreakpoints } from '../../../hooks/use_breakpoints'; import { TimeComparison } from '../time_comparison'; import { TransactionTypeSelect } from '../transaction_type_select'; @@ -38,9 +36,6 @@ export function SearchBar({ searchBarPlaceholder, searchBarBoolFilter, }: Props) { - const { agentName } = useApmServiceContext(); - const isMobileAgent = isMobileAgentName(agentName); - const { isSmall, isMedium, isLarge, isXl, isXXL, isXXXL } = useBreakpoints(); if (hidden) { @@ -95,7 +90,7 @@ export function SearchBar({ - + ); } diff --git a/x-pack/plugins/apm/public/components/shared/stacktrace/index.tsx b/x-pack/plugins/apm/public/components/shared/stacktrace/index.tsx index 4a3646dbb78c1..379dd5700406e 100644 --- a/x-pack/plugins/apm/public/components/shared/stacktrace/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/stacktrace/index.tsx @@ -17,6 +17,7 @@ import { Stackframe as StackframeComponent } from './stackframe'; interface Props { stackframes?: Stackframe[]; codeLanguage?: string; + stackTrace?: string; } export function Stacktrace({ stackframes = [], codeLanguage }: Props) { diff --git a/x-pack/plugins/apm/public/components/shared/transaction_action_menu/__snapshots__/transaction_action_menu.test.tsx.snap b/x-pack/plugins/apm/public/components/shared/transaction_action_menu/__snapshots__/transaction_action_menu.test.tsx.snap index 17f22a43a1c43..02ed85e387ecb 100644 --- a/x-pack/plugins/apm/public/components/shared/transaction_action_menu/__snapshots__/transaction_action_menu.test.tsx.snap +++ b/x-pack/plugins/apm/public/components/shared/transaction_action_menu/__snapshots__/transaction_action_menu.test.tsx.snap @@ -3,32 +3,28 @@ exports[`TransactionActionMenu component matches the snapshot 1`] = `
-
- -
+ + +
`; diff --git a/x-pack/plugins/apm/public/components/shared/transaction_action_menu/sections.ts b/x-pack/plugins/apm/public/components/shared/transaction_action_menu/sections.ts index b50bbd5239ccb..a21e79353e675 100644 --- a/x-pack/plugins/apm/public/components/shared/transaction_action_menu/sections.ts +++ b/x-pack/plugins/apm/public/components/shared/transaction_action_menu/sections.ts @@ -69,9 +69,9 @@ export const getSections = ({ allDatasetsLocator: LocatorPublic; logsLocator: LocatorPublic; nodeLogsLocator: LocatorPublic; - dataViewId: string; + dataViewId?: string; }) => { - if (!transaction) return []; + if (!transaction || !dataViewId) return []; const hostName = transaction.host?.hostname; const podId = transaction.kubernetes?.pod?.uid; diff --git a/x-pack/plugins/apm/public/components/shared/transaction_action_menu/transaction_action_menu.test.tsx b/x-pack/plugins/apm/public/components/shared/transaction_action_menu/transaction_action_menu.test.tsx index 28fc0657ee831..764f2a11c02cc 100644 --- a/x-pack/plugins/apm/public/components/shared/transaction_action_menu/transaction_action_menu.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/transaction_action_menu/transaction_action_menu.test.tsx @@ -30,6 +30,7 @@ import { } from '../../../utils/test_helpers'; import { TransactionActionMenu } from './transaction_action_menu'; import * as Transactions from './__fixtures__/mock_data'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; const apmContextMock = { ...mockApmPluginContextValue, @@ -60,10 +61,16 @@ history.replace( ); function Wrapper({ children }: { children?: React.ReactNode }) { + const mockSpaces = { + getActiveSpace: jest.fn().mockImplementation(() => ({ id: 'mockSpaceId' })), + }; + return ( - {children} + + {children} + ); @@ -412,9 +419,9 @@ describe('TransactionActionMenu component', () => { component .getByTestId(`${key}.value`) .querySelector( - '[data-test-subj="comboBoxInput"] span' - ) as HTMLSpanElement - ).textContent, + '[data-test-subj="comboBoxSearchInput"]' + ) as HTMLInputElement + ).value, }; }; expect(getFilterKeyValue('service.name')).toEqual({ diff --git a/x-pack/plugins/apm/public/hooks/use_data_view_id.tsx b/x-pack/plugins/apm/public/hooks/use_data_view_id.tsx index 3390471ff60e7..14e1a1fb92721 100644 --- a/x-pack/plugins/apm/public/hooks/use_data_view_id.tsx +++ b/x-pack/plugins/apm/public/hooks/use_data_view_id.tsx @@ -11,15 +11,15 @@ import { getDataViewId } from '../../common/data_view_constants'; import { ApmPluginStartDeps } from '../plugin'; export function useDataViewId() { - const [dataViewId, setDataViewId] = useState( - getDataViewId('default') - ); + const [dataViewId, setDataViewId] = useState(); const { spaces } = useKibana().services; useEffect(() => { const fetchSpaceId = async () => { const space = await spaces?.getActiveSpace(); - setDataViewId(getDataViewId(space?.id ?? 'default')); + if (space?.id) { + setDataViewId(getDataViewId(space?.id)); + } }; fetchSpaceId(); diff --git a/x-pack/plugins/apm/public/plugin.ts b/x-pack/plugins/apm/public/plugin.ts index 51a5587452261..173e52e5955cd 100644 --- a/x-pack/plugins/apm/public/plugin.ts +++ b/x-pack/plugins/apm/public/plugin.ts @@ -428,15 +428,11 @@ export class ApmPlugin implements Plugin { const { fleet } = plugins; plugins.observabilityAIAssistant.service.register( - async ({ signal, registerContext, registerFunction }) => { + async ({ registerRenderFunction }) => { const mod = await import('./assistant_functions'); mod.registerAssistantFunctions({ - coreStart: core, - pluginsStart: plugins, - registerContext, - registerFunction, - signal, + registerRenderFunction, }); } ); diff --git a/x-pack/plugins/apm/public/tutorial/config_agent/index.test.tsx b/x-pack/plugins/apm/public/tutorial/config_agent/index.test.tsx index edae01e0b6043..8ef39fcbc8c73 100644 --- a/x-pack/plugins/apm/public/tutorial/config_agent/index.test.tsx +++ b/x-pack/plugins/apm/public/tutorial/config_agent/index.test.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { fireEvent, render, screen } from '@testing-library/react'; +import { fireEvent, render, screen, within } from '@testing-library/react'; import { HttpStart } from '@kbn/core/public'; import React from 'react'; import { @@ -80,9 +80,17 @@ describe('TutorialConfigAgent', () => { kibanaVersion="8.0.0" /> ); - expect( - await screen.findByText('Default Standalone configuration') - ).toBeInTheDocument(); + + const policySelectorWrapper = await screen.findByTestId( + 'policySelector_onPrem' + ); + expect(policySelectorWrapper).toBeInTheDocument(); + + const input = within(policySelectorWrapper).getByTestId( + 'comboBoxSearchInput' + ); + expect(input).toHaveValue('Default Standalone configuration'); + let commands = component.getByTestId('commands').innerHTML; expect(commands).not.toEqual(''); expect(commands).toMatchInlineSnapshot(` @@ -128,12 +136,17 @@ describe('TutorialConfigAgent', () => { kibanaVersion="8.0.0" /> ); - expect( - await screen.findByText('Default Standalone configuration') - ).toBeInTheDocument(); - expect( - component.getByTestId('policySelector_onPrem') - ).toBeInTheDocument(); + + const policySelectorWrapper = await screen.findByTestId( + 'policySelector_onPrem' + ); + expect(policySelectorWrapper).toBeInTheDocument(); + + const input = within(policySelectorWrapper).getByTestId( + 'comboBoxSearchInput' + ); + expect(input).toHaveValue('Default Standalone configuration'); + const commands = component.getByTestId('commands').innerHTML; expect(commands).not.toEqual(''); expect(commands).toMatchInlineSnapshot(` @@ -164,12 +177,17 @@ describe('TutorialConfigAgent', () => { kibanaVersion="8.0.0" /> ); - expect( - await screen.findByText('Default Standalone configuration') - ).toBeInTheDocument(); - expect( - component.getByTestId('policySelector_onPrem') - ).toBeInTheDocument(); + + const policySelectorWrapper = await screen.findByTestId( + 'policySelector_onPrem' + ); + expect(policySelectorWrapper).toBeInTheDocument(); + + const input = within(policySelectorWrapper).getByTestId( + 'comboBoxSearchInput' + ); + expect(input).toHaveValue('Default Standalone configuration'); + const commands = component.getByTestId('commands').innerHTML; expect(commands).not.toEqual(''); expect(commands).toMatchInlineSnapshot(` @@ -206,12 +224,17 @@ describe('TutorialConfigAgent', () => { kibanaVersion="8.0.0" /> ); - expect( - await screen.findByText('Default Standalone configuration') - ).toBeInTheDocument(); - expect( - component.getByTestId('policySelector_cloud') - ).toBeInTheDocument(); + + const policySelectorWrapper = await screen.findByTestId( + 'policySelector_cloud' + ); + expect(policySelectorWrapper).toBeInTheDocument(); + + const input = within(policySelectorWrapper).getByTestId( + 'comboBoxSearchInput' + ); + expect(input).toHaveValue('Default Standalone configuration'); + const commands = component.getByTestId('commands').innerHTML; expect(commands).not.toEqual(''); expect(commands).toMatchInlineSnapshot(` @@ -245,12 +268,17 @@ describe('TutorialConfigAgent', () => { kibanaVersion="8.0.0" /> ); - expect( - await screen.findByText('Elastic Cloud agent policy') - ).toBeInTheDocument(); - expect( - component.getByTestId('policySelector_policy-elastic-agent-on-cloud') - ).toBeInTheDocument(); + + const policySelectorWrapper = await screen.findByTestId( + 'policySelector_policy-elastic-agent-on-cloud' + ); + expect(policySelectorWrapper).toBeInTheDocument(); + + const input = within(policySelectorWrapper).getByTestId( + 'comboBoxSearchInput' + ); + expect(input).toHaveValue('Elastic Cloud agent policy'); + const commands = component.getByTestId('commands').innerHTML; expect(commands).not.toEqual(''); expect(commands).toMatchInlineSnapshot(` @@ -280,9 +308,17 @@ describe('TutorialConfigAgent', () => { kibanaVersion="8.0.0" /> ); - expect( - await screen.findByText('Default Standalone configuration') - ).toBeInTheDocument(); + + const policySelectorWrapper = await screen.findByTestId( + 'policySelector_onPrem' + ); + expect(policySelectorWrapper).toBeInTheDocument(); + + const input = within(policySelectorWrapper).getByTestId( + 'comboBoxSearchInput' + ); + expect(input).toHaveValue('Default Standalone configuration'); + const commands = component.getByTestId('commands').innerHTML; expect(commands).not.toEqual(''); expect(commands).toMatchInlineSnapshot(` @@ -340,12 +376,17 @@ describe('TutorialConfigAgent', () => { kibanaVersion="8.0.0" /> ); - expect( - await screen.findByText('Default Standalone configuration') - ).toBeInTheDocument(); - expect( - component.getByTestId('policySelector_onPrem') - ).toBeInTheDocument(); + + const policySelectorWrapper = await screen.findByTestId( + 'policySelector_onPrem' + ); + expect(policySelectorWrapper).toBeInTheDocument(); + + const input = within(policySelectorWrapper).getByTestId( + 'comboBoxSearchInput' + ); + expect(input).toHaveValue('Default Standalone configuration'); + const commands = component.getByTestId('commands').innerHTML; expect(commands).not.toEqual(''); expect(commands).toMatchInlineSnapshot(` @@ -379,10 +420,16 @@ describe('TutorialConfigAgent', () => { kibanaVersion="8.0.0" /> ); - expect( - await screen.findByText('Default Standalone configuration') - ).toBeInTheDocument(); - expect(component.getByTestId('policySelector_cloud')).toBeInTheDocument(); + const policySelectorWrapper = await screen.findByTestId( + 'policySelector_cloud' + ); + expect(policySelectorWrapper).toBeInTheDocument(); + + const input = within(policySelectorWrapper).getByTestId( + 'comboBoxSearchInput' + ); + expect(input).toHaveValue('Default Standalone configuration'); + const commands = component.getByTestId('commands').innerHTML; expect(commands).not.toEqual(''); expect(commands).toMatchInlineSnapshot(` diff --git a/x-pack/plugins/apm/public/utils/test_helpers.tsx b/x-pack/plugins/apm/public/utils/test_helpers.tsx index bb81f73ef7705..360ac9a95298b 100644 --- a/x-pack/plugins/apm/public/utils/test_helpers.tsx +++ b/x-pack/plugins/apm/public/utils/test_helpers.tsx @@ -19,6 +19,7 @@ import { Moment } from 'moment-timezone'; import React from 'react'; import { MemoryRouter } from 'react-router-dom'; import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; import { MockApmPluginContextWrapper } from '../context/apm_plugin/mock_apm_plugin_context'; import { UrlParamsProvider } from '../context/url_params_context/url_params_context'; @@ -61,20 +62,27 @@ export function mockMoment() { // Useful for getting the rendered href from any kind of link component export async function getRenderedHref(Component: React.FC, location: Location) { + const mockSpaces = { + getActiveSpace: jest.fn().mockImplementation(() => ({ id: 'mockSpaceId' })), + }; + const el = render( - - - + + + + + ); - const a = el.container.querySelector('a'); - await waitFor(() => {}, { container: a! }); + await waitFor(() => el.container.querySelector('a') !== null, { + container: el.container, + }); - return a ? a.getAttribute('href') : ''; + return el.container.querySelector('a')?.getAttribute('href'); } export function mockNow(date: string | number | Date) { diff --git a/x-pack/plugins/apm/public/assistant_functions/get_apm_correlations.ts b/x-pack/plugins/apm/server/assistant_functions/get_apm_correlations.ts similarity index 91% rename from x-pack/plugins/apm/public/assistant_functions/get_apm_correlations.ts rename to x-pack/plugins/apm/server/assistant_functions/get_apm_correlations.ts index 2d600e41e4860..f3f61f7d16937 100644 --- a/x-pack/plugins/apm/public/assistant_functions/get_apm_correlations.ts +++ b/x-pack/plugins/apm/server/assistant_functions/get_apm_correlations.ts @@ -6,15 +6,14 @@ */ import { i18n } from '@kbn/i18n'; -import type { RegisterFunctionDefinition } from '@kbn/observability-ai-assistant-plugin/common/types'; +import { FunctionRegistrationParameters } from '.'; import { CorrelationsEventType } from '../../common/assistant/constants'; -import { callApmApi } from '../services/rest/create_call_apm_api'; +import { getApmCorrelationValues } from '../routes/assistant_functions/get_apm_correlation_values'; export function registerGetApmCorrelationsFunction({ + apmEventClient, registerFunction, -}: { - registerFunction: RegisterFunctionDefinition; -}) { +}: FunctionRegistrationParameters) { registerFunction( { name: 'get_apm_correlations', @@ -113,12 +112,12 @@ export function registerGetApmCorrelationsFunction({ } as const, }, async ({ arguments: args }, signal) => { - return callApmApi('POST /internal/apm/assistant/get_correlation_values', { - signal, - params: { - body: args, - }, - }); + return { + content: await getApmCorrelationValues({ + arguments: args as any, + apmEventClient, + }), + }; } ); } diff --git a/x-pack/plugins/apm/public/assistant_functions/get_apm_downstream_dependencies.ts b/x-pack/plugins/apm/server/assistant_functions/get_apm_downstream_dependencies.ts similarity index 83% rename from x-pack/plugins/apm/public/assistant_functions/get_apm_downstream_dependencies.ts rename to x-pack/plugins/apm/server/assistant_functions/get_apm_downstream_dependencies.ts index fea837dd76c31..0440f684eeedd 100644 --- a/x-pack/plugins/apm/public/assistant_functions/get_apm_downstream_dependencies.ts +++ b/x-pack/plugins/apm/server/assistant_functions/get_apm_downstream_dependencies.ts @@ -6,14 +6,13 @@ */ import { i18n } from '@kbn/i18n'; -import type { RegisterFunctionDefinition } from '@kbn/observability-ai-assistant-plugin/common/types'; -import { callApmApi } from '../services/rest/create_call_apm_api'; +import type { FunctionRegistrationParameters } from '.'; +import { getAssistantDownstreamDependencies } from '../routes/assistant_functions/get_apm_downstream_dependencies'; export function registerGetApmDownstreamDependenciesFunction({ + apmEventClient, registerFunction, -}: { - registerFunction: RegisterFunctionDefinition; -}) { +}: FunctionRegistrationParameters) { registerFunction( { name: 'get_apm_downstream_dependencies', @@ -57,15 +56,12 @@ export function registerGetApmDownstreamDependenciesFunction({ } as const, }, async ({ arguments: args }, signal) => { - return callApmApi( - 'GET /internal/apm/assistant/get_downstream_dependencies', - { - signal, - params: { - query: args, - }, - } - ); + return { + content: await getAssistantDownstreamDependencies({ + arguments: args, + apmEventClient, + }), + }; } ); } diff --git a/x-pack/plugins/apm/public/assistant_functions/get_apm_error_document.ts b/x-pack/plugins/apm/server/assistant_functions/get_apm_error_document.ts similarity index 83% rename from x-pack/plugins/apm/public/assistant_functions/get_apm_error_document.ts rename to x-pack/plugins/apm/server/assistant_functions/get_apm_error_document.ts index a5c66478e3fd7..e5082f47ad8eb 100644 --- a/x-pack/plugins/apm/public/assistant_functions/get_apm_error_document.ts +++ b/x-pack/plugins/apm/server/assistant_functions/get_apm_error_document.ts @@ -6,14 +6,13 @@ */ import { i18n } from '@kbn/i18n'; -import type { RegisterFunctionDefinition } from '@kbn/observability-ai-assistant-plugin/common/types'; -import { callApmApi } from '../services/rest/create_call_apm_api'; +import type { FunctionRegistrationParameters } from '.'; +import { getApmErrorDocument } from '../routes/assistant_functions/get_apm_error_document'; export function registerGetApmErrorDocumentFunction({ + apmEventClient, registerFunction, -}: { - registerFunction: RegisterFunctionDefinition; -}) { +}: FunctionRegistrationParameters) { registerFunction( { name: 'get_apm_error_document', @@ -55,12 +54,12 @@ export function registerGetApmErrorDocumentFunction({ } as const, }, async ({ arguments: args }, signal) => { - return callApmApi('GET /internal/apm/assistant/get_error_document', { - signal, - params: { - query: args, - }, - }); + return { + content: await getApmErrorDocument({ + apmEventClient, + arguments: args, + }), + }; } ); } diff --git a/x-pack/plugins/apm/public/assistant_functions/get_apm_service_summary.ts b/x-pack/plugins/apm/server/assistant_functions/get_apm_service_summary.ts similarity index 64% rename from x-pack/plugins/apm/public/assistant_functions/get_apm_service_summary.ts rename to x-pack/plugins/apm/server/assistant_functions/get_apm_service_summary.ts index 189633ec95975..291e4fdae33b0 100644 --- a/x-pack/plugins/apm/public/assistant_functions/get_apm_service_summary.ts +++ b/x-pack/plugins/apm/server/assistant_functions/get_apm_service_summary.ts @@ -5,16 +5,19 @@ * 2.0. */ +import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; import { i18n } from '@kbn/i18n'; -import type { RegisterFunctionDefinition } from '@kbn/observability-ai-assistant-plugin/common/types'; -import { callApmApi } from '../services/rest/create_call_apm_api'; +import type { FunctionRegistrationParameters } from '.'; +import { getApmAlertsClient } from '../lib/helpers/get_apm_alerts_client'; +import { getMlClient } from '../lib/helpers/get_ml_client'; +import { getApmServiceSummary } from '../routes/assistant_functions/get_apm_service_summary'; import { NON_EMPTY_STRING } from '../utils/non_empty_string_ref'; export function registerGetApmServiceSummaryFunction({ + resources, + apmEventClient, registerFunction, -}: { - registerFunction: RegisterFunctionDefinition; -}) { +}: FunctionRegistrationParameters) { registerFunction( { name: 'get_apm_service_summary', @@ -58,12 +61,33 @@ alerts and anomalies.`, } as const, }, async ({ arguments: args }, signal) => { - return callApmApi('GET /internal/apm/assistant/get_service_summary', { - signal, - params: { - query: args, - }, - }); + const { context, request, plugins, logger } = resources; + + const [annotationsClient, esClient, apmAlertsClient, mlClient] = + await Promise.all([ + plugins.observability.setup.getScopedAnnotationsClient( + context, + request + ), + context.core.then( + (coreContext): ElasticsearchClient => + coreContext.elasticsearch.client.asCurrentUser + ), + getApmAlertsClient(resources), + getMlClient(resources), + ]); + + return { + content: await getApmServiceSummary({ + apmEventClient, + annotationsClient, + esClient, + apmAlertsClient, + mlClient, + logger, + arguments: args, + }), + }; } ); } diff --git a/x-pack/plugins/apm/server/assistant_functions/get_apm_services_list.ts b/x-pack/plugins/apm/server/assistant_functions/get_apm_services_list.ts new file mode 100644 index 0000000000000..178d47f8d33ed --- /dev/null +++ b/x-pack/plugins/apm/server/assistant_functions/get_apm_services_list.ts @@ -0,0 +1,137 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import datemath from '@elastic/datemath'; +import { i18n } from '@kbn/i18n'; +import { FunctionRegistrationParameters } from '.'; +import { ApmDocumentType } from '../../common/document_type'; +import { ENVIRONMENT_ALL } from '../../common/environment_filter_values'; +import { RollupInterval } from '../../common/rollup'; +import { ServiceHealthStatus } from '../../common/service_health_status'; +import { getApmAlertsClient } from '../lib/helpers/get_apm_alerts_client'; +import { getMlClient } from '../lib/helpers/get_ml_client'; +import { getRandomSampler } from '../lib/helpers/get_random_sampler'; +import { getServicesItems } from '../routes/services/get_services/get_services_items'; +import { NON_EMPTY_STRING } from '../utils/non_empty_string_ref'; + +export interface ApmServicesListItem { + 'service.name': string; + 'agent.name'?: string; + 'transaction.type'?: string; + alertsCount: number; + healthStatus: ServiceHealthStatus; + 'service.environment'?: string[]; +} + +export function registerGetApmServicesListFunction({ + apmEventClient, + resources, + registerFunction, +}: FunctionRegistrationParameters) { + registerFunction( + { + name: 'get_apm_services_list', + contexts: ['apm'], + description: `Gets a list of services`, + descriptionForUser: i18n.translate( + 'xpack.apm.observabilityAiAssistant.functions.registerGetApmServicesList.descriptionForUser', + { + defaultMessage: `Gets the list of monitored services, their health status, and alerts.`, + } + ), + parameters: { + type: 'object', + additionalProperties: false, + properties: { + 'service.environment': { + ...NON_EMPTY_STRING, + description: + 'Optionally filter the services by the environments that they are running in', + }, + start: { + ...NON_EMPTY_STRING, + description: + 'The start of the time range, in Elasticsearch date math, like `now`.', + }, + end: { + ...NON_EMPTY_STRING, + description: + 'The end of the time range, in Elasticsearch date math, like `now-24h`.', + }, + healthStatus: { + type: 'array', + description: 'Filter service list by health status', + additionalProperties: false, + additionalItems: false, + items: { + type: 'string', + enum: [ + ServiceHealthStatus.unknown, + ServiceHealthStatus.healthy, + ServiceHealthStatus.warning, + ServiceHealthStatus.critical, + ], + }, + }, + }, + required: ['start', 'end'], + } as const, + }, + async ({ arguments: args }, signal) => { + const { healthStatus } = args; + const [apmAlertsClient, mlClient, randomSampler] = await Promise.all([ + getApmAlertsClient(resources), + getMlClient(resources), + getRandomSampler({ + security: resources.plugins.security, + probability: 1, + request: resources.request, + }), + ]); + + const start = datemath.parse(args.start)?.valueOf()!; + const end = datemath.parse(args.end)?.valueOf()!; + + const serviceItems = await getServicesItems({ + apmAlertsClient, + apmEventClient, + documentType: ApmDocumentType.TransactionMetric, + start, + end, + environment: args['service.environment'] || ENVIRONMENT_ALL.value, + kuery: '', + logger: resources.logger, + randomSampler, + rollupInterval: RollupInterval.OneMinute, + serviceGroup: null, + mlClient, + useDurationSummary: false, + }); + + let mappedItems = serviceItems.items.map((item): ApmServicesListItem => { + return { + 'service.name': item.serviceName, + 'agent.name': item.agentName, + alertsCount: item.alertsCount ?? 0, + healthStatus: item.healthStatus ?? ServiceHealthStatus.unknown, + 'service.environment': item.environments, + 'transaction.type': item.transactionType, + }; + }); + + if (healthStatus && healthStatus.length) { + mappedItems = mappedItems.filter((item): boolean => + healthStatus.includes(item.healthStatus) + ); + } + + return { + content: mappedItems, + }; + } + ); +} diff --git a/x-pack/plugins/apm/server/assistant_functions/get_apm_timeseries.ts b/x-pack/plugins/apm/server/assistant_functions/get_apm_timeseries.ts new file mode 100644 index 0000000000000..0d5be32611eb4 --- /dev/null +++ b/x-pack/plugins/apm/server/assistant_functions/get_apm_timeseries.ts @@ -0,0 +1,174 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { i18n } from '@kbn/i18n'; +import { FromSchema } from 'json-schema-to-ts'; +import { omit } from 'lodash'; +import { FunctionRegistrationParameters } from '.'; +import { + ApmTimeseries, + getApmTimeseries, +} from '../routes/assistant_functions/get_apm_timeseries'; +import { NON_EMPTY_STRING } from '../utils/non_empty_string_ref'; + +const parameters = { + type: 'object', + properties: { + start: { + type: 'string', + description: + 'The start of the time range, in Elasticsearch date math, like `now`.', + }, + end: { + type: 'string', + description: + 'The end of the time range, in Elasticsearch date math, like `now-24h`.', + }, + stats: { + type: 'array', + items: { + type: 'object', + properties: { + timeseries: { + description: 'The metric to be displayed', + oneOf: [ + { + type: 'object', + properties: { + name: { + type: 'string', + enum: [ + 'transaction_throughput', + 'transaction_failure_rate', + ], + }, + 'transaction.type': { + type: 'string', + description: 'The transaction type', + }, + }, + required: ['name'], + }, + { + type: 'object', + properties: { + name: { + type: 'string', + enum: [ + 'exit_span_throughput', + 'exit_span_failure_rate', + 'exit_span_latency', + ], + }, + 'span.destination.service.resource': { + type: 'string', + description: + 'The name of the downstream dependency for the service', + }, + }, + required: ['name'], + }, + { + type: 'object', + properties: { + name: { + type: 'string', + const: 'error_event_rate', + }, + }, + required: ['name'], + }, + { + type: 'object', + properties: { + name: { + type: 'string', + const: 'transaction_latency', + }, + 'transaction.type': { + type: 'string', + }, + function: { + type: 'string', + enum: ['avg', 'p95', 'p99'], + }, + }, + required: ['name', 'function'], + }, + ], + }, + 'service.name': { + ...NON_EMPTY_STRING, + description: 'The name of the service', + }, + 'service.environment': { + description: + 'The environment that the service is running in. If undefined, all environments will be included. Only use this if you have confirmed the environment that the service is running in.', + }, + filter: { + type: 'string', + description: + 'a KQL query to filter the data by. If no filter should be applied, leave it empty.', + }, + title: { + type: 'string', + description: + 'A unique, human readable, concise title for this specific group series.', + }, + offset: { + type: 'string', + description: + 'The offset. Right: 15m. 8h. 1d. Wrong: -15m. -8h. -1d.', + }, + }, + required: ['service.name', 'timeseries', 'title'], + }, + }, + }, + required: ['stats', 'start', 'end'], +} as const; + +export function registerGetApmTimeseriesFunction({ + apmEventClient, + registerFunction, +}: FunctionRegistrationParameters) { + registerFunction( + { + contexts: ['apm'], + name: 'get_apm_timeseries', + descriptionForUser: i18n.translate( + 'xpack.apm.observabilityAiAssistant.functions.registerGetApmTimeseries.descriptionForUser', + { + defaultMessage: `Display different APM metrics, like throughput, failure rate, or latency, for any service or all services, or any or all of its dependencies, both as a timeseries and as a single statistic. Additionally, the function will return any changes, such as spikes, step and trend changes, or dips. You can also use it to compare data by requesting two different time ranges, or for instance two different service versions`, + } + ), + description: `Visualise and analyse different APM metrics, like throughput, failure rate, or latency, for any service or all services, or any or all of its dependencies, both as a timeseries and as a single statistic. A visualisation will be displayed above your reply - DO NOT attempt to display or generate an image yourself, or any other placeholder. Additionally, the function will return any changes, such as spikes, step and trend changes, or dips. You can also use it to compare data by requesting two different time ranges, or for instance two different service versions.`, + parameters, + }, + async ( + { arguments: args }, + signal + ): Promise => { + const timeseries = await getApmTimeseries({ + apmEventClient, + arguments: args as any, + }); + + return { + content: timeseries.map( + (series): Omit => omit(series, 'data') + ), + data: timeseries, + }; + } + ); +} + +export type GetApmTimeseriesFunctionArguments = FromSchema; +export interface GetApmTimeseriesFunctionResponse { + content: Array>; + data: ApmTimeseries[]; +} diff --git a/x-pack/plugins/apm/server/assistant_functions/index.ts b/x-pack/plugins/apm/server/assistant_functions/index.ts new file mode 100644 index 0000000000000..81521a233ad1d --- /dev/null +++ b/x-pack/plugins/apm/server/assistant_functions/index.ts @@ -0,0 +1,186 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { CoreSetup } from '@kbn/core-lifecycle-server'; +import type { Logger } from '@kbn/logging'; +import type { + ChatRegistrationFunction, + RegisterFunction, +} from '@kbn/observability-ai-assistant-plugin/server/service/types'; +import type { IRuleDataClient } from '@kbn/rule-registry-plugin/server'; +import type { APMConfig } from '..'; +import type { ApmFeatureFlags } from '../../common/apm_feature_flags'; +import { APMEventClient } from '../lib/helpers/create_es_client/create_apm_event_client'; +import { getApmEventClient } from '../lib/helpers/get_apm_event_client'; +import type { APMRouteHandlerResources } from '../routes/apm_routes/register_apm_server_routes'; +import { hasHistoricalAgentData } from '../routes/historical_data/has_historical_agent_data'; +import { registerGetApmCorrelationsFunction } from './get_apm_correlations'; +import { registerGetApmDownstreamDependenciesFunction } from './get_apm_downstream_dependencies'; +import { registerGetApmErrorDocumentFunction } from './get_apm_error_document'; +import { registerGetApmServicesListFunction } from './get_apm_services_list'; +import { registerGetApmServiceSummaryFunction } from './get_apm_service_summary'; +import { registerGetApmTimeseriesFunction } from './get_apm_timeseries'; + +export interface FunctionRegistrationParameters { + apmEventClient: APMEventClient; + registerFunction: RegisterFunction; + resources: APMRouteHandlerResources; +} + +export function registerAssistantFunctions({ + coreSetup, + config, + featureFlags, + logger, + kibanaVersion, + ruleDataClient, + plugins, +}: { + coreSetup: CoreSetup; + config: APMConfig; + featureFlags: ApmFeatureFlags; + logger: Logger; + kibanaVersion: string; + ruleDataClient: IRuleDataClient; + plugins: APMRouteHandlerResources['plugins']; +}): ChatRegistrationFunction { + return async ({ resources, registerContext, registerFunction }) => { + const apmRouteHandlerResources: APMRouteHandlerResources = { + context: resources.context, + request: resources.request, + core: { + setup: coreSetup, + start: () => + coreSetup.getStartServices().then(([coreStart]) => coreStart), + }, + params: { + query: { + _inspect: false, + }, + }, + config, + featureFlags, + logger, + kibanaVersion, + ruleDataClient, + plugins, + getApmIndices: async () => { + const coreContext = await resources.context.core; + const apmIndices = await plugins.apmDataAccess.setup.getApmIndices( + coreContext.savedObjects.client + ); + return apmIndices; + }, + }; + + const apmEventClient = await getApmEventClient(apmRouteHandlerResources); + + const hasData = await hasHistoricalAgentData(apmEventClient); + + if (!hasData) { + return; + } + + const parameters: FunctionRegistrationParameters = { + resources: apmRouteHandlerResources, + apmEventClient, + registerFunction, + }; + + registerGetApmServicesListFunction(parameters); + registerGetApmServiceSummaryFunction(parameters); + registerGetApmErrorDocumentFunction(parameters); + registerGetApmDownstreamDependenciesFunction(parameters); + registerGetApmCorrelationsFunction(parameters); + registerGetApmTimeseriesFunction(parameters); + + registerContext({ + name: 'apm', + description: ` + When analyzing APM data, prefer the APM specific functions over the generic Lens, + Elasticsearch or Kibana ones, unless those are explicitly requested by the user. + + When requesting metrics for a service, make sure you also know what environment + it is running in. Metrics aggregated over multiple environments are useless. + + There are four important data types in Elastic APM. Each of them have the + following fields: + - service.name: the name of the service + - service.node.name: the id of the service instance (often the hostname) + - service.environment: the environment (often production, development) + - agent.name: the name of the agent (go, java, etc) + + The four data types are transactions, exit spans, error events, and application + metrics. + + Transactions have three metrics: throughput, failure rate, and latency. The + fields are: + + - transaction.type: often request or page-load (the main transaction types), + but can also be worker, or route-change. + - transaction.name: The name of the transaction group, often something like + 'GET /api/product/:productId' + - transaction.result: The result. Used to capture HTTP response codes + (2xx,3xx,4xx,5xx) for request transactions. + - event.outcome: whether the transaction was succesful or not. success, + failure, or unknown. + + Exit spans have three metrics: throughput, failure rate and latency. The fields + are: + - span.type: db, external + - span.subtype: the type of database (redis, postgres) or protocol (http, grpc) + - span.destination.service.resource: the address of the destination of the call + - event.outcome: whether the transaction was succesful or not. success, + failure, or unknown. + + Error events have one metric, error event rate. The fields are: + - error.grouping_name: a human readable keyword that identifies the error group + + For transaction metrics we also collect anomalies. These are scored 0 (low) to + 100 (critical). + + For root cause analysis, locate a change point in the relevant metrics for a + service or downstream dependency. You can locate a change point by using a + sliding window, e.g. start with a small time range, like 30m, and make it + bigger until you identify a change point. It's very important to identify a + change point. If you don't have a change point, ask the user for next steps. + You can also use an anomaly or a deployment as a change point. Then, compare + data before the change with data after the change. You can either use the + groupBy parameter in get_apm_chart to get the most occuring values in a certain + data set, or you can use correlations to see for which field and value the + frequency has changed when comparing the foreground set to the background set. + This is useful when comparing data from before the change point with after the + change point. For instance, you might see a specific error pop up more often + after the change point. + + When comparing anomalies and changes in timeseries, first, zoom in to a smaller + time window, at least 30 minutes before and 30 minutes after the change + occured. E.g., if the anomaly occured at 2023-07-05T08:15:00.000Z, request a + time window that starts at 2023-07-05T07:45:00.000Z and ends at + 2023-07-05T08:45:00.000Z. When comparing changes in different timeseries and + anomalies to determine a correlation, make sure to compare the timestamps. If + in doubt, rate the likelihood of them being related, given the time difference, + between 1 and 10. If below 5, assume it's not related. Mention this likelihood + (and the time difference) to the user. + + Your goal is to help the user determine the root cause of an issue quickly and + transparently. If you see a change or + anomaly in a metric for a service, try to find similar changes in the metrics + for the traffic to its downstream dependencies, by comparing transaction + metrics to span metrics. To inspect the traffic from one service to a + downstream dependency, first get the downstream dependencies for a service, + then get the span metrics from that service (\`service.name\`) to its + downstream dependency (\`span.destination.service.resource\`). For instance, + for an anomaly in throughput, first inspect \`transaction_throughput\` for + \`service.name\`. Then, inspect \`exit_span_throughput\` for its downstream + dependencies, by grouping by \`span.destination.service.resource\`. Repeat this + process over the next service its downstream dependencies until you identify a + root cause. If you can not find any similar changes, use correlations or + grouping to find attributes that could be causes for the change.`, + }); + }; +} diff --git a/x-pack/plugins/apm/server/lib/apm_telemetry/schema.ts b/x-pack/plugins/apm/server/lib/apm_telemetry/schema.ts index e52be12ca5c36..33a95f246f86b 100644 --- a/x-pack/plugins/apm/server/lib/apm_telemetry/schema.ts +++ b/x-pack/plugins/apm/server/lib/apm_telemetry/schema.ts @@ -373,6 +373,13 @@ const apmPerAgentSchema: Pick< 'Total number of services utilizing the opentelemetry/swift agent within the last day', }, }, + 'opentelemetry/android': { + type: 'long', + _meta: { + description: + 'Total number of services utilizing the opentelemetry/android agent within the last day', + }, + }, 'opentelemetry/webjs': { type: 'long', _meta: { diff --git a/x-pack/plugins/apm/server/plugin.ts b/x-pack/plugins/apm/server/plugin.ts index 07010d5f8dc5b..220bdebbf32d7 100644 --- a/x-pack/plugins/apm/server/plugin.ts +++ b/x-pack/plugins/apm/server/plugin.ts @@ -49,6 +49,7 @@ import { scheduleSourceMapMigration } from './routes/source_maps/schedule_source import { createApmSourceMapIndexTemplate } from './routes/source_maps/create_apm_source_map_index_template'; import { addApiKeysToEveryPackagePolicyIfMissing } from './routes/fleet/api_keys/add_api_keys_to_policies_if_missing'; import { apmTutorialCustomIntegration } from '../common/tutorial/tutorials'; +import { registerAssistantFunctions } from './assistant_functions'; export class APMPlugin implements @@ -167,6 +168,8 @@ export class APMPlugin APM_SERVER_FEATURE_ID ); + const kibanaVersion = this.initContext.env.packageInfo.version; + registerRoutes({ core: { setup: core, @@ -179,7 +182,7 @@ export class APMPlugin ruleDataClient, plugins: resourcePlugins, telemetryUsageCounter, - kibanaVersion: this.initContext.env.packageInfo.version, + kibanaVersion, }); const { getApmIndices } = plugins.apmDataAccess; @@ -230,6 +233,18 @@ export class APMPlugin this.logger?.error(e); }); + plugins.observabilityAIAssistant.service.register( + registerAssistantFunctions({ + config: this.currentConfig!, + coreSetup: core, + featureFlags: this.currentConfig!.featureFlags, + kibanaVersion, + logger: this.logger.get('assistant'), + plugins: resourcePlugins, + ruleDataClient, + }) + ); + return { config$ }; } diff --git a/x-pack/plugins/apm/server/routes/assistant_functions/route.ts b/x-pack/plugins/apm/server/routes/assistant_functions/route.ts index df7bb7e7a146d..5aed2451858a9 100644 --- a/x-pack/plugins/apm/server/routes/assistant_functions/route.ts +++ b/x-pack/plugins/apm/server/routes/assistant_functions/route.ts @@ -4,21 +4,13 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import datemath from '@elastic/datemath'; import { ElasticsearchClient } from '@kbn/core/server'; import * as t from 'io-ts'; import { omit } from 'lodash'; -import { ApmDocumentType } from '../../../common/document_type'; -import { ENVIRONMENT_ALL } from '../../../common/environment_filter_values'; -import { RollupInterval } from '../../../common/rollup'; -import { ServiceHealthStatus } from '../../../common/service_health_status'; -import type { APMError } from '../../../typings/es_schemas/ui/apm_error'; import { getApmAlertsClient } from '../../lib/helpers/get_apm_alerts_client'; import { getApmEventClient } from '../../lib/helpers/get_apm_event_client'; import { getMlClient } from '../../lib/helpers/get_ml_client'; -import { getRandomSampler } from '../../lib/helpers/get_random_sampler'; import { createApmServerRoute } from '../apm_routes/create_apm_server_route'; -import { getServicesItems } from '../services/get_services/get_services_items'; import { CorrelationValue, correlationValuesRouteRt, @@ -29,7 +21,6 @@ import { getAssistantDownstreamDependencies, type APMDownstreamDependency, } from './get_apm_downstream_dependencies'; -import { errorRouteRt, getApmErrorDocument } from './get_apm_error_document'; import { getApmServiceSummary, serviceSummaryRouteRt, @@ -167,130 +158,9 @@ const getApmCorrelationValuesRoute = createApmServerRoute({ }, }); -const getApmErrorDocRoute = createApmServerRoute({ - endpoint: 'GET /internal/apm/assistant/get_error_document', - params: t.type({ - query: errorRouteRt, - }), - options: { - tags: ['access:apm'], - }, - handler: async ( - resources - ): Promise<{ content: Array> }> => { - const { params } = resources; - const apmEventClient = await getApmEventClient(resources); - const { query } = params; - - return { - content: await getApmErrorDocument({ - apmEventClient, - arguments: query, - }), - }; - }, -}); - -export interface ApmServicesListItem { - 'service.name': string; - 'agent.name'?: string; - 'transaction.type'?: string; - alertsCount: number; - healthStatus: ServiceHealthStatus; - 'service.environment'?: string[]; -} - -type ApmServicesListContent = ApmServicesListItem[]; - -const getApmServicesListRoute = createApmServerRoute({ - endpoint: 'POST /internal/apm/assistant/get_services_list', - params: t.type({ - body: t.intersection([ - t.type({ - start: t.string, - end: t.string, - }), - t.partial({ - 'service.environment': t.string, - healthStatus: t.array( - t.union([ - t.literal(ServiceHealthStatus.unknown), - t.literal(ServiceHealthStatus.healthy), - t.literal(ServiceHealthStatus.warning), - t.literal(ServiceHealthStatus.critical), - ]) - ), - }), - ]), - }), - options: { - tags: ['access:apm'], - }, - handler: async (resources): Promise<{ content: ApmServicesListContent }> => { - const { params } = resources; - const { body } = params; - - const { healthStatus } = body; - - const [apmEventClient, apmAlertsClient, mlClient, randomSampler] = - await Promise.all([ - getApmEventClient(resources), - getApmAlertsClient(resources), - getMlClient(resources), - getRandomSampler({ - security: resources.plugins.security, - probability: 1, - request: resources.request, - }), - ]); - - const start = datemath.parse(body.start)?.valueOf()!; - const end = datemath.parse(body.end)?.valueOf()!; - - const serviceItems = await getServicesItems({ - apmAlertsClient, - apmEventClient, - documentType: ApmDocumentType.TransactionMetric, - start, - end, - environment: body['service.environment'] || ENVIRONMENT_ALL.value, - kuery: '', - logger: resources.logger, - randomSampler, - rollupInterval: RollupInterval.OneMinute, - serviceGroup: null, - mlClient, - useDurationSummary: false, - }); - - let mappedItems = serviceItems.items.map((item): ApmServicesListItem => { - return { - 'service.name': item.serviceName, - 'agent.name': item.agentName, - alertsCount: item.alertsCount ?? 0, - healthStatus: item.healthStatus ?? ServiceHealthStatus.unknown, - 'service.environment': item.environments, - 'transaction.type': item.transactionType, - }; - }); - - if (healthStatus && healthStatus.length) { - mappedItems = mappedItems.filter((item): boolean => - healthStatus.includes(item.healthStatus) - ); - } - - return { - content: mappedItems, - }; - }, -}); - export const assistantRouteRepository = { ...getApmTimeSeriesRoute, ...getApmServiceSummaryRoute, - ...getApmErrorDocRoute, ...getApmCorrelationValuesRoute, ...getDownstreamDependenciesRoute, - ...getApmServicesListRoute, }; diff --git a/x-pack/plugins/apm/server/routes/fleet/get_latest_apm_package.ts b/x-pack/plugins/apm/server/routes/fleet/get_latest_apm_package.ts index 43528c037222e..efa6c1ee2d593 100644 --- a/x-pack/plugins/apm/server/routes/fleet/get_latest_apm_package.ts +++ b/x-pack/plugins/apm/server/routes/fleet/get_latest_apm_package.ts @@ -21,7 +21,7 @@ export async function getLatestApmPackage({ APM_PACKAGE_NAME ); const packageInfo = - 'buffer' in latestPackage + 'getBuffer' in latestPackage ? (await packageClient.readBundledPackage(latestPackage)).packageInfo : latestPackage; const { diff --git a/x-pack/plugins/apm/server/routes/fleet/merge_package_policy_with_apm.ts b/x-pack/plugins/apm/server/routes/fleet/merge_package_policy_with_apm.ts index ea6e1436f00d0..1cd2ebfc7542a 100644 --- a/x-pack/plugins/apm/server/routes/fleet/merge_package_policy_with_apm.ts +++ b/x-pack/plugins/apm/server/routes/fleet/merge_package_policy_with_apm.ts @@ -28,7 +28,7 @@ export async function decoratePackagePolicyWithAgentConfigAndSourceMap({ apmIndices: APMIndices; }) { const [agentConfigurations, { artifacts }] = await Promise.all([ - listConfigurations(internalESClient, apmIndices), + listConfigurations({ internalESClient, apmIndices }), listSourceMapArtifacts({ fleetPluginStart }), ]); diff --git a/x-pack/plugins/apm/server/routes/fleet/sync_agent_configs_to_apm_package_policies.ts b/x-pack/plugins/apm/server/routes/fleet/sync_agent_configs_to_apm_package_policies.ts index ce8d4421dfc46..56f124ec150ba 100644 --- a/x-pack/plugins/apm/server/routes/fleet/sync_agent_configs_to_apm_package_policies.ts +++ b/x-pack/plugins/apm/server/routes/fleet/sync_agent_configs_to_apm_package_policies.ts @@ -39,7 +39,7 @@ export async function syncAgentConfigsToApmPackagePolicies({ const [savedObjectsClient, agentConfigurations, packagePolicies] = await Promise.all([ getInternalSavedObjectsClient(coreStart), - listConfigurations(internalESClient, apmIndices), + listConfigurations({ internalESClient, apmIndices }), getApmPackagePolicies({ coreStart, fleetPluginStart }), ]); diff --git a/x-pack/plugins/apm/server/routes/historical_data/has_historical_agent_data.ts b/x-pack/plugins/apm/server/routes/historical_data/has_historical_agent_data.ts index befd25abb22f2..4968ceba47e68 100644 --- a/x-pack/plugins/apm/server/routes/historical_data/has_historical_agent_data.ts +++ b/x-pack/plugins/apm/server/routes/historical_data/has_historical_agent_data.ts @@ -8,10 +8,29 @@ import { ProcessorEvent } from '@kbn/observability-plugin/common'; import { APMEventClient } from '../../lib/helpers/create_es_client/create_apm_event_client'; -// Note: this logic is duplicated in tutorials/apm/envs/on_prem export async function hasHistoricalAgentData(apmEventClient: APMEventClient) { + const hasDataInWarmOrHotDataTiers = await hasDataRequest(apmEventClient, [ + 'data_hot', + 'data_warm', + ]); + + if (hasDataInWarmOrHotDataTiers) { + return true; + } + + const hasDataUnbounded = await hasDataRequest(apmEventClient); + + return hasDataUnbounded; +} + +type DataTier = 'data_hot' | 'data_warm' | 'data_cold' | 'data_frozen'; +async function hasDataRequest( + apmEventClient: APMEventClient, + dataTiers?: DataTier[] +) { + const query = dataTiers ? { terms: { _tier: dataTiers } } : undefined; + const params = { - terminate_after: 1, apm: { events: [ ProcessorEvent.error, @@ -20,8 +39,10 @@ export async function hasHistoricalAgentData(apmEventClient: APMEventClient) { ], }, body: { + terminate_after: 1, track_total_hits: 1, size: 0, + query, }, }; diff --git a/x-pack/plugins/apm/server/routes/services/__snapshots__/queries.test.ts.snap b/x-pack/plugins/apm/server/routes/services/__snapshots__/queries.test.ts.snap deleted file mode 100644 index 03c3f038ad311..0000000000000 --- a/x-pack/plugins/apm/server/routes/services/__snapshots__/queries.test.ts.snap +++ /dev/null @@ -1,313 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`services queries fetches the agent status 1`] = ` -Object { - "apm": Object { - "events": Array [ - "error", - "metric", - "transaction", - ], - }, - "body": Object { - "size": 0, - "track_total_hits": 1, - }, - "terminate_after": 1, -} -`; - -exports[`services queries fetches the service agent name 1`] = ` -Object { - "apm": Object { - "events": Array [ - "error", - "transaction", - "metric", - ], - }, - "body": Object { - "_source": Array [ - "agent.name", - "service.runtime.name", - "cloud.provider", - "cloud.service.name", - ], - "query": Object { - "bool": Object { - "filter": Array [ - Object { - "term": Object { - "service.name": "foo", - }, - }, - Object { - "range": Object { - "@timestamp": Object { - "format": "epoch_millis", - "gte": 0, - "lte": 50000, - }, - }, - }, - Object { - "exists": Object { - "field": "agent.name", - }, - }, - ], - "should": Array [ - Object { - "exists": Object { - "field": "service.runtime.name", - }, - }, - Object { - "exists": Object { - "field": "cloud.provider", - }, - }, - Object { - "exists": Object { - "field": "cloud.service.name", - }, - }, - ], - }, - }, - "size": 1, - "sort": Object { - "_score": Object { - "order": "desc", - }, - }, - "track_total_hits": 1, - }, - "terminate_after": 1, -} -`; - -exports[`services queries fetches the service items 1`] = ` -Array [ - Object { - "apm": Object { - "sources": Array [ - Object { - "documentType": "transactionEvent", - "rollupInterval": "none", - }, - ], - }, - "body": Object { - "aggs": Object { - "sample": Object { - "aggs": Object { - "overflowCount": Object { - "sum": Object { - "field": "service_transaction.aggregation.overflow_count", - }, - }, - "services": Object { - "aggs": Object { - "transactionType": Object { - "aggs": Object { - "avg_duration": Object { - "avg": Object { - "field": "transaction.duration.us", - }, - }, - "environments": Object { - "terms": Object { - "field": "service.environment", - }, - }, - "sample": Object { - "top_metrics": Object { - "metrics": Array [ - Object { - "field": "agent.name", - }, - ], - "sort": Object { - "@timestamp": "desc", - }, - }, - }, - "successful": Object { - "filter": Object { - "bool": Object { - "filter": Array [ - Object { - "terms": Object { - "event.outcome": Array [ - "success", - ], - }, - }, - ], - }, - }, - }, - "successful_or_failed": Object { - "filter": Object { - "bool": Object { - "filter": Array [ - Object { - "terms": Object { - "event.outcome": Array [ - "failure", - "success", - ], - }, - }, - ], - }, - }, - }, - }, - "terms": Object { - "field": "transaction.type", - }, - }, - }, - "terms": Object { - "field": "service.name", - "size": 1000, - }, - }, - }, - "random_sampler": Object { - "probability": 1, - "seed": 0, - }, - }, - }, - "query": Object { - "bool": Object { - "filter": Array [ - Object { - "range": Object { - "@timestamp": Object { - "format": "epoch_millis", - "gte": 0, - "lte": 50000, - }, - }, - }, - ], - }, - }, - "size": 0, - "track_total_hits": false, - }, - }, - Object { - "apm": Object { - "events": Array [ - "metric", - "error", - ], - }, - "body": Object { - "aggs": Object { - "sample": Object { - "aggs": Object { - "services": Object { - "aggs": Object { - "environments": Object { - "terms": Object { - "field": "service.environment", - }, - }, - "latest": Object { - "top_metrics": Object { - "metrics": Array [ - Object { - "field": "agent.name", - }, - ], - "sort": Object { - "@timestamp": "desc", - }, - }, - }, - }, - "terms": Object { - "field": "service.name", - "size": 1000, - }, - }, - }, - "random_sampler": Object { - "probability": 1, - "seed": 0, - }, - }, - }, - "query": Object { - "bool": Object { - "filter": Array [ - Object { - "range": Object { - "@timestamp": Object { - "format": "epoch_millis", - "gte": 0, - "lte": 50000, - }, - }, - }, - ], - }, - }, - "size": 0, - "track_total_hits": false, - }, - }, - undefined, -] -`; - -exports[`services queries fetches the service transaction types 1`] = ` -Object { - "apm": Object { - "sources": Array [ - Object { - "documentType": "transactionMetric", - "rollupInterval": "1m", - }, - ], - }, - "body": Object { - "aggs": Object { - "types": Object { - "terms": Object { - "field": "transaction.type", - "size": 100, - }, - }, - }, - "query": Object { - "bool": Object { - "filter": Array [ - Object { - "term": Object { - "service.name": "foo", - }, - }, - Object { - "range": Object { - "@timestamp": Object { - "format": "epoch_millis", - "gte": 0, - "lte": 50000, - }, - }, - }, - ], - }, - }, - "size": 0, - "track_total_hits": false, - }, -} -`; diff --git a/x-pack/plugins/apm/server/routes/services/queries.test.ts b/x-pack/plugins/apm/server/routes/services/queries.test.ts deleted file mode 100644 index 7d45899aa84c0..0000000000000 --- a/x-pack/plugins/apm/server/routes/services/queries.test.ts +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ApmDocumentType } from '../../../common/document_type'; -import { ENVIRONMENT_ALL } from '../../../common/environment_filter_values'; -import { RollupInterval } from '../../../common/rollup'; -import { - inspectSearchParams, - SearchParamsMock, -} from '../../utils/test_helpers'; -import { hasHistoricalAgentData } from '../historical_data/has_historical_agent_data'; -import { getServicesItems } from './get_services/get_services_items'; -import { getServiceAgent } from './get_service_agent'; -import { getServiceTransactionTypes } from './get_service_transaction_types'; - -describe('services queries', () => { - let mock: SearchParamsMock; - - afterEach(() => { - mock.teardown(); - }); - - it('fetches the service agent name', async () => { - mock = await inspectSearchParams(({ mockApmEventClient }) => - getServiceAgent({ - serviceName: 'foo', - apmEventClient: mockApmEventClient, - start: 0, - end: 50000, - }) - ); - - expect(mock.params).toMatchSnapshot(); - }); - - it('fetches the service transaction types', async () => { - mock = await inspectSearchParams(({ mockApmEventClient }) => - getServiceTransactionTypes({ - serviceName: 'foo', - apmEventClient: mockApmEventClient, - start: 0, - end: 50000, - documentType: ApmDocumentType.TransactionMetric, - rollupInterval: RollupInterval.OneMinute, - }) - ); - - expect(mock.params).toMatchSnapshot(); - }); - - it('fetches the service items', async () => { - mock = await inspectSearchParams( - ({ mockApmEventClient, mockApmAlertsClient }) => - getServicesItems({ - mlClient: undefined, - apmEventClient: mockApmEventClient, - documentType: ApmDocumentType.TransactionEvent, - rollupInterval: RollupInterval.None, - logger: {} as any, - environment: ENVIRONMENT_ALL.value, - kuery: '', - start: 0, - end: 50000, - serviceGroup: null, - randomSampler: { - probability: 1, - seed: 0, - }, - apmAlertsClient: mockApmAlertsClient, - useDurationSummary: false, - }) - ); - - const allParams = mock.spy.mock.calls.map((call) => call[1]); - - expect(allParams).toMatchSnapshot(); - }); - - it('fetches the agent status', async () => { - mock = await inspectSearchParams(({ mockApmEventClient }) => - hasHistoricalAgentData(mockApmEventClient) - ); - - expect(mock.params).toMatchSnapshot(); - }); -}); diff --git a/x-pack/plugins/apm/server/routes/settings/agent_configuration/find_exact_configuration.ts b/x-pack/plugins/apm/server/routes/settings/agent_configuration/find_exact_configuration.ts index 232c3d2d50c46..b8f393bb34406 100644 --- a/x-pack/plugins/apm/server/routes/settings/agent_configuration/find_exact_configuration.ts +++ b/x-pack/plugins/apm/server/routes/settings/agent_configuration/find_exact_configuration.ts @@ -6,7 +6,7 @@ */ import type { SearchHit } from '@kbn/es-types'; -import { APMIndices } from '@kbn/apm-data-access-plugin/server'; +import { APMEventClient } from '../../../lib/helpers/create_es_client/create_apm_event_client'; import { AgentConfiguration } from '../../../../common/agent_configuration/configuration_types'; import { SERVICE_ENVIRONMENT, @@ -15,16 +15,16 @@ import { import { APMInternalESClient } from '../../../lib/helpers/create_es_client/create_internal_es_client'; import { APM_AGENT_CONFIGURATION_INDEX } from '../apm_indices/apm_system_index_constants'; import { convertConfigSettingsToString } from './convert_settings_to_string'; -import { getConfigsAppliedToAgentsThroughFleet } from './get_config_applied_to_agent_through_fleet'; +import { getAgentConfigEtagMetrics } from './get_agent_config_etag_metrics'; export async function findExactConfiguration({ service, internalESClient, - apmIndices, + apmEventClient, }: { service: AgentConfiguration['service']; internalESClient: APMInternalESClient; - apmIndices: APMIndices; + apmEventClient: APMEventClient; }) { const serviceNameFilter = service.name ? { term: { [SERVICE_NAME]: service.name } } @@ -43,13 +43,10 @@ export async function findExactConfiguration({ }, }; - const [agentConfig, configsAppliedToAgentsThroughFleet] = await Promise.all([ - internalESClient.search( - 'find_exact_agent_configuration', - params - ), - getConfigsAppliedToAgentsThroughFleet(internalESClient, apmIndices), - ]); + const agentConfig = await internalESClient.search< + AgentConfiguration, + typeof params + >('find_exact_agent_configuration', params); const hit = agentConfig.hits.hits[0] as | SearchHit @@ -59,11 +56,33 @@ export async function findExactConfiguration({ return; } + const appliedByAgent = await getIsAppliedByAgent({ + apmEventClient, + agentConfiguration: hit._source, + }); + return { id: hit._id, ...convertConfigSettingsToString(hit)._source, - applied_by_agent: - hit._source.applied_by_agent || - configsAppliedToAgentsThroughFleet.hasOwnProperty(hit._source.etag), + applied_by_agent: appliedByAgent, }; } + +async function getIsAppliedByAgent({ + apmEventClient, + agentConfiguration, +}: { + apmEventClient: APMEventClient; + agentConfiguration: AgentConfiguration; +}) { + if (agentConfiguration.applied_by_agent) { + return true; + } + + const appliedEtags = await getAgentConfigEtagMetrics( + apmEventClient, + agentConfiguration.etag + ); + + return appliedEtags.includes(agentConfiguration.etag); +} diff --git a/x-pack/plugins/apm/server/routes/settings/agent_configuration/get_config_applied_to_agent_through_fleet.ts b/x-pack/plugins/apm/server/routes/settings/agent_configuration/get_agent_config_etag_metrics.ts similarity index 55% rename from x-pack/plugins/apm/server/routes/settings/agent_configuration/get_config_applied_to_agent_through_fleet.ts rename to x-pack/plugins/apm/server/routes/settings/agent_configuration/get_agent_config_etag_metrics.ts index 067d7565247b9..fccf26f591ce3 100644 --- a/x-pack/plugins/apm/server/routes/settings/agent_configuration/get_config_applied_to_agent_through_fleet.ts +++ b/x-pack/plugins/apm/server/routes/settings/agent_configuration/get_agent_config_etag_metrics.ts @@ -7,23 +7,26 @@ import { termQuery, rangeQuery } from '@kbn/observability-plugin/server'; import datemath from '@kbn/datemath'; -import { APMIndices } from '@kbn/apm-data-access-plugin/server'; +import { ProcessorEvent } from '@kbn/observability-plugin/common'; +import { APMEventClient } from '../../../lib/helpers/create_es_client/create_apm_event_client'; import { METRICSET_NAME } from '../../../../common/es_fields/apm'; -import { APMInternalESClient } from '../../../lib/helpers/create_es_client/create_internal_es_client'; -export async function getConfigsAppliedToAgentsThroughFleet( - internalESClient: APMInternalESClient, - apmIndices: APMIndices +export async function getAgentConfigEtagMetrics( + apmEventClient: APMEventClient, + etag?: string ) { const params = { - index: apmIndices.metric, - track_total_hits: 0, - size: 0, + apm: { + events: [ProcessorEvent.metric], + }, body: { + track_total_hits: 0, + size: 0, query: { bool: { filter: [ ...termQuery(METRICSET_NAME, 'agent_config'), + ...termQuery('labels.etag', etag), ...rangeQuery( datemath.parse('now-15m')!.valueOf(), datemath.parse('now')!.valueOf() @@ -42,18 +45,14 @@ export async function getConfigsAppliedToAgentsThroughFleet( }, }; - const response = await internalESClient.search( - 'get_config_applied_to_agent_through_fleet', + const response = await apmEventClient.search( + 'get_agent_config_etag_metrics', params ); return ( - response.aggregations?.config_by_etag.buckets.reduce( - (configsAppliedToAgentsThroughFleet, bucket) => { - configsAppliedToAgentsThroughFleet[bucket.key as string] = true; - return configsAppliedToAgentsThroughFleet; - }, - {} as Record - ) ?? {} + response.aggregations?.config_by_etag.buckets.map( + ({ key }) => key as string + ) ?? [] ); } diff --git a/x-pack/plugins/apm/server/routes/settings/agent_configuration/list_configurations.ts b/x-pack/plugins/apm/server/routes/settings/agent_configuration/list_configurations.ts index 940ac7cd0b5a5..4566629bebd85 100644 --- a/x-pack/plugins/apm/server/routes/settings/agent_configuration/list_configurations.ts +++ b/x-pack/plugins/apm/server/routes/settings/agent_configuration/list_configurations.ts @@ -6,27 +6,33 @@ */ import { APMIndices } from '@kbn/apm-data-access-plugin/server'; +import { APMEventClient } from '../../../lib/helpers/create_es_client/create_apm_event_client'; import { AgentConfiguration } from '../../../../common/agent_configuration/configuration_types'; import { convertConfigSettingsToString } from './convert_settings_to_string'; -import { getConfigsAppliedToAgentsThroughFleet } from './get_config_applied_to_agent_through_fleet'; +import { getAgentConfigEtagMetrics } from './get_agent_config_etag_metrics'; import { APMInternalESClient } from '../../../lib/helpers/create_es_client/create_internal_es_client'; import { APM_AGENT_CONFIGURATION_INDEX } from '../apm_indices/apm_system_index_constants'; -export async function listConfigurations( - internalESClient: APMInternalESClient, - apmIndices: APMIndices -) { +export async function listConfigurations({ + internalESClient, + apmEventClient, + apmIndices, +}: { + internalESClient: APMInternalESClient; + apmEventClient?: APMEventClient; + apmIndices: APMIndices; +}) { const params = { index: APM_AGENT_CONFIGURATION_INDEX, size: 200, }; - const [agentConfigs, configsAppliedToAgentsThroughFleet] = await Promise.all([ + const [agentConfigs, appliedEtags = []] = await Promise.all([ internalESClient.search( 'list_agent_configuration', params ), - getConfigsAppliedToAgentsThroughFleet(internalESClient, apmIndices), + apmEventClient ? getAgentConfigEtagMetrics(apmEventClient) : undefined, ]); return agentConfigs.hits.hits @@ -36,7 +42,7 @@ export async function listConfigurations( ...hit._source, applied_by_agent: hit._source.applied_by_agent || - configsAppliedToAgentsThroughFleet.hasOwnProperty(hit._source.etag), + appliedEtags.includes(hit._source.etag), }; }); } diff --git a/x-pack/plugins/apm/server/routes/settings/agent_configuration/queries.test.ts b/x-pack/plugins/apm/server/routes/settings/agent_configuration/queries.test.ts index 5a26721150660..176c0ef8e687a 100644 --- a/x-pack/plugins/apm/server/routes/settings/agent_configuration/queries.test.ts +++ b/x-pack/plugins/apm/server/routes/settings/agent_configuration/queries.test.ts @@ -55,7 +55,10 @@ describe('agent configuration queries', () => { it('fetches configurations', async () => { mock = await inspectSearchParams( ({ mockInternalESClient, mockIndices }) => - listConfigurations(mockInternalESClient, mockIndices) + listConfigurations({ + internalESClient: mockInternalESClient, + apmIndices: mockIndices, + }) ); expect(mock.params).toMatchSnapshot(); @@ -94,11 +97,11 @@ describe('agent configuration queries', () => { describe('findExactConfiguration', () => { it('find configuration by service.name', async () => { mock = await inspectSearchParams( - ({ mockInternalESClient, mockIndices }) => + ({ mockInternalESClient, mockApmEventClient }) => findExactConfiguration({ service: { name: 'foo' }, internalESClient: mockInternalESClient, - apmIndices: mockIndices, + apmEventClient: mockApmEventClient, }) ); @@ -107,11 +110,11 @@ describe('agent configuration queries', () => { it('find configuration by service.environment', async () => { mock = await inspectSearchParams( - ({ mockInternalESClient, mockIndices }) => + ({ mockInternalESClient, mockApmEventClient }) => findExactConfiguration({ service: { environment: 'bar' }, internalESClient: mockInternalESClient, - apmIndices: mockIndices, + apmEventClient: mockApmEventClient, }) ); @@ -120,11 +123,11 @@ describe('agent configuration queries', () => { it('find configuration by service.name and service.environment', async () => { mock = await inspectSearchParams( - ({ mockInternalESClient, mockIndices }) => + ({ mockInternalESClient, mockApmEventClient }) => findExactConfiguration({ service: { name: 'foo', environment: 'bar' }, internalESClient: mockInternalESClient, - apmIndices: mockIndices, + apmEventClient: mockApmEventClient, }) ); diff --git a/x-pack/plugins/apm/server/routes/settings/agent_configuration/route.ts b/x-pack/plugins/apm/server/routes/settings/agent_configuration/route.ts index b2c06044838ff..b47a4536191d2 100644 --- a/x-pack/plugins/apm/server/routes/settings/agent_configuration/route.ts +++ b/x-pack/plugins/apm/server/routes/settings/agent_configuration/route.ts @@ -50,13 +50,15 @@ const agentConfigurationRoute = createApmServerRoute({ throwNotFoundIfAgentConfigNotAvailable(resources.featureFlags); const apmIndices = await resources.getApmIndices(); - const internalESClient = await createInternalESClientWithResources( - resources - ); - const configurations = await listConfigurations( + const [internalESClient, apmEventClient] = await Promise.all([ + createInternalESClientWithResources(resources), + getApmEventClient(resources), + ]); + const configurations = await listConfigurations({ internalESClient, - apmIndices - ); + apmIndices, + apmEventClient, + }); return { configurations }; }, @@ -75,14 +77,14 @@ const getSingleAgentConfigurationRoute = createApmServerRoute({ const { params, logger } = resources; const { name, environment } = params.query; const service = { name, environment }; - const apmIndices = await resources.getApmIndices(); - const internalESClient = await createInternalESClientWithResources( - resources - ); + const [internalESClient, apmEventClient] = await Promise.all([ + createInternalESClientWithResources(resources), + getApmEventClient(resources), + ]); const exactConfig = await findExactConfiguration({ service, internalESClient, - apmIndices, + apmEventClient, }); if (!exactConfig) { @@ -114,13 +116,14 @@ const deleteAgentConfigurationRoute = createApmServerRoute({ const { params, logger, core, telemetryUsageCounter } = resources; const { service } = params.body; const apmIndices = await resources.getApmIndices(); - const internalESClient = await createInternalESClientWithResources( - resources - ); + const [internalESClient, apmEventClient] = await Promise.all([ + createInternalESClientWithResources(resources), + getApmEventClient(resources), + ]); const exactConfig = await findExactConfiguration({ service, internalESClient, - apmIndices, + apmEventClient, }); if (!exactConfig) { logger.info( @@ -171,16 +174,17 @@ const createOrUpdateAgentConfigurationRoute = createApmServerRoute({ const { params, logger, core, telemetryUsageCounter } = resources; const { body, query } = params; const apmIndices = await resources.getApmIndices(); - const internalESClient = await createInternalESClientWithResources( - resources - ); + const [internalESClient, apmEventClient] = await Promise.all([ + createInternalESClientWithResources(resources), + getApmEventClient(resources), + ]); // if the config already exists, it is fetched and updated // this is to avoid creating two configs with identical service params const exactConfig = await findExactConfiguration({ service: body.service, internalESClient, - apmIndices, + apmEventClient, }); // if the config exists ?overwrite=true is required diff --git a/x-pack/plugins/apm/server/types.ts b/x-pack/plugins/apm/server/types.ts index 10687e3757849..fa77e52e76836 100644 --- a/x-pack/plugins/apm/server/types.ts +++ b/x-pack/plugins/apm/server/types.ts @@ -65,6 +65,10 @@ import { ProfilingDataAccessPluginSetup, ProfilingDataAccessPluginStart, } from '@kbn/profiling-data-access-plugin/server'; +import type { + ObservabilityAIAssistantPluginSetup, + ObservabilityAIAssistantPluginStart, +} from '@kbn/observability-ai-assistant-plugin/server'; import { APMConfig } from '.'; export interface APMPluginSetup { @@ -82,7 +86,7 @@ export interface APMPluginSetupDependencies { metricsDataAccess: MetricsDataPluginSetup; dataViews: {}; share: SharePluginSetup; - + observabilityAIAssistant: ObservabilityAIAssistantPluginSetup; // optional dependencies actions?: ActionsPlugin['setup']; alerting?: AlertingPlugin['setup']; @@ -108,7 +112,7 @@ export interface APMPluginStartDependencies { metricsDataAccess: MetricsDataPluginSetup; dataViews: DataViewsServerPluginStart; share: undefined; - + observabilityAIAssistant: ObservabilityAIAssistantPluginStart; // optional dependencies actions?: ActionsPlugin['start']; alerting?: AlertingPlugin['start']; diff --git a/x-pack/plugins/apm/public/utils/non_empty_string_ref.ts b/x-pack/plugins/apm/server/utils/non_empty_string_ref.ts similarity index 100% rename from x-pack/plugins/apm/public/utils/non_empty_string_ref.ts rename to x-pack/plugins/apm/server/utils/non_empty_string_ref.ts diff --git a/x-pack/plugins/apm/tsconfig.json b/x-pack/plugins/apm/tsconfig.json index db829dc3ed5f8..ba23f19f78c4a 100644 --- a/x-pack/plugins/apm/tsconfig.json +++ b/x-pack/plugins/apm/tsconfig.json @@ -106,7 +106,8 @@ "@kbn/custom-icons", "@kbn/elastic-agent-utils", "@kbn/shared-ux-link-redirect-app", - "@kbn/observability-get-padded-alert-time-range-util" + "@kbn/observability-get-padded-alert-time-range-util", + "@kbn/core-lifecycle-server" ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/apm/typings/es_schemas/raw/span_raw.ts b/x-pack/plugins/apm/typings/es_schemas/raw/span_raw.ts index 1cac68f74b8b7..301a4c96dfa35 100644 --- a/x-pack/plugins/apm/typings/es_schemas/raw/span_raw.ts +++ b/x-pack/plugins/apm/typings/es_schemas/raw/span_raw.ts @@ -71,6 +71,9 @@ export interface SpanRaw extends APMBaseDoc { id: string; }; child?: { id: string[] }; + code?: { + stacktrace?: string; + }; http?: Http; url?: Url; } diff --git a/x-pack/plugins/asset_manager/docs/api.md b/x-pack/plugins/asset_manager/docs/api.md index 66a1984297c16..60810dce22911 100644 --- a/x-pack/plugins/asset_manager/docs/api.md +++ b/x-pack/plugins/asset_manager/docs/api.md @@ -41,7 +41,7 @@ publicMethodA(...options: MethodAPublicOptions) injects some internal dependencies from the plugin's config on your behalf ``` -The public and server clientss are both accessible to plugin dependants, but the REST API is NOT. +The public and server clients are both accessible to plugin dependants, but the REST API is NOT. ### Required dependency setup @@ -175,14 +175,40 @@ Get a list of host assets found within a specified time range. | :-------- | :-------------- | :-------- | :--------------------------------------------------------------------- | | from | datetime string | yes | ISO date string representing the START of the time range being queried | | to | datetime string | yes | ISO date string representing the END of the time range being queried | +| filters | object | no | key/value pairs filtering the assets returned. See [supported filters](https://github.com/klacabane/kibana/blob/main/x-pack/plugins/asset_manager/common/types_api.ts#L168-L178) | + +**Example** + +```js +getHosts({ + from: '2023-12-04T09:42:00.000Z', + to: '2023-12-04T09:44:00.000Z', + filters: { + 'cloud.provider': 'gcp' + } +}) +``` **Response** ```json { - "hosts": [ - ...found host assets - ] + "hosts": [ + { + "@timestamp": "2023-12-04T09:42:52.538Z", + "asset.kind": "host", + "asset.id": "gcp-host-2zze1241", + "asset.name": "gcp-host-2zze1241", + "asset.ean": "host:gcp-host-2zze1241", + "cloud.provider": "gcp", + "cloud.instance.id": "235111598995020799", + "cloud.service.name": "GCE", + "cloud.region": "us-central1", + "asset.children": [ + "pod:3ca933f5-effa-47f6-b991-fa1b528cc2e4" + ] + } + ] } ``` @@ -194,14 +220,159 @@ Get a list of service assets found within a specified time range. | :-------- | :-------------- | :-------- | :--------------------------------------------------------------------- | | from | datetime string | yes | ISO date string representing the START of the time range being queried | | to | datetime string | yes | ISO date string representing the END of the time range being queried | -| parent | string | no | EAN value for a given parent service to filter services by | +| filters | object | no | key/value pairs filtering the assets returned. See [supported filters](https://github.com/klacabane/kibana/blob/main/x-pack/plugins/asset_manager/common/types_api.ts#L168-L178) | + +**Example** + +```js +getServices({ + from: '2023-12-04T09:42:00.000Z', + to: '2023-12-04T09:44:00.000Z' +}) +``` + +**Response** + +```json +{ + "services": [ + { + "@timestamp": "2023-12-03T09:44:00.000Z", + "asset.kind": "service", + "asset.id": "adservice", + "asset.ean": "service:adservice", + "asset.parents": [ + "container:50041db622002301a71b15e3c8468aa2b0cdb716b2ebd3091e20975580a397ae" + ] + } + ] +} +``` + +#### getContainers + +Get a list of container assets found within a specified time range. + +| Parameter | Type | Required? | Description | +| :-------- | :-------------- | :-------- | :--------------------------------------------------------------------- | +| from | datetime string | yes | ISO date string representing the START of the time range being queried | +| to | datetime string | yes | ISO date string representing the END of the time range being queried | +| filters | object | no | key/value pairs filtering the assets returned. See [supported filters](https://github.com/klacabane/kibana/blob/main/x-pack/plugins/asset_manager/common/types_api.ts#L168-L178) | + +**Example** + +```js +getContainers({ + from: '2023-12-04T09:42:00.000Z', + to: '2023-12-04T09:44:00.000Z' +}) +``` + +**Response** + +```json +{ + "containers": [ + { + "@timestamp": "2023-12-03T09:42:11.427Z", + "asset.kind": "container", + "asset.id": "040744a58fdbdf9a7d628c4e71842301ccc3ed54a1efa8ff47747af251e5896c", + "asset.ean": "container:040744a58fdbdf9a7d628c4e71842301ccc3ed54a1efa8ff47747af251e5896c", + "asset.parents": [ + "pod:928db0ae-2b63-48f3-8725-a8ddc490d69e" + ], + "asset.references": [ + "host:gcp-host-2zze1241" + ] + } + ] +} +``` + +#### getPods + +Get a list of pod assets found within a specified time range. + +| Parameter | Type | Required? | Description | +| :-------- | :-------------- | :-------- | :--------------------------------------------------------------------- | +| from | datetime string | yes | ISO date string representing the START of the time range being queried | +| to | datetime string | yes | ISO date string representing the END of the time range being queried | +| filters | object | no | key/value pairs filtering the assets returned. See [supported filters](https://github.com/klacabane/kibana/blob/main/x-pack/plugins/asset_manager/common/types_api.ts#L168-L178) | + +**Example** + +```js +getPods({ + from: '2023-12-04T09:42:00.000Z', + to: '2023-12-04T09:44:00.000Z' +}) +``` + +**Response** + +```json +{ + "pods": [ + { + "@timestamp": "2023-12-03T09:42:14.680Z", + "asset.kind": "pod", + "asset.id": "00b2bad4-b878-4647-aa00-d8b24c9216f1", + "asset.ean": "pod:00b2bad4-b878-4647-aa00-d8b24c9216f1", + "asset.parents": [ + "host:gcp-host-2zze1241" + ], + "cloud.provider": "gcp" + } + ] +} +``` + +#### getAssets + +Get a list of service assets found within a specified time range, sorted by timestamp desc. + +| Parameter | Type | Required? | Description | +| :-------- | :-------------- | :-------- | :--------------------------------------------------------------------- | +| from | datetime string | yes | ISO date string representing the START of the time range being queried | +| to | datetime string | yes | ISO date string representing the END of the time range being queried | +| filters | object | no | key/value pairs filtering the assets returned. See [supported filters](https://github.com/klacabane/kibana/blob/main/x-pack/plugins/asset_manager/common/types_api.ts#L168-L178) | + +**Example** + +```js +getAssets({ + from: '2023-12-04T09:42:00.000Z', + to: '2023-12-04T09:44:00.000Z' +}) +``` **Response** ```json { - "services": [ - ...found service assets - ] + "assets": [ + { + "@timestamp": "2023-12-03T09:44:00.000Z", + "asset.kind": "service", + "asset.id": "adservice", + "asset.ean": "service:adservice", + "asset.parents": [ + "container:50041db622002301a71b15e3c8468aa2b0cdb716b2ebd3091e20975580a397ae" + ] + }, + { + "@timestamp": "2023-12-04T09:42:52.538Z", + "asset.kind": "host", + "asset.id": "gcp-host-2zze1241", + "asset.name": "gcp-host-2zze1241", + "asset.ean": "host:gcp-host-2zze1241", + "cloud.provider": "gcp", + "cloud.instance.id": "235111598995020799", + "cloud.region": "us-central1", + "asset.children": [ + "pod:3ca933f5-effa-47f6-b991-fa1b528cc2e4" + ] + } + ] } ``` diff --git a/x-pack/plugins/asset_manager/server/lib/collectors/containers.ts b/x-pack/plugins/asset_manager/server/lib/collectors/containers.ts index c9d0e8379dbdd..2012e05912487 100644 --- a/x-pack/plugins/asset_manager/server/lib/collectors/containers.ts +++ b/x-pack/plugins/asset_manager/server/lib/collectors/containers.ts @@ -8,6 +8,7 @@ import { estypes } from '@elastic/elasticsearch'; import { Asset } from '../../../common/types_api'; import { CollectorOptions, QUERY_MAX_SIZE } from '.'; +import { extractFieldValue } from '../utils'; export async function collectContainers({ client, @@ -33,6 +34,7 @@ export async function collectContainers({ sort: [{ 'container.id': 'asc' }], _source: false, fields: [ + '@timestamp', 'kubernetes.*', 'cloud.provider', 'orchestrator.cluster.name', @@ -70,14 +72,14 @@ export async function collectContainers({ const assets = esResponse.hits.hits.reduce((acc: Asset[], hit: any) => { const { fields = {} } = hit; - const containerId = fields['container.id']; - const podUid = fields['kubernetes.pod.uid']; - const nodeName = fields['kubernetes.node.name']; + const containerId = extractFieldValue(fields['container.id']); + const podUid = extractFieldValue(fields['kubernetes.pod.uid']); + const nodeName = extractFieldValue(fields['kubernetes.node.name']); const parentEan = podUid ? `pod:${podUid}` : `host:${fields['host.hostname']}`; const container: Asset = { - '@timestamp': new Date().toISOString(), + '@timestamp': extractFieldValue(fields['@timestamp']), 'asset.kind': 'container', 'asset.id': containerId, 'asset.ean': `container:${containerId}`, diff --git a/x-pack/plugins/asset_manager/server/lib/collectors/hosts.ts b/x-pack/plugins/asset_manager/server/lib/collectors/hosts.ts index a082cafc8546c..b624373010f10 100644 --- a/x-pack/plugins/asset_manager/server/lib/collectors/hosts.ts +++ b/x-pack/plugins/asset_manager/server/lib/collectors/hosts.ts @@ -8,6 +8,7 @@ import { estypes } from '@elastic/elasticsearch'; import { Asset } from '../../../common/types_api'; import { CollectorOptions, QUERY_MAX_SIZE } from '.'; +import { extractFieldValue } from '../utils'; export async function collectHosts({ client, @@ -69,14 +70,14 @@ export async function collectHosts({ const assets = esResponse.hits.hits.reduce((acc: Asset[], hit: any) => { const { fields = {} } = hit; - const hostName = fields['host.hostname']; - const k8sNode = fields['kubernetes.node.name']; - const k8sPod = fields['kubernetes.pod.uid']; + const hostName = extractFieldValue(fields['host.hostname']); + const k8sNode = extractFieldValue(fields['kubernetes.node.name']); + const k8sPod = extractFieldValue(fields['kubernetes.pod.uid']); const hostEan = `host:${k8sNode || hostName}`; const host: Asset = { - '@timestamp': new Date().toISOString(), + '@timestamp': extractFieldValue(fields['@timestamp']), 'asset.kind': 'host', 'asset.id': k8sNode || hostName, 'asset.name': k8sNode || hostName, @@ -84,23 +85,23 @@ export async function collectHosts({ }; if (fields['cloud.provider']) { - host['cloud.provider'] = fields['cloud.provider']; + host['cloud.provider'] = extractFieldValue(fields['cloud.provider']); } if (fields['cloud.instance.id']) { - host['cloud.instance.id'] = fields['cloud.instance.id']; + host['cloud.instance.id'] = extractFieldValue(fields['cloud.instance.id']); } if (fields['cloud.service.name']) { - host['cloud.service.name'] = fields['cloud.service.name']; + host['cloud.service.name'] = extractFieldValue(fields['cloud.service.name']); } if (fields['cloud.region']) { - host['cloud.region'] = fields['cloud.region']; + host['cloud.region'] = extractFieldValue(fields['cloud.region']); } if (fields['orchestrator.cluster.name']) { - host['orchestrator.cluster.name'] = fields['orchestrator.cluster.name']; + host['orchestrator.cluster.name'] = extractFieldValue(fields['orchestrator.cluster.name']); } if (k8sPod) { diff --git a/x-pack/plugins/asset_manager/server/lib/collectors/pods.ts b/x-pack/plugins/asset_manager/server/lib/collectors/pods.ts index f9a4e2e22ae51..e78fa106452ca 100644 --- a/x-pack/plugins/asset_manager/server/lib/collectors/pods.ts +++ b/x-pack/plugins/asset_manager/server/lib/collectors/pods.ts @@ -8,6 +8,7 @@ import { estypes } from '@elastic/elasticsearch'; import { Asset } from '../../../common/types_api'; import { CollectorOptions, QUERY_MAX_SIZE } from '.'; +import { extractFieldValue } from '../utils'; export async function collectPods({ client, @@ -37,6 +38,7 @@ export async function collectPods({ sort: [{ 'kubernetes.pod.uid': 'asc' }], _source: false, fields: [ + '@timestamp', 'kubernetes.*', 'cloud.provider', 'orchestrator.cluster.name', @@ -68,12 +70,12 @@ export async function collectPods({ const assets = esResponse.hits.hits.reduce((acc: Asset[], hit: any) => { const { fields = {} } = hit; - const podUid = fields['kubernetes.pod.uid']; - const nodeName = fields['kubernetes.node.name']; - const clusterName = fields['orchestrator.cluster.name']; + const podUid = extractFieldValue(fields['kubernetes.pod.uid']); + const nodeName = extractFieldValue(fields['kubernetes.node.name']); + const clusterName = extractFieldValue(fields['orchestrator.cluster.name']); const pod: Asset = { - '@timestamp': new Date().toISOString(), + '@timestamp': extractFieldValue(fields['@timestamp']), 'asset.kind': 'pod', 'asset.id': podUid, 'asset.ean': `pod:${podUid}`, @@ -81,7 +83,7 @@ export async function collectPods({ }; if (fields['cloud.provider']) { - pod['cloud.provider'] = fields['cloud.provider']; + pod['cloud.provider'] = extractFieldValue(fields['cloud.provider']); } if (clusterName) { diff --git a/x-pack/plugins/asset_manager/server/lib/collectors/services.ts b/x-pack/plugins/asset_manager/server/lib/collectors/services.ts index e000860c9f4c5..4c49f75d13594 100644 --- a/x-pack/plugins/asset_manager/server/lib/collectors/services.ts +++ b/x-pack/plugins/asset_manager/server/lib/collectors/services.ts @@ -74,6 +74,9 @@ export async function collectServices({ ], }, aggs: { + last_seen: { + max: { field: '@timestamp' }, + }, container_and_hosts: { multi_terms: { terms: [ @@ -104,6 +107,7 @@ export async function collectServices({ const { key: { serviceName, serviceEnvironment }, container_and_hosts: containerHosts, + last_seen: lastSeen, } = bucket; if (!serviceName) { @@ -111,7 +115,7 @@ export async function collectServices({ } const service: Asset = { - '@timestamp': new Date().toISOString(), + '@timestamp': lastSeen.value_as_string, 'asset.kind': 'service', 'asset.id': serviceName, 'asset.ean': `service:${serviceName}`, diff --git a/x-pack/plugins/asset_manager/server/lib/utils.ts b/x-pack/plugins/asset_manager/server/lib/utils.ts index fa9cf965771ef..d948ac28bda31 100644 --- a/x-pack/plugins/asset_manager/server/lib/utils.ts +++ b/x-pack/plugins/asset_manager/server/lib/utils.ts @@ -35,3 +35,7 @@ export function isStringOrNonEmptyArray( } return true; } + +export function extractFieldValue(maybeArray: T | T[] | undefined): T { + return toArray(maybeArray)[0]; +} diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/csv.test.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/csv.test.ts index a3941afc07e44..4442b1afe84d0 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/csv.test.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/csv.test.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { SerializableRecord } from '@kbn/utility-types'; import { functionWrapper } from '@kbn/presentation-util-plugin/test_helpers'; import { getFunctionErrors } from '../../../i18n'; import { csv } from './csv'; @@ -40,7 +39,7 @@ one,1 two,2 fourty two,42`, }, - {} as ExecutionContext + {} as ExecutionContext ) ).toEqual(expected); }); @@ -56,7 +55,7 @@ two\t2 fourty two\t42`, delimiter: '\t', }, - {} as ExecutionContext + {} as ExecutionContext ) ).toEqual(expected); @@ -70,7 +69,7 @@ two%SPLIT%2 fourty two%SPLIT%42`, delimiter: '%SPLIT%', }, - {} as ExecutionContext + {} as ExecutionContext ) ).toEqual(expected); }); @@ -83,7 +82,7 @@ fourty two%SPLIT%42`, data: `name,number\rone,1\rtwo,2\rfourty two,42`, newline: '\r', }, - {} as ExecutionContext + {} as ExecutionContext ) ).toEqual(expected); }); @@ -107,7 +106,7 @@ fourty two%SPLIT%42`, data: `foo," bar ", baz, " buz " 1,2,3,4`, }, - {} as ExecutionContext + {} as ExecutionContext ) ).toEqual(expectedResult); }); @@ -135,7 +134,7 @@ fourty two%SPLIT%42`, 1," best ",3, " ok" " good", bad, better , " worst " `, }, - {} as ExecutionContext + {} as ExecutionContext ) ).toEqual(expectedResult); }); @@ -150,7 +149,7 @@ one|1 two.2 fourty two,42`, }, - {} as ExecutionContext + {} as ExecutionContext ); }).toThrow(new RegExp(errors.invalidInputCSV().message)); }); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/dropdown_control.test.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/dropdown_control.test.ts index c3cf677c87ff6..8c500bd28c1ca 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/dropdown_control.test.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/dropdown_control.test.ts @@ -10,25 +10,16 @@ import { testTable, relationalTable } from './__fixtures__/test_tables'; import { dropdownControl } from './dropdownControl'; import { ExecutionContext } from '@kbn/expressions-plugin/common'; import { Adapters } from '@kbn/inspector-plugin/common'; -import { SerializableRecord } from '@kbn/utility-types'; describe('dropdownControl', () => { const fn = functionWrapper(dropdownControl); it('returns a render as dropdown_filter', () => { expect( - fn( - testTable, - { filterColumn: 'name', valueColumn: 'name' }, - {} as ExecutionContext - ) + fn(testTable, { filterColumn: 'name', valueColumn: 'name' }, {} as ExecutionContext) ).toHaveProperty('type', 'render'); expect( - fn( - testTable, - { filterColumn: 'name', valueColumn: 'name' }, - {} as ExecutionContext - ) + fn(testTable, { filterColumn: 'name', valueColumn: 'name' }, {} as ExecutionContext) ).toHaveProperty('as', 'dropdown_filter'); }); @@ -41,25 +32,16 @@ describe('dropdownControl', () => { [] ); expect( - fn( - testTable, - { valueColumn: 'name' }, - {} as ExecutionContext - )?.value?.choices + fn(testTable, { valueColumn: 'name' }, {} as ExecutionContext)?.value?.choices ).toEqual(uniqueNames); }); it('returns an empty array when provided an invalid column', () => { expect( - fn( - testTable, - { valueColumn: 'foo' }, - {} as ExecutionContext - )?.value?.choices + fn(testTable, { valueColumn: 'foo' }, {} as ExecutionContext)?.value?.choices ).toEqual([]); expect( - fn(testTable, { valueColumn: '' }, {} as ExecutionContext) - ?.value?.choices + fn(testTable, { valueColumn: '' }, {} as ExecutionContext)?.value?.choices ).toEqual([]); }); }); @@ -71,7 +53,7 @@ describe('dropdownControl', () => { fn( relationalTable, { valueColumn: 'id', labelColumn: 'name' }, - {} as ExecutionContext + {} as ExecutionContext )?.value?.choices ).toEqual(expectedChoices); }); @@ -81,28 +63,20 @@ describe('dropdownControl', () => { describe('filterColumn', () => { it('sets which column the filter is applied to', () => { expect( - fn( - testTable, - { filterColumn: 'name' }, - {} as ExecutionContext - )?.value + fn(testTable, { filterColumn: 'name' }, {} as ExecutionContext)?.value ).toHaveProperty('column', 'name'); expect( fn( testTable, { filterColumn: 'name', valueColumn: 'price' }, - {} as ExecutionContext + {} as ExecutionContext )?.value ).toHaveProperty('column', 'name'); }); it('defaults to valueColumn if not provided', () => { expect( - fn( - testTable, - { valueColumn: 'price' }, - {} as ExecutionContext - )?.value + fn(testTable, { valueColumn: 'price' }, {} as ExecutionContext)?.value ).toHaveProperty('column', 'price'); }); }); diff --git a/x-pack/plugins/canvas/public/components/popover/popover.tsx b/x-pack/plugins/canvas/public/components/popover/popover.tsx index 2ef1d4efc10d3..85bf9520b2758 100644 --- a/x-pack/plugins/canvas/public/components/popover/popover.tsx +++ b/x-pack/plugins/canvas/public/components/popover/popover.tsx @@ -17,7 +17,6 @@ interface Props { ownFocus?: boolean; tooltip?: string; panelClassName?: string; - anchorClassName?: string; anchorPosition?: string; panelPaddingSize?: 'none' | 's' | 'm' | 'l'; id?: string; diff --git a/x-pack/plugins/canvas/shareable_runtime/components/__snapshots__/app.test.tsx.snap b/x-pack/plugins/canvas/shareable_runtime/components/__snapshots__/app.test.tsx.snap index 5d2efe2903bf3..b661ee6e992e7 100644 --- a/x-pack/plugins/canvas/shareable_runtime/components/__snapshots__/app.test.tsx.snap +++ b/x-pack/plugins/canvas/shareable_runtime/components/__snapshots__/app.test.tsx.snap @@ -1,3 +1,3 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[` App renders properly 1`] = `"
markdown mock
markdown mock

Page level controls

My Canvas Workpad

There is a new region landmark with page level controls at the end of the document.

"`; +exports[` App renders properly 1`] = `"
markdown mock
markdown mock

Page level controls

My Canvas Workpad

There is a new region landmark with page level controls at the end of the document.

"`; diff --git a/x-pack/plugins/canvas/shareable_runtime/components/footer/settings/__snapshots__/settings.test.tsx.snap b/x-pack/plugins/canvas/shareable_runtime/components/footer/settings/__snapshots__/settings.test.tsx.snap index db2b294c9074a..da3ad71c3f034 100644 --- a/x-pack/plugins/canvas/shareable_runtime/components/footer/settings/__snapshots__/settings.test.tsx.snap +++ b/x-pack/plugins/canvas/shareable_runtime/components/footer/settings/__snapshots__/settings.test.tsx.snap @@ -9,7 +9,7 @@ exports[` can navigate Autoplay Settings 1`] = ` aria-describedby="generated-id" aria-live="off" aria-modal="true" - class="euiPanel euiPanel--plain euiPopover__panel emotion-euiPanel-grow-m-plain-euiPopover__panel-hasTransform" + class="euiPanel euiPanel--plain euiPopover__panel emotion-euiPanel-grow-m-plain-euiPopover__panel-light-hasTransform" data-popover-panel="true" role="dialog" style="top: -16px; left: -22px; will-change: transform, opacity; z-index: 2000;" @@ -102,7 +102,7 @@ exports[` can navigate Autoplay Settings 2`] = ` aria-describedby="generated-id" aria-live="off" aria-modal="true" - class="euiPanel euiPanel--plain euiPopover__panel emotion-euiPanel-grow-m-plain-euiPopover__panel-isOpen-hasTransform-top" + class="euiPanel euiPanel--plain euiPopover__panel emotion-euiPanel-grow-m-plain-euiPopover__panel-light-isOpen-hasTransform-top" data-popover-open="true" data-popover-panel="true" role="dialog" @@ -345,7 +345,7 @@ exports[` can navigate Toolbar Settings, closes when activated 1`] = aria-describedby="generated-id" aria-live="off" aria-modal="true" - class="euiPanel euiPanel--plain euiPopover__panel emotion-euiPanel-grow-m-plain-euiPopover__panel-hasTransform" + class="euiPanel euiPanel--plain euiPopover__panel emotion-euiPanel-grow-m-plain-euiPopover__panel-light-hasTransform" data-popover-panel="true" role="dialog" style="top: -16px; left: -22px; will-change: transform, opacity; z-index: 2000;" @@ -438,7 +438,7 @@ exports[` can navigate Toolbar Settings, closes when activated 2`] = aria-describedby="generated-id" aria-live="off" aria-modal="true" - class="euiPanel euiPanel--plain euiPopover__panel emotion-euiPanel-grow-m-plain-euiPopover__panel-isOpen-hasTransform-top" + class="euiPanel euiPanel--plain euiPopover__panel emotion-euiPanel-grow-m-plain-euiPopover__panel-light-isOpen-hasTransform-top" data-popover-open="true" data-popover-panel="true" role="dialog" @@ -615,7 +615,7 @@ exports[` can navigate Toolbar Settings, closes when activated 3`] = aria-describedby="generated-id" aria-live="off" aria-modal="true" - class="euiPanel euiPanel--plain euiPopover__panel emotion-euiPanel-grow-m-plain-euiPopover__panel-hasTransform" + class="euiPanel euiPanel--plain euiPopover__panel emotion-euiPanel-grow-m-plain-euiPopover__panel-light-hasTransform" data-popover-panel="true" role="dialog" style="top: -16px; left: -22px; z-index: 2000;" diff --git a/x-pack/plugins/cases/common/ui/types.ts b/x-pack/plugins/cases/common/ui/types.ts index b4019861c06fa..2ab04f058179d 100644 --- a/x-pack/plugins/cases/common/ui/types.ts +++ b/x-pack/plugins/cases/common/ui/types.ts @@ -156,7 +156,7 @@ export interface SystemFilterOptions { severity: CaseSeverity[]; status: CaseStatuses[]; tags: string[]; - assignees: Array | null; + assignees: Array; reporters: User[]; owner: string[]; category: string[]; diff --git a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx index 6a425b9a803a7..028849f48fdb4 100644 --- a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.test.tsx @@ -47,6 +47,7 @@ import { useLicense } from '../../common/use_license'; import * as api from '../../containers/api'; import { useGetCaseConfiguration } from '../../containers/configure/use_get_case_configuration'; import { useCaseConfigureResponse } from '../configure_cases/__mock__'; +import { useSuggestUserProfiles } from '../../containers/user_profiles/use_suggest_user_profiles'; jest.mock('../../containers/configure/use_get_case_configuration'); jest.mock('../../containers/use_get_cases'); @@ -63,6 +64,7 @@ jest.mock('../app/use_available_owners', () => ({ })); jest.mock('../../containers/use_update_case'); jest.mock('../../common/use_license'); +jest.mock('../../containers/user_profiles/use_suggest_user_profiles'); const useGetCaseConfigurationMock = useGetCaseConfiguration as jest.Mock; const useGetCasesMock = useGetCases as jest.Mock; @@ -74,6 +76,7 @@ const useGetConnectorsMock = useGetSupportedActionConnectors as jest.Mock; const useUpdateCaseMock = useUpdateCase as jest.Mock; const useLicenseMock = useLicense as jest.Mock; const useGetCategoriesMock = useGetCategories as jest.Mock; +const useSuggestUserProfilesMock = useSuggestUserProfiles as jest.Mock; const mockTriggersActionsUiService = triggersActionsUiMock.createStart(); @@ -164,6 +167,7 @@ describe('AllCasesListGeneric', () => { useBulkGetUserProfilesMock.mockReturnValue({ data: userProfilesMap }); useUpdateCaseMock.mockReturnValue({ mutate: updateCaseProperty }); useLicenseMock.mockReturnValue({ isAtLeastPlatinum: () => false }); + useSuggestUserProfilesMock.mockReturnValue({ data: userProfiles, isLoading: false }); mockKibana(); moment.tz.setDefault('UTC'); window.localStorage.clear(); @@ -512,7 +516,6 @@ describe('AllCasesListGeneric', () => { filterOptions: { ...DEFAULT_FILTER_OPTIONS, searchFields: ['title', 'description'], - owner: ['securitySolution'], category: ['twix'], }, queryParams: DEFAULT_QUERY_PARAMS, @@ -641,82 +644,6 @@ describe('AllCasesListGeneric', () => { }); describe('Solutions', () => { - it('should set the owner to all available solutions when deselecting all solutions', async () => { - const { getByTestId } = render( - - - - ); - - expect(useGetCasesMock).toHaveBeenCalledWith({ - filterOptions: { - search: '', - searchFields: ['title', 'description'], - severity: [], - reporters: [], - status: [], - tags: [], - assignees: [], - owner: ['securitySolution', 'observability'], - category: [], - customFields: {}, - }, - queryParams: DEFAULT_QUERY_PARAMS, - }); - - userEvent.click(getByTestId('options-filter-popover-button-owner')); - - await waitForEuiPopoverOpen(); - - userEvent.click( - getByTestId(`options-filter-popover-item-${SECURITY_SOLUTION_OWNER}`), - undefined, - { - skipPointerEventsCheck: true, - } - ); - - expect(useGetCasesMock).toBeCalledWith({ - filterOptions: { - search: '', - searchFields: ['title', 'description'], - severity: [], - reporters: [], - status: [], - tags: [], - assignees: [], - owner: ['securitySolution'], - category: [], - customFields: {}, - }, - queryParams: DEFAULT_QUERY_PARAMS, - }); - - userEvent.click( - getByTestId(`options-filter-popover-item-${SECURITY_SOLUTION_OWNER}`), - undefined, - { - skipPointerEventsCheck: true, - } - ); - - expect(useGetCasesMock).toHaveBeenLastCalledWith({ - filterOptions: { - search: '', - searchFields: ['title', 'description'], - severity: [], - reporters: [], - status: [], - tags: [], - assignees: [], - owner: ['securitySolution', 'observability'], - category: [], - customFields: {}, - }, - queryParams: DEFAULT_QUERY_PARAMS, - }); - }); - it('should hide the solutions filter if the owner is provided', async () => { const { queryByTestId } = render( @@ -726,30 +653,6 @@ describe('AllCasesListGeneric', () => { expect(queryByTestId('options-filter-popover-button-owner')).toBeFalsy(); }); - - it('should call useGetCases with the correct owner on initial render', async () => { - render( - - - - ); - - expect(useGetCasesMock).toHaveBeenCalledWith({ - filterOptions: { - search: '', - searchFields: ['title', 'description'], - severity: [], - reporters: [], - status: [], - tags: [], - assignees: [], - owner: ['securitySolution'], - category: [], - customFields: {}, - }, - queryParams: DEFAULT_QUERY_PARAMS, - }); - }); }); describe('Actions', () => { @@ -1078,6 +981,42 @@ describe('AllCasesListGeneric', () => { ).toBeGreaterThan(0); }); }); + + it('should reset the assignees when deactivating the filter', async () => { + useLicenseMock.mockReturnValue({ isAtLeastPlatinum: () => true }); + + appMockRenderer.render(); + + // Opens assignees filter and checks an option + const assigneesButton = screen.getByTestId('options-filter-popover-button-assignees'); + userEvent.click(assigneesButton); + userEvent.click(screen.getByText('Damaged Raccoon')); + expect(within(assigneesButton).getByLabelText('1 active filters')).toBeInTheDocument(); + + // Deactivates assignees filter + userEvent.click(screen.getByRole('button', { name: 'More' })); + await waitForEuiPopoverOpen(); + userEvent.click(screen.getByRole('option', { name: 'Assignees' })); + + expect(useGetCasesMock).toHaveBeenLastCalledWith({ + filterOptions: { + ...DEFAULT_FILTER_OPTIONS, + assignees: [], + }, + queryParams: DEFAULT_QUERY_PARAMS, + }); + + // Reopens assignees filter + userEvent.click(screen.getByRole('option', { name: 'Assignees' })); + // Opens the assignees popup + userEvent.click(assigneesButton); + expect(screen.getByLabelText('click to filter assignees')).toBeInTheDocument(); + expect( + within(screen.getByTestId('options-filter-popover-button-assignees')).queryByLabelText( + '1 active filters' + ) + ).not.toBeInTheDocument(); + }); }); }); diff --git a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx index a997e25a59f2d..449f7a190f6f0 100644 --- a/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/all_cases_list.tsx @@ -63,10 +63,10 @@ export const AllCasesList = React.memo( const isLoading = useIsLoadingCases(); const hasOwner = !!owner.length; + const firstAvailableStatus = head(difference(caseStatuses, hiddenStatuses)); const initialFilterOptions = { ...(!isEmpty(hiddenStatuses) && firstAvailableStatus && { status: [firstAvailableStatus] }), - owner: hasOwner ? owner : availableSolutions, }; const { queryParams, setQueryParams, filterOptions, setFilterOptions } = useAllCasesState( @@ -210,7 +210,6 @@ export const AllCasesList = React.memo( availableSolutions={hasOwner ? [] : availableSolutions} hiddenStatuses={hiddenStatuses} onCreateCasePressed={onCreateCasePressed} - initialFilterOptions={initialFilterOptions} isSelectorView={isSelectorView} isLoading={isLoadingCurrentUserProfile} currentUserProfile={currentUserProfile} diff --git a/x-pack/plugins/cases/public/components/all_cases/assignees_filter.test.tsx b/x-pack/plugins/cases/public/components/all_cases/assignees_filter.test.tsx index 258f30b64eca8..90e204fe8ccfb 100644 --- a/x-pack/plugins/cases/public/components/all_cases/assignees_filter.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/assignees_filter.test.tsx @@ -51,19 +51,13 @@ describe('AssigneesFilterPopover', () => { userEvent.click(screen.getByText('WD')); expect(onSelectionChange.mock.calls[0][0]).toMatchInlineSnapshot(` - Array [ - Object { - "data": Object {}, - "enabled": true, - "uid": "u_9xDEQqUqoYCnFnPPLq5mIRHKL8gBTo_NiKgOnd5gGk0_0", - "user": Object { - "email": "wet_dingo@elastic.co", - "full_name": "Wet Dingo", - "username": "wet_dingo", - }, - }, - ] - `); + Object { + "filterId": "assignees", + "selectedOptionKeys": Array [ + "u_9xDEQqUqoYCnFnPPLq5mIRHKL8gBTo_NiKgOnd5gGk0_0", + ], + } + `); }); it('calls onSelectionChange with a single user when different users are selected', async () => { @@ -83,32 +77,20 @@ describe('AssigneesFilterPopover', () => { userEvent.click(screen.getByText('damaged_raccoon@elastic.co')); expect(onSelectionChange.mock.calls[0][0]).toMatchInlineSnapshot(` - Array [ - Object { - "data": Object {}, - "enabled": true, - "uid": "u_9xDEQqUqoYCnFnPPLq5mIRHKL8gBTo_NiKgOnd5gGk0_0", - "user": Object { - "email": "wet_dingo@elastic.co", - "full_name": "Wet Dingo", - "username": "wet_dingo", - }, - }, - ] + Object { + "filterId": "assignees", + "selectedOptionKeys": Array [ + "u_9xDEQqUqoYCnFnPPLq5mIRHKL8gBTo_NiKgOnd5gGk0_0", + ], + } `); expect(onSelectionChange.mock.calls[1][0]).toMatchInlineSnapshot(` - Array [ - Object { - "data": Object {}, - "enabled": true, - "uid": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0", - "user": Object { - "email": "damaged_raccoon@elastic.co", - "full_name": "Damaged Raccoon", - "username": "damaged_raccoon", - }, - }, - ] + Object { + "filterId": "assignees", + "selectedOptionKeys": Array [ + "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0", + ], + } `); }); @@ -128,7 +110,7 @@ describe('AssigneesFilterPopover', () => { it('shows the 1 assigned total when the users are passed in', async () => { const props = { ...defaultProps, - selectedAssignees: [userProfiles[0]], + selectedAssignees: [userProfiles[0].uid], }; appMockRender.render(); @@ -145,7 +127,7 @@ describe('AssigneesFilterPopover', () => { it('shows the total when the multiple users are selected', async () => { const props = { ...defaultProps, - selectedAssignees: [userProfiles[0], userProfiles[1]], + selectedAssignees: [userProfiles[0].uid, userProfiles[1].uid], }; appMockRender.render(); @@ -239,9 +221,12 @@ describe('AssigneesFilterPopover', () => { userEvent.click(screen.getByText('No assignees')); expect(onSelectionChange.mock.calls[0][0]).toMatchInlineSnapshot(` - Array [ - null, - ] + Object { + "filterId": "assignees", + "selectedOptionKeys": Array [ + null, + ], + } `); }); @@ -261,39 +246,30 @@ describe('AssigneesFilterPopover', () => { userEvent.click(screen.getByText('damaged_raccoon@elastic.co')); expect(onSelectionChange.mock.calls[0][0]).toMatchInlineSnapshot(` - Array [ - null, - ] + Object { + "filterId": "assignees", + "selectedOptionKeys": Array [ + null, + ], + } `); expect(onSelectionChange.mock.calls[1][0]).toMatchInlineSnapshot(` - Array [ - Object { - "data": Object {}, - "enabled": true, - "uid": "u_9xDEQqUqoYCnFnPPLq5mIRHKL8gBTo_NiKgOnd5gGk0_0", - "user": Object { - "email": "wet_dingo@elastic.co", - "full_name": "Wet Dingo", - "username": "wet_dingo", - }, - }, - ] + Object { + "filterId": "assignees", + "selectedOptionKeys": Array [ + "u_9xDEQqUqoYCnFnPPLq5mIRHKL8gBTo_NiKgOnd5gGk0_0", + ], + } `); expect(onSelectionChange.mock.calls[2][0]).toMatchInlineSnapshot(` - Array [ - Object { - "data": Object {}, - "enabled": true, - "uid": "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0", - "user": Object { - "email": "damaged_raccoon@elastic.co", - "full_name": "Damaged Raccoon", - "username": "damaged_raccoon", - }, - }, - ] + Object { + "filterId": "assignees", + "selectedOptionKeys": Array [ + "u_J41Oh6L9ki-Vo2tOogS8WRTENzhHurGtRc87NgEAlkc_0", + ], + } `); }); @@ -313,7 +289,7 @@ describe('AssigneesFilterPopover', () => { }); it('shows warning message when reaching maximum limit to filter', async () => { - const maxAssignees = Array(MAX_ASSIGNEES_FILTER_LENGTH).fill(userProfiles[0]); + const maxAssignees = Array(MAX_ASSIGNEES_FILTER_LENGTH).fill(userProfiles[0].uid); const props = { ...defaultProps, selectedAssignees: maxAssignees, diff --git a/x-pack/plugins/cases/public/components/all_cases/assignees_filter.tsx b/x-pack/plugins/cases/public/components/all_cases/assignees_filter.tsx index 8e88eec447e6c..3a2e03e827665 100644 --- a/x-pack/plugins/cases/public/components/all_cases/assignees_filter.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/assignees_filter.tsx @@ -5,7 +5,8 @@ * 2.0. */ -import { EuiFilterButton } from '@elastic/eui'; +import { EuiFilterButton, EuiFilterGroup } from '@elastic/eui'; +import type { UserProfileWithAvatar } from '@kbn/user-profile-components'; import { UserProfilesPopover } from '@kbn/user-profile-components'; import { isEmpty } from 'lodash'; import React, { useCallback, useMemo, useState } from 'react'; @@ -24,14 +25,17 @@ import { MAX_ASSIGNEES_FILTER_LENGTH } from '../../../common/constants'; export const NO_ASSIGNEES_VALUE = null; export interface AssigneesFilterPopoverProps { - selectedAssignees: AssigneesFilteringSelection[]; + selectedAssignees: Array; currentUserProfile: CurrentUserProfile; isLoading: boolean; - onSelectionChange: (users: AssigneesFilteringSelection[]) => void; + onSelectionChange: (params: { + filterId: string; + selectedOptionKeys: Array; + }) => void; } const AssigneesFilterPopoverComponent: React.FC = ({ - selectedAssignees, + selectedAssignees: selectedAssigneesUids, currentUserProfile, isLoading, onSelectionChange, @@ -48,8 +52,10 @@ const AssigneesFilterPopoverComponent: React.FC = ( const onChange = useCallback( (users: AssigneesFilteringSelection[]) => { const sortedUsers = orderAssigneesIncludingNone(currentUserProfile, users); - - onSelectionChange(sortedUsers); + onSelectionChange({ + filterId: 'assignees', + selectedOptionKeys: sortedUsers.map((user) => user?.uid ?? null), + }); }, [currentUserProfile, onSelectionChange] ); @@ -88,47 +94,59 @@ const AssigneesFilterPopoverComponent: React.FC = ( return sortedUsers; }, [currentUserProfile, userProfiles, searchTerm]); + const selectedAssignees = selectedAssigneesUids + .map((uuid) => { + // this is the "no assignees" option + if (uuid === null) return null; + const userProfile = searchResultProfiles.find((user) => user?.uid === uuid); + return userProfile; + }) + .filter( + (userProfile): userProfile is UserProfileWithAvatar | null => userProfile !== undefined + ); // Filter out profiles that no longer exists const isLoadingData = isLoading || isLoadingSuggest; return ( - 0} - numActiveFilters={selectedAssignees.length} - aria-label={i18n.FILTER_ASSIGNEES_ARIA_LABEL} - > - {i18n.ASSIGNEES} - - } - selectableProps={{ - onChange, - onSearchChange, - selectedStatusMessage, - options: searchResultProfiles, - selectedOptions: selectedAssignees, - isLoading: isLoadingData || isUserTyping, - height: 'full', - searchPlaceholder: i18n.SEARCH_USERS, - clearButtonLabel: i18n.CLEAR_FILTERS, - emptyMessage: , - noMatchesMessage: !isUserTyping && !isLoadingData ? : , - limit: MAX_ASSIGNEES_FILTER_LENGTH, - limitReachedMessage, - singleSelection: false, - nullOptionLabel: i18n.NO_ASSIGNEES, - }} - /> + + 0} + numActiveFilters={selectedAssignees.length} + aria-label={i18n.FILTER_ASSIGNEES_ARIA_LABEL} + > + {i18n.ASSIGNEES} + + } + selectableProps={{ + onChange, + onSearchChange, + selectedStatusMessage, + options: searchResultProfiles, + selectedOptions: selectedAssignees, + isLoading: isLoadingData || isUserTyping, + height: 'full', + searchPlaceholder: i18n.SEARCH_USERS, + clearButtonLabel: i18n.CLEAR_FILTERS, + emptyMessage: , + noMatchesMessage: !isUserTyping && !isLoadingData ? : , + limit: MAX_ASSIGNEES_FILTER_LENGTH, + limitReachedMessage, + singleSelection: false, + nullOptionLabel: i18n.NO_ASSIGNEES, + }} + /> + ); }; AssigneesFilterPopoverComponent.displayName = 'AssigneesFilterPopover'; diff --git a/x-pack/plugins/cases/public/components/all_cases/multi_select_filter.tsx b/x-pack/plugins/cases/public/components/all_cases/multi_select_filter.tsx index cbfdb30d122aa..b56c926bab965 100644 --- a/x-pack/plugins/cases/public/components/all_cases/multi_select_filter.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/multi_select_filter.tsx @@ -18,6 +18,8 @@ import { EuiTextColor, EuiSpacer, useEuiTheme, + EuiFilterGroup, + EuiText, } from '@elastic/eui'; import { isEqual } from 'lodash/fp'; import * as i18n from './translations'; @@ -63,16 +65,17 @@ const getEuiSelectableCheckedOptions = ( ) => options.filter((option) => option.checked === 'on') as Array>; interface UseFilterParams { - buttonLabel?: string; buttonIconType?: string; + buttonLabel?: string; hideActiveOptionsNumber?: boolean; id: string; limit?: number; limitReachedMessage?: string; onChange: (params: { filterId: string; selectedOptionKeys: string[] }) => void; options: Array>; - selectedOptionKeys?: string[]; renderOption?: (option: FilterOption) => React.ReactNode; + selectedOptionKeys?: string[]; + transparentBackground?: boolean; } export const MultiSelectFilter = ({ buttonLabel, @@ -85,6 +88,7 @@ export const MultiSelectFilter = ({ options: rawOptions, selectedOptionKeys = [], renderOption, + transparentBackground, }: UseFilterParams) => { const { euiTheme } = useEuiTheme(); const [isPopoverOpen, setIsPopoverOpen] = useState(false); @@ -118,74 +122,85 @@ export const MultiSelectFilter = ({ }; return ( - 0 : undefined} - numActiveFilters={showActiveOptionsNumber ? selectedOptionKeys.length : undefined} - aria-label={buttonLabel} - > - {buttonLabel} - - } - isOpen={isPopoverOpen} - closePopover={() => setIsPopoverOpen(false)} - panelPaddingSize="none" - repositionOnScroll + - {isInvalid && ( - <> - - - - - )} - > - options={options} - searchable - searchProps={{ - placeholder: buttonLabel, - compressed: false, - 'data-test-subj': `${id}-search-input`, - }} - emptyMessage={i18n.EMPTY_FILTER_MESSAGE} - onChange={_onChange} - singleSelection={false} - renderOption={renderOption} - > - {(list, search) => ( -
0 : undefined} + numActiveFilters={showActiveOptionsNumber ? selectedOptionKeys.length : undefined} + aria-label={buttonLabel} > - {search} + + {buttonLabel} + + + } + isOpen={isPopoverOpen} + closePopover={() => setIsPopoverOpen(false)} + panelPaddingSize="none" + repositionOnScroll + > + {isInvalid && ( + <> + + + + + )} + > + options={options} + searchable + searchProps={{ + placeholder: buttonLabel, + compressed: false, + 'data-test-subj': `${id}-search-input`, + }} + emptyMessage={i18n.EMPTY_FILTER_MESSAGE} + onChange={_onChange} + singleSelection={false} + renderOption={renderOption} + > + {(list, search) => (
- {i18n.OPTIONS(options.length)} + {search} +
+ {i18n.OPTIONS(options.length)} +
+ + {list}
- - {list} -
- )} - -
+ )} + + + ); }; diff --git a/x-pack/plugins/cases/public/components/all_cases/solution_filter.test.tsx b/x-pack/plugins/cases/public/components/all_cases/solution_filter.test.tsx index 69c757e354b86..1118a633c479f 100644 --- a/x-pack/plugins/cases/public/components/all_cases/solution_filter.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/solution_filter.test.tsx @@ -100,7 +100,7 @@ describe('SolutionFilter ', () => { expect(onChange).toHaveBeenCalledWith({ filterId: 'owner', - selectedOptionKeys: [solutions[0]], + selectedOptionKeys: [], }); }); }); @@ -168,7 +168,7 @@ describe('SolutionFilter ', () => { expect(onChange).toHaveBeenCalledWith({ filterId: 'owner', - selectedOptionKeys: [solutions[0], solutions[1]], + selectedOptionKeys: [], }); }); }); diff --git a/x-pack/plugins/cases/public/components/all_cases/solution_filter.tsx b/x-pack/plugins/cases/public/components/all_cases/solution_filter.tsx index 6f4eabddc0f8f..f2002e4c7899b 100644 --- a/x-pack/plugins/cases/public/components/all_cases/solution_filter.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/solution_filter.tsx @@ -43,37 +43,6 @@ export const SolutionFilterComponent = ({ const options = mapToMultiSelectOption(hasOwner ? owner : availableSolutions); const solutions = availableSolutions.map((solution) => mapToReadableSolutionName(solution)); - /** - * If the user selects and deselects all solutions then the owner is set to an empty array. - * This results in fetching all cases the user has access to including - * the ones with read access. We want to show only the cases the user has full access to. - * For that reason we fallback to availableSolutions if the owner is empty. - * - * If the consumer of cases has passed an owner we fallback to the provided owner - */ - const _onChange = ({ - filterId, - selectedOptionKeys: newOptions, - }: { - filterId: string; - selectedOptionKeys: string[]; - }) => { - if (hasOwner) { - onChange({ - filterId, - selectedOptionKeys: newOptions.length === 0 ? owner : newOptions, - }); - } else { - onChange({ - filterId, - selectedOptionKeys: newOptions.length === 0 ? availableSolutions : newOptions, - }); - } - }; - - const selectedOptionsInFilter = - selectedOptionKeys.length === availableSolutions.length ? [] : selectedOptionKeys; - const renderOption = (option: EuiSelectableOption) => { const solution = solutions.find((solutionData) => solutionData.id === option.label) as Solution; return ( @@ -90,10 +59,10 @@ export const SolutionFilterComponent = ({ ); }; diff --git a/x-pack/plugins/cases/public/components/all_cases/table_filter_config/more_filters_selectable.tsx b/x-pack/plugins/cases/public/components/all_cases/table_filter_config/more_filters_selectable.tsx index e89ff2af07242..d191b339a05aa 100644 --- a/x-pack/plugins/cases/public/components/all_cases/table_filter_config/more_filters_selectable.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/table_filter_config/more_filters_selectable.tsx @@ -27,6 +27,7 @@ export const MoreFiltersSelectable = ({ onChange={onChange} options={options} selectedOptionKeys={activeFilters} + transparentBackground={true} /> ); }; diff --git a/x-pack/plugins/cases/public/components/all_cases/table_filter_config/use_custom_fields_filter_config.tsx b/x-pack/plugins/cases/public/components/all_cases/table_filter_config/use_custom_fields_filter_config.tsx index 3193b1a1cc70f..6901bca807319 100644 --- a/x-pack/plugins/cases/public/components/all_cases/table_filter_config/use_custom_fields_filter_config.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/table_filter_config/use_custom_fields_filter_config.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useState, useEffect } from 'react'; +import React from 'react'; import type { CustomFieldTypes } from '../../../../common/types/domain'; import { builderMap as customFieldsBuilder } from '../../custom_fields/builder'; import { useGetCaseConfiguration } from '../../../containers/configure/use_get_case_configuration'; @@ -84,36 +84,33 @@ export const useCustomFieldsFilterConfig = ({ isSelectorView: boolean; onFilterOptionsChange: FilterChangeHandler; }) => { - const [filterConfig, setFilterConfig] = useState([]); - const { data: { customFields }, } = useGetCaseConfiguration(); - useEffect(() => { - if (isSelectorView) return; + const customFieldsFilterConfig: FilterConfig[] = []; - const customFieldsFilterConfig: FilterConfig[] = []; - for (const { key: fieldKey, type, label: buttonLabel } of customFields ?? []) { - if (customFieldsBuilder[type]) { - const { filterOptions: customFieldOptions } = customFieldsBuilder[type](); + if (isSelectorView) { + return { customFieldsFilterConfig: [] }; + } - if (customFieldOptions) { - customFieldsFilterConfig.push( - customFieldFilterOptionFactory({ - buttonLabel, - customFieldOptions, - fieldKey, - onFilterOptionsChange, - type, - }) - ); - } + for (const { key: fieldKey, type, label: buttonLabel } of customFields ?? []) { + if (customFieldsBuilder[type]) { + const { filterOptions: customFieldOptions } = customFieldsBuilder[type](); + + if (customFieldOptions) { + customFieldsFilterConfig.push( + customFieldFilterOptionFactory({ + buttonLabel, + customFieldOptions, + fieldKey, + onFilterOptionsChange, + type, + }) + ); } } + } - setFilterConfig(customFieldsFilterConfig); - }, [customFields, isSelectorView, onFilterOptionsChange]); - - return { customFieldsFilterConfig: filterConfig }; + return { customFieldsFilterConfig }; }; diff --git a/x-pack/plugins/cases/public/components/all_cases/table_filter_config/use_filter_config.test.tsx b/x-pack/plugins/cases/public/components/all_cases/table_filter_config/use_filter_config.test.tsx index 483080ffd5ea6..62dd688cae29a 100644 --- a/x-pack/plugins/cases/public/components/all_cases/table_filter_config/use_filter_config.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/table_filter_config/use_filter_config.test.tsx @@ -27,7 +27,7 @@ const emptyFilterOptions: FilterOptions = { severity: [], status: [], tags: [], - assignees: null, + assignees: [], reporters: [], owner: [], category: [], diff --git a/x-pack/plugins/cases/public/components/all_cases/table_filter_config/use_filter_config.tsx b/x-pack/plugins/cases/public/components/all_cases/table_filter_config/use_filter_config.tsx index 6a050a5b86f3b..8d721cd13daa7 100644 --- a/x-pack/plugins/cases/public/components/all_cases/table_filter_config/use_filter_config.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/table_filter_config/use_filter_config.tsx @@ -6,7 +6,7 @@ */ import type { SetStateAction } from 'react'; -import { useEffect, useState } from 'react'; +import usePrevious from 'react-use/lib/usePrevious'; import useLocalStorage from 'react-use/lib/useLocalStorage'; import { merge, isEqual, isEmpty } from 'lodash'; import type { FilterOptions } from '../../../../common/ui'; @@ -70,6 +70,29 @@ const useActiveByFilterKeyState = ({ filterOptions }: { filterOptions: FilterOpt ]; }; +const deactivateNonExistingFilters = ({ + prevFilterConfigs, + currentFilterConfigs, + onFilterOptionsChange, +}: { + prevFilterConfigs: Map; + currentFilterConfigs: Map; + onFilterOptionsChange: (params: Partial) => void; +}) => { + const emptyOptions: Array> = []; + + [...(prevFilterConfigs?.entries() ?? [])].forEach(([filterKey, filter]) => { + if (!currentFilterConfigs.has(filterKey)) { + emptyOptions.push(filter.getEmptyOptions()); + } + }); + + if (emptyOptions.length > 0) { + const mergedEmptyOptions = merge({}, ...emptyOptions); + onFilterOptionsChange(mergedEmptyOptions); + } +}; + export const useFilterConfig = ({ isSelectorView, onFilterOptionsChange, @@ -90,46 +113,19 @@ export const useFilterConfig = ({ isSelectorView, onFilterOptionsChange, }); - const [filterConfigs, setFilterConfigs] = useState>( - () => new Map([...systemFilterConfig].map((filter) => [filter.key, filter])) - ); - /** - * This effect is needed in case a filter (mostly custom field) is removed from the settings - * but the user was filtering by it. - */ - useEffect(() => { - const newFilterConfig = mergeSystemAndCustomFieldConfigs({ - systemFilterConfig, - customFieldsFilterConfig, - }); + const filterConfigs = mergeSystemAndCustomFieldConfigs({ + systemFilterConfig, + customFieldsFilterConfig, + }); - const emptyOptions: Array> = []; - filterConfigs.forEach((filter) => { - if (!newFilterConfig.has(filter.key)) { - emptyOptions.push(filter.getEmptyOptions()); - } - }); + const prevFilterConfigs = usePrevious(filterConfigs) ?? new Map(); - if (emptyOptions.length > 0) { - const mergedEmptyOptions = merge({}, ...emptyOptions); - onFilterOptionsChange(mergedEmptyOptions); - } - }, [filterConfigs, systemFilterConfig, customFieldsFilterConfig, onFilterOptionsChange]); - - /** - * As custom fields are loaded by fetching an API and they might also be removed - * while using the app, we merge the system and custom fields configs on every time - * they change. - */ - useEffect(() => { - setFilterConfigs( - mergeSystemAndCustomFieldConfigs({ - systemFilterConfig, - customFieldsFilterConfig, - }) - ); - }, [systemFilterConfig, customFieldsFilterConfig]); + deactivateNonExistingFilters({ + prevFilterConfigs, + currentFilterConfigs: filterConfigs, + onFilterOptionsChange, + }); const onChange = ({ selectedOptionKeys }: { filterId: string; selectedOptionKeys: string[] }) => { const newActiveByFilterKey = [...(activeByFilterKey || [])]; diff --git a/x-pack/plugins/cases/public/components/all_cases/table_filter_config/use_system_filter_config.tsx b/x-pack/plugins/cases/public/components/all_cases/table_filter_config/use_system_filter_config.tsx index 4e921e222d4df..ba2ca2d5f363f 100644 --- a/x-pack/plugins/cases/public/components/all_cases/table_filter_config/use_system_filter_config.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/table_filter_config/use_system_filter_config.tsx @@ -5,9 +5,8 @@ * 2.0. */ -import React, { useState, useEffect } from 'react'; +import React from 'react'; -import type { FilterOptions } from '../../../../common/ui'; import type { CaseStatuses } from '../../../../common/types/domain'; import { MAX_TAGS_FILTER_LENGTH, MAX_CATEGORY_FILTER_LENGTH } from '../../../../common/constants'; import { MultiSelectFilter, mapToMultiSelectOption } from '../multi_select_filter'; @@ -17,7 +16,6 @@ import * as i18n from '../translations'; import { SeverityFilter } from '../severity_filter'; import { AssigneesFilterPopover } from '../assignees_filter'; import type { CurrentUserProfile } from '../../types'; -import type { AssigneesFilteringSelection } from '../../user_profiles/types'; import type { FilterChangeHandler, FilterConfig, FilterConfigRenderParams } from './types'; interface UseFilterConfigProps { @@ -28,13 +26,10 @@ interface UseFilterConfigProps { countInProgressCases: number | null; countOpenCases: number | null; currentUserProfile: CurrentUserProfile; - handleSelectedAssignees: (newAssignees: AssigneesFilteringSelection[]) => void; hiddenStatuses?: CaseStatuses[]; - initialFilterOptions: Partial; isLoading: boolean; isSelectorView?: boolean; onFilterOptionsChange: FilterChangeHandler; - selectedAssignees: AssigneesFilteringSelection[]; tags: string[]; } @@ -46,13 +41,10 @@ export const getSystemFilterConfig = ({ countInProgressCases, countOpenCases, currentUserProfile, - handleSelectedAssignees, hiddenStatuses, - initialFilterOptions, isLoading, isSelectorView, onFilterOptionsChange, - selectedAssignees, tags, }: UseFilterConfigProps): FilterConfig[] => { const onSystemFilterChange = ({ @@ -60,7 +52,7 @@ export const getSystemFilterConfig = ({ selectedOptionKeys, }: { filterId: string; - selectedOptionKeys: string[]; + selectedOptionKeys: Array; }) => { onFilterOptionsChange({ [filterId]: selectedOptionKeys, @@ -74,7 +66,7 @@ export const getSystemFilterConfig = ({ isAvailable: true, getEmptyOptions: () => { return { - severity: initialFilterOptions.severity || [], + severity: [], }; }, render: ({ filterOptions }: FilterConfigRenderParams) => ( @@ -91,7 +83,7 @@ export const getSystemFilterConfig = ({ isAvailable: true, getEmptyOptions: () => { return { - status: initialFilterOptions.status || [], + status: [], }; }, render: ({ filterOptions }: FilterConfigRenderParams) => ( @@ -112,16 +104,16 @@ export const getSystemFilterConfig = ({ isAvailable: caseAssignmentAuthorized && !isSelectorView, getEmptyOptions: () => { return { - assignees: initialFilterOptions.assignees || [], + assignees: [], }; }, render: ({ filterOptions }: FilterConfigRenderParams) => { return ( ); }, @@ -133,7 +125,7 @@ export const getSystemFilterConfig = ({ isAvailable: true, getEmptyOptions: () => { return { - tags: initialFilterOptions.tags || [], + tags: [], }; }, render: ({ filterOptions }: FilterConfigRenderParams) => ( @@ -155,7 +147,7 @@ export const getSystemFilterConfig = ({ isAvailable: true, getEmptyOptions: () => { return { - category: initialFilterOptions.category || [], + category: [], }; }, render: ({ filterOptions }: FilterConfigRenderParams) => ( @@ -177,7 +169,7 @@ export const getSystemFilterConfig = ({ isAvailable: availableSolutions.length > 1, getEmptyOptions: () => { return { - owner: initialFilterOptions.owner || [], + owner: [], }; }, render: ({ filterOptions }: FilterConfigRenderParams) => ( @@ -199,56 +191,13 @@ export const useSystemFilterConfig = ({ countInProgressCases, countOpenCases, currentUserProfile, - handleSelectedAssignees, hiddenStatuses, - initialFilterOptions, isLoading, isSelectorView, onFilterOptionsChange, - selectedAssignees, tags, }: UseFilterConfigProps) => { - const [filterConfig, setFilterConfig] = useState(() => - getSystemFilterConfig({ - availableSolutions, - caseAssignmentAuthorized, - categories, - countClosedCases, - countInProgressCases, - countOpenCases, - currentUserProfile, - handleSelectedAssignees, - hiddenStatuses, - initialFilterOptions, - isLoading, - isSelectorView, - onFilterOptionsChange, - selectedAssignees, - tags, - }) - ); - - useEffect(() => { - setFilterConfig( - getSystemFilterConfig({ - availableSolutions, - caseAssignmentAuthorized, - categories, - countClosedCases, - countInProgressCases, - countOpenCases, - currentUserProfile, - handleSelectedAssignees, - hiddenStatuses, - initialFilterOptions, - isLoading, - isSelectorView, - onFilterOptionsChange, - selectedAssignees, - tags, - }) - ); - }, [ + const filterConfig = getSystemFilterConfig({ availableSolutions, caseAssignmentAuthorized, categories, @@ -256,15 +205,12 @@ export const useSystemFilterConfig = ({ countInProgressCases, countOpenCases, currentUserProfile, - handleSelectedAssignees, hiddenStatuses, - initialFilterOptions, isLoading, isSelectorView, onFilterOptionsChange, - selectedAssignees, tags, - ]); + }); return { systemFilterConfig: filterConfig, diff --git a/x-pack/plugins/cases/public/components/all_cases/table_filters.test.tsx b/x-pack/plugins/cases/public/components/all_cases/table_filters.test.tsx index 17854c7c33547..0abed867a29b3 100644 --- a/x-pack/plugins/cases/public/components/all_cases/table_filters.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/table_filters.test.tsx @@ -165,6 +165,18 @@ describe('CasesTableFilters ', () => { "assignees": Array [ "u_A_tM4n0wPkdiQ9smmd8o0Hr_h61XQfu8aRPh9GMoRoc_0", ], + "category": Array [], + "customFields": Object {}, + "owner": Array [], + "reporters": Array [], + "search": "", + "searchFields": Array [ + "title", + "description", + ], + "severity": Array [], + "status": Array [], + "tags": Array [], } `); }); @@ -206,12 +218,11 @@ describe('CasesTableFilters ', () => { it('should remove assignee from selected assignees when assignee no longer exists', async () => { const overrideProps = { ...props, - initial: { + filterOptions: { ...DEFAULT_FILTER_OPTIONS, assignees: [ // invalid profile uid '123', - 'u_A_tM4n0wPkdiQ9smmd8o0Hr_h61XQfu8aRPh9GMoRoc_0', ], }, }; @@ -233,12 +244,27 @@ describe('CasesTableFilters ', () => { "assignees": Array [ "u_A_tM4n0wPkdiQ9smmd8o0Hr_h61XQfu8aRPh9GMoRoc_0", ], + "category": Array [], + "customFields": Object {}, + "owner": Array [], + "reporters": Array [], + "search": "", + "searchFields": Array [ + "title", + "description", + ], + "severity": Array [], + "status": Array [], + "tags": Array [], } `); }); describe('Solution filter', () => { it('shows Solution filter when provided more than 1 availableSolutions', () => { + appMockRender = createAppMockRenderer({ + owner: [SECURITY_SOLUTION_OWNER, OBSERVABILITY_OWNER], + }); appMockRender.render( { }); it('does not show Solution filter when provided less than 1 availableSolutions', () => { - appMockRender.render( - - ); + appMockRender = createAppMockRenderer({ + owner: [], + }); + appMockRender.render(); expect(screen.queryByTestId('options-filter-popover-button-owner')).not.toBeInTheDocument(); }); it('does not select a solution on initial render', () => { + appMockRender = createAppMockRenderer({ + owner: [SECURITY_SOLUTION_OWNER, OBSERVABILITY_OWNER], + }); appMockRender.render( { ); }); - it('should reset the filter setting all available solutions when deactivated', async () => { + it('should reset the filter when deactivated', async () => { + appMockRender = createAppMockRenderer({ + owner: [SECURITY_SOLUTION_OWNER, OBSERVABILITY_OWNER], + }); + + const overrideProps = { + ...props, + filterOptions: { + ...props.filterOptions, + owner: [SECURITY_SOLUTION_OWNER, OBSERVABILITY_OWNER], + }, + }; + appMockRender.render( ); @@ -283,8 +324,39 @@ describe('CasesTableFilters ', () => { expect(onFilterChanged).toHaveBeenCalledWith({ ...DEFAULT_FILTER_OPTIONS, + owner: [], + }); + }); + + it('should check all options when all options are selected', async () => { + appMockRender = createAppMockRenderer({ owner: [SECURITY_SOLUTION_OWNER, OBSERVABILITY_OWNER], }); + + const overrideProps = { + ...props, + filterOptions: { + ...props.filterOptions, + owner: [SECURITY_SOLUTION_OWNER, OBSERVABILITY_OWNER], + }, + }; + + appMockRender.render( + + ); + + userEvent.click(screen.getByRole('button', { name: 'Solution' })); + await waitForEuiPopoverOpen(); + + const allOptions = screen.getAllByRole('option'); + expect(allOptions).toHaveLength(2); + expect(allOptions[0]).toHaveAttribute('aria-checked', 'true'); + expect(allOptions[0]).toHaveTextContent('Security'); + expect(allOptions[1]).toHaveAttribute('aria-checked', 'true'); + expect(allOptions[1]).toHaveTextContent('Observability'); }); }); @@ -305,6 +377,32 @@ describe('CasesTableFilters ', () => { expect(screen.getByTestId('options-filter-popover-button-assignees')).toBeInTheDocument(); }); + + it('shuld reset the assignees when deactivating the filter', async () => { + const overrideProps = { + ...props, + filterOptions: { + ...DEFAULT_FILTER_OPTIONS, + assignees: ['u_A_tM4n0wPkdiQ9smmd8o0Hr_h61XQfu8aRPh9GMoRoc_0'], + }, + }; + const license = licensingMock.createLicense({ + license: { type: 'platinum' }, + }); + + appMockRender = createAppMockRenderer({ license }); + appMockRender.render(); + + // deactivate the assignees filter + userEvent.click(screen.getByRole('button', { name: 'More' })); + await waitForEuiPopoverOpen(); + userEvent.click(screen.getByRole('option', { name: 'Assignees' })); + + expect(onFilterChanged).toHaveBeenCalledWith({ + ...DEFAULT_FILTER_OPTIONS, + assignees: [], + }); + }); }); describe('create case button', () => { @@ -534,7 +632,7 @@ describe('CasesTableFilters ', () => { userEvent.click(screen.getByRole('option', { name: 'Toggle' })); expect(screen.getByRole('button', { name: 'Toggle' })).toBeInTheDocument(); - const filterBar = screen.getByTestId('cases-table-filters-group'); + const filterBar = screen.getByTestId('cases-table-filters'); const allFilters = within(filterBar).getAllByRole('button'); const orderedFilterLabels = ['Severity', 'Status', 'Tags', 'Categories', 'Toggle', 'More']; orderedFilterLabels.forEach((label, index) => { @@ -587,7 +685,7 @@ describe('CasesTableFilters ', () => { userEvent.click(screen.getByRole('option', { name: 'Status' })); expect(screen.queryByRole('button', { name: 'Status' })).not.toBeInTheDocument(); - const filterBar = screen.getByTestId('cases-table-filters-group'); + const filterBar = screen.getByTestId('cases-table-filters'); const allFilters = within(filterBar).getAllByRole('button'); const orderedFilterLabels = ['Severity', 'Tags', 'Categories', 'More']; orderedFilterLabels.forEach((label, index) => { @@ -673,7 +771,7 @@ describe('CasesTableFilters ', () => { appMockRender.render(); - const filterBar = screen.getByTestId('cases-table-filters-group'); + const filterBar = screen.getByTestId('cases-table-filters'); let allFilters: HTMLElement[]; await waitFor(() => { allFilters = within(filterBar).getAllByRole('button'); @@ -703,7 +801,7 @@ describe('CasesTableFilters ', () => { appMockRender.render(); - const filterBar = screen.getByTestId('cases-table-filters-group'); + const filterBar = screen.getByTestId('cases-table-filters'); let allFilters: HTMLElement[]; await waitFor(() => { allFilters = within(filterBar).getAllByRole('button'); @@ -756,7 +854,7 @@ describe('CasesTableFilters ', () => { it('when a filter is active and isnt last in the list, it should move the filter to last position after deactivating and activating', async () => { appMockRender.render(); - const filterBar = screen.getByTestId('cases-table-filters-group'); + const filterBar = screen.getByTestId('cases-table-filters'); let allFilters = within(filterBar).getAllByRole('button'); let orderedFilterLabels = ['Severity', 'Status', 'Tags', 'Categories', 'More']; orderedFilterLabels.forEach((label, index) => { @@ -788,7 +886,7 @@ describe('CasesTableFilters ', () => { }); appMockRender.render(); - const filterBar = screen.getByTestId('cases-table-filters-group'); + const filterBar = screen.getByTestId('cases-table-filters'); let allFilters: HTMLElement[]; await waitFor(() => { allFilters = within(filterBar).getAllByRole('button'); diff --git a/x-pack/plugins/cases/public/components/all_cases/table_filters.tsx b/x-pack/plugins/cases/public/components/all_cases/table_filters.tsx index 8d472402b4e77..dbdc947418eca 100644 --- a/x-pack/plugins/cases/public/components/all_cases/table_filters.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/table_filters.tsx @@ -6,7 +6,7 @@ */ import React, { useCallback, useState } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiFieldSearch, EuiFilterGroup, EuiButton } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiFieldSearch, EuiButton } from '@elastic/eui'; import { mergeWith, isEqual } from 'lodash'; import { MoreFiltersSelectable } from './table_filter_config/more_filters_selectable'; import type { CaseStatuses } from '../../../common/types/domain'; @@ -16,11 +16,10 @@ import { useGetTags } from '../../containers/use_get_tags'; import { useGetCategories } from '../../containers/use_get_categories'; import type { CurrentUserProfile } from '../types'; import { useCasesFeatures } from '../../common/use_cases_features'; -import type { AssigneesFilteringSelection } from '../user_profiles/types'; import { useSystemFilterConfig } from './table_filter_config/use_system_filter_config'; import { useFilterConfig } from './table_filter_config/use_filter_config'; -interface CasesTableFiltersProps { +export interface CasesTableFiltersProps { countClosedCases: number | null; countInProgressCases: number | null; countOpenCases: number | null; @@ -29,7 +28,6 @@ interface CasesTableFiltersProps { availableSolutions: string[]; isSelectorView?: boolean; onCreateCasePressed?: () => void; - initialFilterOptions: Partial; isLoading: boolean; currentUserProfile: CurrentUserProfile; filterOptions: FilterOptions; @@ -50,29 +48,15 @@ const CasesTableFiltersComponent = ({ availableSolutions, isSelectorView = false, onCreateCasePressed, - initialFilterOptions, isLoading, currentUserProfile, filterOptions, }: CasesTableFiltersProps) => { const [search, setSearch] = useState(filterOptions.search); - const [selectedAssignees, setSelectedAssignees] = useState([]); const { data: tags = [] } = useGetTags(); const { data: categories = [] } = useGetCategories(); const { caseAssignmentAuthorized } = useCasesFeatures(); - const handleSelectedAssignees = useCallback( - (newAssignees: AssigneesFilteringSelection[]) => { - if (!isEqual(newAssignees, selectedAssignees)) { - setSelectedAssignees(newAssignees); - onFilterChanged({ - assignees: newAssignees.map((assignee) => assignee?.uid ?? null), - }); - } - }, - [selectedAssignees, onFilterChanged] - ); - const onFilterOptionsChange = useCallback( (partialFilterOptions: Partial) => { const newFilterOptions = mergeWith({}, filterOptions, partialFilterOptions, mergeCustomizer); @@ -91,13 +75,10 @@ const CasesTableFiltersComponent = ({ countInProgressCases, countOpenCases, currentUserProfile, - handleSelectedAssignees, hiddenStatuses, - initialFilterOptions, isLoading, isSelectorView, onFilterOptionsChange, - selectedAssignees, tags, }); @@ -126,7 +107,12 @@ const CasesTableFiltersComponent = ({ }, [onCreateCasePressed]); return ( - + {isSelectorView && onCreateCasePressed ? ( - - - {activeFilters.map((filter) => ( - {filter.render({ filterOptions })} - ))} - {isSelectorView || ( - - )} - - + {activeFilters.map((filter) => ( + + {filter.render({ filterOptions })} + + ))} + + {isSelectorView || ( + + + + )} ); }; diff --git a/x-pack/plugins/cases/public/components/all_cases/use_actions.test.tsx b/x-pack/plugins/cases/public/components/all_cases/use_actions.test.tsx index dcec2558aad46..e88b5e52c0848 100644 --- a/x-pack/plugins/cases/public/components/all_cases/use_actions.test.tsx +++ b/x-pack/plugins/cases/public/components/all_cases/use_actions.test.tsx @@ -8,6 +8,10 @@ import userEvent from '@testing-library/user-event'; import { waitFor } from '@testing-library/react'; import { renderHook } from '@testing-library/react-hooks/dom'; +import { + waitForEuiPopoverOpen, + waitForEuiContextMenuPanelTransition, +} from '@elastic/eui/lib/test/rtl'; import { useActions } from './use_actions'; import { basicCase } from '../../containers/mock'; @@ -69,13 +73,12 @@ describe('useActions', () => { const res = appMockRender.render(comp); userEvent.click(res.getByTestId(`case-action-popover-button-${basicCase.id}`)); + await waitForEuiPopoverOpen(); - await waitFor(() => { - expect(res.getByText('Actions')).toBeInTheDocument(); - expect(res.getByTestId(`case-action-status-panel-${basicCase.id}`)).toBeInTheDocument(); - expect(res.getByTestId('cases-bulk-action-delete')).toBeInTheDocument(); - expect(res.getByTestId('cases-action-copy-id')).toBeInTheDocument(); - }); + expect(res.getByText('Actions')).toBeInTheDocument(); + expect(res.getByTestId(`case-action-status-panel-${basicCase.id}`)).toBeInTheDocument(); + expect(res.getByTestId('cases-bulk-action-delete')).toBeInTheDocument(); + expect(res.getByTestId('cases-action-copy-id')).toBeInTheDocument(); }); it('change the status of the case', async () => { @@ -89,24 +92,16 @@ describe('useActions', () => { const res = appMockRender.render(comp); userEvent.click(res.getByTestId(`case-action-popover-button-${basicCase.id}`)); + await waitForEuiPopoverOpen(); - await waitFor(() => { - expect(res.getByTestId(`case-action-status-panel-${basicCase.id}`)).toBeInTheDocument(); - }); + userEvent.click(res.getByTestId(`case-action-status-panel-${basicCase.id}`)); + await waitForEuiContextMenuPanelTransition(); - userEvent.click(res.getByTestId(`case-action-status-panel-${basicCase.id}`), undefined, { - skipPointerEventsCheck: true, - }); + expect(res.getByTestId('cases-bulk-action-status-open')).toBeInTheDocument(); + expect(res.getByTestId('cases-bulk-action-status-in-progress')).toBeInTheDocument(); + expect(res.getByTestId('cases-bulk-action-status-closed')).toBeInTheDocument(); - await waitFor(() => { - expect(res.getByTestId('cases-bulk-action-status-open')).toBeInTheDocument(); - expect(res.getByTestId('cases-bulk-action-status-in-progress')).toBeInTheDocument(); - expect(res.getByTestId('cases-bulk-action-status-closed')).toBeInTheDocument(); - }); - - userEvent.click(res.getByTestId('cases-bulk-action-status-in-progress'), undefined, { - skipPointerEventsCheck: true, - }); + userEvent.click(res.getByTestId('cases-bulk-action-status-in-progress')); await waitFor(() => { expect(updateCasesSpy).toHaveBeenCalled(); @@ -124,25 +119,17 @@ describe('useActions', () => { const res = appMockRender.render(comp); userEvent.click(res.getByTestId(`case-action-popover-button-${basicCase.id}`)); + await waitForEuiPopoverOpen(); - await waitFor(() => { - expect(res.getByTestId(`case-action-severity-panel-${basicCase.id}`)).toBeInTheDocument(); - }); - - userEvent.click(res.getByTestId(`case-action-severity-panel-${basicCase.id}`), undefined, { - skipPointerEventsCheck: true, - }); + userEvent.click(res.getByTestId(`case-action-severity-panel-${basicCase.id}`)); + await waitForEuiContextMenuPanelTransition(); - await waitFor(() => { - expect(res.getByTestId('cases-bulk-action-severity-low')).toBeInTheDocument(); - expect(res.getByTestId('cases-bulk-action-severity-medium')).toBeInTheDocument(); - expect(res.getByTestId('cases-bulk-action-severity-high')).toBeInTheDocument(); - expect(res.getByTestId('cases-bulk-action-severity-critical')).toBeInTheDocument(); - }); + expect(res.getByTestId('cases-bulk-action-severity-low')).toBeInTheDocument(); + expect(res.getByTestId('cases-bulk-action-severity-medium')).toBeInTheDocument(); + expect(res.getByTestId('cases-bulk-action-severity-high')).toBeInTheDocument(); + expect(res.getByTestId('cases-bulk-action-severity-critical')).toBeInTheDocument(); - userEvent.click(res.getByTestId('cases-bulk-action-severity-medium'), undefined, { - skipPointerEventsCheck: true, - }); + userEvent.click(res.getByTestId('cases-bulk-action-severity-medium')); await waitFor(() => { expect(updateCasesSpy).toHaveBeenCalled(); @@ -167,14 +154,9 @@ describe('useActions', () => { const res = appMockRender.render(comp); userEvent.click(res.getByTestId(`case-action-popover-button-${basicCase.id}`)); + await waitForEuiPopoverOpen(); - await waitFor(() => { - expect(res.getByTestId('cases-action-copy-id')).toBeInTheDocument(); - }); - - userEvent.click(res.getByTestId('cases-action-copy-id'), undefined, { - skipPointerEventsCheck: true, - }); + userEvent.click(res.getByTestId('cases-action-copy-id')); expect(navigator.clipboard.writeText).toHaveBeenCalledWith(basicCase.id); @@ -195,14 +177,9 @@ describe('useActions', () => { const res = appMockRender.render(comp); userEvent.click(res.getByTestId(`case-action-popover-button-${basicCase.id}`)); + await waitForEuiPopoverOpen(); - await waitFor(() => { - expect(res.getByTestId('cases-bulk-action-delete')).toBeInTheDocument(); - }); - - userEvent.click(res.getByTestId('cases-bulk-action-delete'), undefined, { - skipPointerEventsCheck: true, - }); + userEvent.click(res.getByTestId('cases-bulk-action-delete')); await waitFor(() => { expect(res.getByTestId('confirm-delete-case-modal')).toBeInTheDocument(); @@ -224,22 +201,15 @@ describe('useActions', () => { const res = appMockRender.render(comp); userEvent.click(res.getByTestId(`case-action-popover-button-${basicCase.id}`)); + await waitForEuiPopoverOpen(); - await waitFor(() => { - expect(res.getByTestId('cases-bulk-action-delete')).toBeInTheDocument(); - }); - - userEvent.click(res.getByTestId('cases-bulk-action-delete'), undefined, { - skipPointerEventsCheck: true, - }); + userEvent.click(res.getByTestId('cases-bulk-action-delete')); await waitFor(() => { expect(res.getByTestId('confirm-delete-case-modal')).toBeInTheDocument(); }); - userEvent.click(res.getByTestId('confirmModalCancelButton'), undefined, { - skipPointerEventsCheck: true, - }); + userEvent.click(res.getByTestId('confirmModalCancelButton')); expect(res.queryByTestId('confirm-delete-case-modal')).toBeFalsy(); }); @@ -257,14 +227,9 @@ describe('useActions', () => { const res = appMockRender.render(comp); userEvent.click(res.getByTestId(`case-action-popover-button-${basicCase.id}`)); + await waitForEuiPopoverOpen(); - await waitFor(() => { - expect(res.getByTestId('cases-bulk-action-tags')).toBeInTheDocument(); - }); - - userEvent.click(res.getByTestId('cases-bulk-action-tags'), undefined, { - skipPointerEventsCheck: true, - }); + userEvent.click(res.getByTestId('cases-bulk-action-tags')); await waitFor(() => { expect(res.getByTestId('cases-edit-tags-flyout')).toBeInTheDocument(); @@ -297,14 +262,9 @@ describe('useActions', () => { const res = appMockRender.render(comp); userEvent.click(res.getByTestId(`case-action-popover-button-${basicCase.id}`)); + await waitForEuiPopoverOpen(); - await waitFor(() => { - expect(res.getByTestId('cases-bulk-action-assignees')).toBeInTheDocument(); - }); - - userEvent.click(res.getByTestId('cases-bulk-action-assignees'), undefined, { - skipPointerEventsCheck: true, - }); + userEvent.click(res.getByTestId('cases-bulk-action-assignees')); await waitFor(() => { expect(res.getByTestId('cases-edit-assignees-flyout')).toBeInTheDocument(); @@ -338,14 +298,13 @@ describe('useActions', () => { const res = appMockRender.render(comp); userEvent.click(res.getByTestId(`case-action-popover-button-${basicCase.id}`)); + await waitForEuiPopoverOpen(); - await waitFor(() => { - expect(res.getByTestId(`case-action-status-panel-${basicCase.id}`)).toBeInTheDocument(); - expect(res.getByTestId(`case-action-severity-panel-${basicCase.id}`)).toBeInTheDocument(); - expect(res.getByTestId('cases-bulk-action-delete')).toBeInTheDocument(); - expect(res.getByTestId(`actions-separator-${basicCase.id}`)).toBeInTheDocument(); - expect(res.getByTestId('cases-action-copy-id')).toBeInTheDocument(); - }); + expect(res.getByTestId(`case-action-status-panel-${basicCase.id}`)).toBeInTheDocument(); + expect(res.getByTestId(`case-action-severity-panel-${basicCase.id}`)).toBeInTheDocument(); + expect(res.getByTestId('cases-bulk-action-delete')).toBeInTheDocument(); + expect(res.getByTestId(`actions-separator-${basicCase.id}`)).toBeInTheDocument(); + expect(res.getByTestId('cases-action-copy-id')).toBeInTheDocument(); }); it('shows the correct actions with no delete permissions', async () => { @@ -358,14 +317,13 @@ describe('useActions', () => { const res = appMockRender.render(comp); userEvent.click(res.getByTestId(`case-action-popover-button-${basicCase.id}`)); + await waitForEuiPopoverOpen(); - await waitFor(() => { - expect(res.getByTestId(`case-action-status-panel-${basicCase.id}`)).toBeInTheDocument(); - expect(res.getByTestId(`case-action-severity-panel-${basicCase.id}`)).toBeInTheDocument(); - expect(res.getByTestId('cases-action-copy-id')).toBeInTheDocument(); - expect(res.queryByTestId('cases-bulk-action-delete')).toBeFalsy(); - expect(res.queryByTestId(`actions-separator-${basicCase.id}`)).toBeFalsy(); - }); + expect(res.getByTestId(`case-action-status-panel-${basicCase.id}`)).toBeInTheDocument(); + expect(res.getByTestId(`case-action-severity-panel-${basicCase.id}`)).toBeInTheDocument(); + expect(res.getByTestId('cases-action-copy-id')).toBeInTheDocument(); + expect(res.queryByTestId('cases-bulk-action-delete')).toBeFalsy(); + expect(res.queryByTestId(`actions-separator-${basicCase.id}`)).toBeFalsy(); }); it('shows the correct actions with only delete permissions', async () => { @@ -378,14 +336,13 @@ describe('useActions', () => { const res = appMockRender.render(comp); userEvent.click(res.getByTestId(`case-action-popover-button-${basicCase.id}`)); + await waitForEuiPopoverOpen(); - await waitFor(() => { - expect(res.queryByTestId(`case-action-status-panel-${basicCase.id}`)).toBeFalsy(); - expect(res.queryByTestId(`case-action-severity-panel-${basicCase.id}`)).toBeFalsy(); - expect(res.getByTestId('cases-action-copy-id')).toBeInTheDocument(); - expect(res.getByTestId('cases-bulk-action-delete')).toBeInTheDocument(); - expect(res.queryByTestId(`actions-separator-${basicCase.id}`)).toBeFalsy(); - }); + expect(res.queryByTestId(`case-action-status-panel-${basicCase.id}`)).toBeFalsy(); + expect(res.queryByTestId(`case-action-severity-panel-${basicCase.id}`)).toBeFalsy(); + expect(res.getByTestId('cases-action-copy-id')).toBeInTheDocument(); + expect(res.getByTestId('cases-bulk-action-delete')).toBeInTheDocument(); + expect(res.queryByTestId(`actions-separator-${basicCase.id}`)).toBeFalsy(); }); it('returns null if the user does not have update or delete permissions', async () => { diff --git a/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.test.tsx b/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.test.tsx index 5d9a6836f6e51..74e04c4afac89 100644 --- a/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.test.tsx +++ b/x-pack/plugins/cases/public/components/case_view/components/case_view_activity.test.tsx @@ -38,6 +38,8 @@ import { useOnUpdateField } from '../use_on_update_field'; import { useCasesFeatures } from '../../../common/use_cases_features'; import { ConnectorTypes, UserActionTypes } from '../../../../common/types/domain'; import { CaseMetricsFeature } from '../../../../common/types/api'; +import { useGetCaseConfiguration } from '../../../containers/configure/use_get_case_configuration'; +import { useGetCurrentUserProfile } from '../../../containers/user_profiles/use_get_current_user_profile'; jest.mock('../../../containers/use_infinite_find_case_user_actions'); jest.mock('../../../containers/use_find_case_user_actions'); @@ -56,9 +58,13 @@ jest.mock('../../../containers/use_get_case_connectors'); jest.mock('../../../containers/use_get_case_users'); jest.mock('../use_on_update_field'); jest.mock('../../../common/use_cases_features'); +jest.mock('../../../containers/configure/use_get_case_configuration'); +jest.mock('../../../containers/user_profiles/use_get_current_user_profile'); (useGetTags as jest.Mock).mockReturnValue({ data: ['coke', 'pepsi'], refetch: jest.fn() }); (useGetCategories as jest.Mock).mockReturnValue({ data: ['foo', 'bar'], refetch: jest.fn() }); +(useGetCaseConfiguration as jest.Mock).mockReturnValue({ data: {} }); +(useGetCurrentUserProfile as jest.Mock).mockReturnValue({ data: {}, isFetching: false }); const caseData: CaseUI = { ...basicCase, @@ -134,9 +140,17 @@ const useGetCaseUsersMock = useGetCaseUsers as jest.Mock; const useOnUpdateFieldMock = useOnUpdateField as jest.Mock; const useCasesFeaturesMock = useCasesFeatures as jest.Mock; -// FLAKY: https://github.com/elastic/kibana/issues/171575 -describe.skip('Case View Page activity tab', () => { +describe('Case View Page activity tab', () => { + let appMockRender: AppMockRenderer; const caseConnectors = getCaseConnectorsMockResponse(); + const platinumLicense = licensingMock.createLicense({ + license: { type: 'platinum' }, + }); + const basicLicense = licensingMock.createLicense({ + license: { type: 'basic' }, + }); + // eslint-disable-next-line prefer-object-spread + const originalGetComputedStyle = Object.assign({}, window.getComputedStyle); beforeAll(() => { useFindCaseUserActionsMock.mockReturnValue(defaultUseFindCaseUserActions); @@ -155,15 +169,36 @@ describe.skip('Case View Page activity tab', () => { isLoading: false, useOnUpdateField: jest.fn, }); - }); - let appMockRender: AppMockRenderer; - const platinumLicense = licensingMock.createLicense({ - license: { type: 'platinum' }, + Object.defineProperty(window, 'getComputedStyle', { + value: (el: HTMLElement) => { + /** + * This is based on the jsdom implementation of getComputedStyle + * https://github.com/jsdom/jsdom/blob/9dae17bf0ad09042cfccd82e6a9d06d3a615d9f4/lib/jsdom/browser/Window.js#L779-L820 + * + * It is missing global style parsing and will only return styles applied directly to an element. + * Will not return styles that are global or from emotion + */ + const declaration = new CSSStyleDeclaration(); + const { style } = el; + + Array.prototype.forEach.call(style, (property: string) => { + declaration.setProperty( + property, + style.getPropertyValue(property), + style.getPropertyPriority(property) + ); + }); + + return declaration; + }, + configurable: true, + writable: true, + }); }); - const basicLicense = licensingMock.createLicense({ - license: { type: 'basic' }, + afterAll(() => { + Object.defineProperty(window, 'getComputedStyle', originalGetComputedStyle); }); beforeEach(() => { @@ -178,13 +213,18 @@ describe.skip('Case View Page activity tab', () => { appMockRender = createAppMockRenderer({ license: platinumLicense }); appMockRender.render(); - expect(await screen.findByTestId('case-view-activity')).toBeInTheDocument(); - expect(await screen.findAllByTestId('user-actions-list')).toHaveLength(2); + const caseViewActivity = await screen.findByTestId('case-view-activity'); + expect(await within(caseViewActivity).findAllByTestId('user-actions-list')).toHaveLength(2); + expect( + await within(caseViewActivity).findByTestId('case-view-status-action-button') + ).toBeInTheDocument(); + expect(await screen.findByTestId('description')).toBeInTheDocument(); - expect(await screen.findByTestId('case-tags')).toBeInTheDocument(); - expect(await screen.findByTestId('cases-categories')).toBeInTheDocument(); - expect(await screen.findByTestId('connector-edit-header')).toBeInTheDocument(); - expect(await screen.findByTestId('case-view-status-action-button')).toBeInTheDocument(); + + const caseViewSidebar = await screen.findByTestId('case-view-page-sidebar'); + expect(await within(caseViewSidebar).findByTestId('case-tags')).toBeInTheDocument(); + expect(await within(caseViewSidebar).findByTestId('cases-categories')).toBeInTheDocument(); + expect(await within(caseViewSidebar).findByTestId('connector-edit-header')).toBeInTheDocument(); await waitForComponentToUpdate(); }); @@ -216,11 +256,7 @@ describe.skip('Case View Page activity tab', () => { }); appMockRender.render(); - expect(await screen.findByTestId('case-view-activity')).toBeInTheDocument(); - expect(await screen.findAllByTestId('user-actions-list')).toHaveLength(2); - expect(await screen.findByTestId('case-tags')).toBeInTheDocument(); - expect(await screen.findByTestId('cases-categories')).toBeInTheDocument(); - expect(await screen.findByTestId('connector-edit-header')).toBeInTheDocument(); + expect(screen.queryByTestId('case-view-status-action-button')).not.toBeInTheDocument(); await waitForComponentToUpdate(); @@ -233,11 +269,7 @@ describe.skip('Case View Page activity tab', () => { }); appMockRender.render(); - expect(await screen.findByTestId('case-view-activity')).toBeInTheDocument(); - expect(await screen.findAllByTestId('user-actions-list')).toHaveLength(2); - expect(await screen.findByTestId('case-tags')).toBeInTheDocument(); - expect(await screen.findByTestId('cases-categories')).toBeInTheDocument(); - expect(await screen.findByTestId('connector-edit-header')).toBeInTheDocument(); + expect(await screen.findByTestId('case-severity-selection')).toBeDisabled(); await waitForComponentToUpdate(); diff --git a/x-pack/plugins/cases/public/components/case_view/components/edit_category.test.tsx b/x-pack/plugins/cases/public/components/case_view/components/edit_category.test.tsx index 33c893d6f6b46..16804fb9580a3 100644 --- a/x-pack/plugins/cases/public/components/case_view/components/edit_category.test.tsx +++ b/x-pack/plugins/cases/public/components/case_view/components/edit_category.test.tsx @@ -242,7 +242,7 @@ describe('EditCategory ', () => { await waitFor(() => { expect(screen.getByTestId('categories-list')).toBeInTheDocument(); - expect(screen.getByText('My category')).toBeInTheDocument(); + expect(screen.getByTestId('comboBoxSearchInput')).toHaveValue('My category'); }); expect(screen.getByTestId('edit-category-submit')).toBeDisabled(); @@ -265,7 +265,7 @@ describe('EditCategory ', () => { await waitFor(() => { expect(screen.getByTestId('categories-list')).toBeInTheDocument(); - expect(screen.getByText('My category')).toBeInTheDocument(); + expect(screen.getByTestId('comboBoxSearchInput')).toHaveValue('My category'); }); userEvent.click(screen.getByTestId('edit-category-cancel')); @@ -276,7 +276,7 @@ describe('EditCategory ', () => { await waitFor(() => { expect(screen.getByTestId('categories-list')).toBeInTheDocument(); - expect(screen.getByText('category from the API')).toBeInTheDocument(); + expect(screen.getByTestId('comboBoxSearchInput')).toHaveValue('category from the API'); }); }); diff --git a/x-pack/plugins/cases/public/components/category/category_component.test.tsx b/x-pack/plugins/cases/public/components/category/category_component.test.tsx index 2b0d7ef39007b..6eb95600cd58c 100644 --- a/x-pack/plugins/cases/public/components/category/category_component.test.tsx +++ b/x-pack/plugins/cases/public/components/category/category_component.test.tsx @@ -47,7 +47,7 @@ describe('Category ', () => { it('renders category correctly', () => { render(); - expect(screen.getByText('new-category')).toBeInTheDocument(); + expect(screen.getByRole('combobox')).toHaveValue('new-category'); }); it('renders allow to add new category option', async () => { @@ -56,7 +56,7 @@ describe('Category ', () => { userEvent.type(screen.getByRole('combobox'), 'new{enter}'); expect(onChange).toBeCalledWith('new'); - expect(screen.getByText('new')).toBeInTheDocument(); + expect(screen.getByRole('combobox')).toHaveValue('new'); }); it('renders current option list', async () => { @@ -97,10 +97,10 @@ describe('Category ', () => { expect(onChange).toHaveBeenCalledWith('hi'); }); - userEvent.type(screen.getByRole('combobox'), 'Hi{enter}'); + userEvent.type(screen.getByRole('combobox'), ' there{enter}'); await waitFor(() => { - expect(onChange).toHaveBeenCalledWith('Hi'); + expect(onChange).toHaveBeenCalledWith('hi there'); }); }); }); diff --git a/x-pack/plugins/cases/public/components/create/assignees.test.tsx b/x-pack/plugins/cases/public/components/create/assignees.test.tsx index 9a32ab5f1ffd9..002cd2976f605 100644 --- a/x-pack/plugins/cases/public/components/create/assignees.test.tsx +++ b/x-pack/plugins/cases/public/components/create/assignees.test.tsx @@ -198,7 +198,7 @@ describe('Assignees', () => { }); act(() => { - userEvent.click(screen.getByText('Turtle')); + userEvent.click(screen.getByText('Turtle'), undefined, { skipPointerEventsCheck: true }); }); // ensure that the similar user is still available for selection diff --git a/x-pack/plugins/cases/public/containers/api.test.tsx b/x-pack/plugins/cases/public/containers/api.test.tsx index 6f42c31186dc7..5b00fabbbbf50 100644 --- a/x-pack/plugins/cases/public/containers/api.test.tsx +++ b/x-pack/plugins/cases/public/containers/api.test.tsx @@ -364,7 +364,7 @@ describe('Cases API', () => { await getCases({ filterOptions: { ...DEFAULT_FILTER_OPTIONS, - assignees: null, + assignees: [], }, queryParams: DEFAULT_QUERY_PARAMS, signal: abortCtrl.signal, @@ -373,7 +373,7 @@ describe('Cases API', () => { expect(fetchMock).toHaveBeenCalledWith(`${CASES_INTERNAL_URL}/_search`, { method: 'POST', body: JSON.stringify({ - assignees: 'none', + assignees: undefined, searchFields: DEFAULT_FILTER_OPTIONS.searchFields, ...DEFAULT_QUERY_PARAMS, }), diff --git a/x-pack/plugins/cases/public/containers/use_get_cases.test.tsx b/x-pack/plugins/cases/public/containers/use_get_cases.test.tsx index cd63e741327b5..72eb3326f199a 100644 --- a/x-pack/plugins/cases/public/containers/use_get_cases.test.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_cases.test.tsx @@ -11,10 +11,11 @@ import { useGetCases } from './use_get_cases'; import * as api from './api'; import type { AppMockRenderer } from '../common/mock'; import { createAppMockRenderer } from '../common/mock'; -import { useToasts } from '../common/lib/kibana'; +import { useToasts } from '../common/lib/kibana/hooks'; +import { OWNERS } from '../../common/constants'; jest.mock('./api'); -jest.mock('../common/lib/kibana'); +jest.mock('../common/lib/kibana/hooks'); describe('useGetCases', () => { const abortCtrl = new AbortController(); @@ -24,8 +25,8 @@ describe('useGetCases', () => { let appMockRender: AppMockRenderer; beforeEach(() => { - appMockRender = createAppMockRenderer(); jest.clearAllMocks(); + appMockRender = createAppMockRenderer(); }); it('calls getCases with correct arguments', async () => { @@ -33,9 +34,10 @@ describe('useGetCases', () => { const { waitForNextUpdate } = renderHook(() => useGetCases(), { wrapper: appMockRender.AppWrapper, }); + await waitForNextUpdate(); expect(spyOnGetCases).toBeCalledWith({ - filterOptions: { ...DEFAULT_FILTER_OPTIONS }, + filterOptions: { ...DEFAULT_FILTER_OPTIONS, owner: ['securitySolution'] }, queryParams: DEFAULT_QUERY_PARAMS, signal: abortCtrl.signal, }); @@ -46,6 +48,7 @@ describe('useGetCases', () => { spyOnGetCases.mockImplementation(() => { throw new Error('Something went wrong'); }); + const addError = jest.fn(); (useToasts as jest.Mock).mockReturnValue({ addSuccess, addError }); @@ -56,4 +59,97 @@ describe('useGetCases', () => { await waitForNextUpdate(); expect(addError).toHaveBeenCalled(); }); + + it('should set all owners when no owner is provided', async () => { + appMockRender = createAppMockRenderer({ owner: [] }); + + appMockRender.coreStart.application.capabilities = { + ...appMockRender.coreStart.application.capabilities, + observabilityCases: { + create_cases: true, + read_cases: true, + update_cases: true, + push_cases: true, + cases_connectors: true, + delete_cases: true, + cases_settings: true, + }, + securitySolutionCases: { + create_cases: true, + read_cases: true, + update_cases: true, + push_cases: true, + cases_connectors: true, + delete_cases: true, + cases_settings: true, + }, + }; + + const spyOnGetCases = jest.spyOn(api, 'getCases'); + const { waitForNextUpdate } = renderHook(() => useGetCases(), { + wrapper: appMockRender.AppWrapper, + }); + + await waitForNextUpdate(); + + expect(spyOnGetCases).toBeCalledWith({ + filterOptions: { ...DEFAULT_FILTER_OPTIONS, owner: [...OWNERS] }, + queryParams: DEFAULT_QUERY_PARAMS, + signal: abortCtrl.signal, + }); + }); + + it('should set only the available owners when no owner is provided', async () => { + appMockRender = createAppMockRenderer({ owner: [] }); + const spyOnGetCases = jest.spyOn(api, 'getCases'); + + const { waitForNextUpdate } = renderHook(() => useGetCases(), { + wrapper: appMockRender.AppWrapper, + }); + + await waitForNextUpdate(); + + expect(spyOnGetCases).toBeCalledWith({ + filterOptions: { ...DEFAULT_FILTER_OPTIONS, owner: ['cases'] }, + queryParams: DEFAULT_QUERY_PARAMS, + signal: abortCtrl.signal, + }); + }); + + it('should use the app owner when the filter options do not specify the owner', async () => { + appMockRender = createAppMockRenderer({ owner: ['observability'] }); + const spyOnGetCases = jest.spyOn(api, 'getCases'); + + const { waitForNextUpdate } = renderHook(() => useGetCases(), { + wrapper: appMockRender.AppWrapper, + }); + + await waitForNextUpdate(); + + expect(spyOnGetCases).toBeCalledWith({ + filterOptions: { ...DEFAULT_FILTER_OPTIONS, owner: ['observability'] }, + queryParams: DEFAULT_QUERY_PARAMS, + signal: abortCtrl.signal, + }); + }); + + it('respects the owner in the filter options if provided', async () => { + appMockRender = createAppMockRenderer({ owner: ['observability'] }); + const spyOnGetCases = jest.spyOn(api, 'getCases'); + + const { waitForNextUpdate } = renderHook( + () => useGetCases({ filterOptions: { owner: ['my-owner'] } }), + { + wrapper: appMockRender.AppWrapper, + } + ); + + await waitForNextUpdate(); + + expect(spyOnGetCases).toBeCalledWith({ + filterOptions: { ...DEFAULT_FILTER_OPTIONS, owner: ['my-owner'] }, + queryParams: DEFAULT_QUERY_PARAMS, + signal: abortCtrl.signal, + }); + }); }); diff --git a/x-pack/plugins/cases/public/containers/use_get_cases.tsx b/x-pack/plugins/cases/public/containers/use_get_cases.tsx index c182cfbfe3cc1..327f1a99cbe9b 100644 --- a/x-pack/plugins/cases/public/containers/use_get_cases.tsx +++ b/x-pack/plugins/cases/public/containers/use_get_cases.tsx @@ -13,6 +13,9 @@ import { useToasts } from '../common/lib/kibana'; import * as i18n from './translations'; import { getCases } from './api'; import type { ServerError } from '../types'; +import { useCasesContext } from '../components/cases_context/use_cases_context'; +import { useAvailableCasesOwners } from '../components/app/use_available_owners'; +import { getAllPermissionsExceptFrom } from '../utils/permissions'; export const initialData: CasesFindResponseUI = { cases: [], @@ -31,6 +34,17 @@ export const useGetCases = ( } = {} ): UseQueryResult => { const toasts = useToasts(); + const { owner } = useCasesContext(); + const availableSolutions = useAvailableCasesOwners(getAllPermissionsExceptFrom('delete')); + + const hasOwner = !!owner.length; + const initialOwner = hasOwner ? owner : availableSolutions; + + const ownerFilter = + params.filterOptions?.owner != null && params.filterOptions.owner.length > 0 + ? { owner: params.filterOptions.owner } + : { owner: initialOwner }; + return useQuery( casesQueriesKeys.cases(params), ({ signal }) => { @@ -38,6 +52,7 @@ export const useGetCases = ( filterOptions: { ...DEFAULT_FILTER_OPTIONS, ...(params.filterOptions ?? {}), + ...ownerFilter, }, queryParams: { ...DEFAULT_QUERY_PARAMS, diff --git a/x-pack/plugins/cases/public/containers/utils.test.ts b/x-pack/plugins/cases/public/containers/utils.test.ts index 16dd6f25f3d2f..bb784686df6f1 100644 --- a/x-pack/plugins/cases/public/containers/utils.test.ts +++ b/x-pack/plugins/cases/public/containers/utils.test.ts @@ -160,10 +160,6 @@ describe('utils', () => { expect(constructAssigneesFilter([])).toEqual({}); }); - it('returns none if the assignees are null', () => { - expect(constructAssigneesFilter(null)).toEqual({ assignees: 'none' }); - }); - it('returns none for null values in the assignees array', () => { expect(constructAssigneesFilter([null, '123'])).toEqual({ assignees: ['none', '123'] }); }); diff --git a/x-pack/plugins/cases/public/containers/utils.ts b/x-pack/plugins/cases/public/containers/utils.ts index 50f8524dbe78e..a00e2e3b738a0 100644 --- a/x-pack/plugins/cases/public/containers/utils.ts +++ b/x-pack/plugins/cases/public/containers/utils.ts @@ -148,12 +148,11 @@ export const createUpdateSuccessToaster = ( export const constructAssigneesFilter = ( assignees: FilterOptions['assignees'] ): { assignees?: string | string[] } => - assignees === null || assignees.length > 0 + assignees.length > 0 ? { - assignees: - assignees?.map((assignee) => - assignee === null ? NO_ASSIGNEES_FILTERING_KEYWORD : assignee - ) ?? NO_ASSIGNEES_FILTERING_KEYWORD, + assignees: assignees?.map((assignee) => + assignee === null ? NO_ASSIGNEES_FILTERING_KEYWORD : assignee + ) ?? [NO_ASSIGNEES_FILTERING_KEYWORD], } : {}; diff --git a/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/help_menu_links.tsx b/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/help_menu_links.tsx index 05ea7b9ce30cd..14edb22d97665 100644 --- a/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/help_menu_links.tsx +++ b/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/help_menu_links.tsx @@ -53,7 +53,6 @@ export const createHelpMenuLinks = ({ title: i18n.translate('xpack.cloudLinks.helpMenuLinks.connectionDetails', { defaultMessage: 'Connection details', }), - iconType: 'console', dataTestSubj: 'connectionDetailsHelpLink', onClick: () => { const modal = overlays.openModal( diff --git a/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/maybe_add_cloud_links.test.ts b/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/maybe_add_cloud_links.test.ts index c22b2bfdfd6ce..5fc4a9549682f 100644 --- a/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/maybe_add_cloud_links.test.ts +++ b/x-pack/plugins/cloud_integrations/cloud_links/public/maybe_add_cloud_links/maybe_add_cloud_links.test.ts @@ -95,7 +95,6 @@ describe('maybeAddCloudLinks', () => { }, Object { "dataTestSubj": "connectionDetailsHelpLink", - "iconType": "console", "onClick": [Function], "title": "Connection details", }, @@ -169,7 +168,6 @@ describe('maybeAddCloudLinks', () => { }, Object { "dataTestSubj": "connectionDetailsHelpLink", - "iconType": "console", "onClick": [Function], "title": "Connection details", }, diff --git a/x-pack/plugins/cloud_security_posture/common/constants.ts b/x-pack/plugins/cloud_security_posture/common/constants.ts index d2fffa9d26a2b..7f4f8c796f4c1 100644 --- a/x-pack/plugins/cloud_security_posture/common/constants.ts +++ b/x-pack/plugins/cloud_security_posture/common/constants.ts @@ -10,7 +10,7 @@ import { VulnSeverity, AwsCredentialsTypeFieldMap, GcpCredentialsTypeFieldMap, -} from './types'; +} from './types_old'; export const STATUS_ROUTE_PATH = '/internal/cloud_security_posture/status'; export const STATUS_API_CURRENT_VERSION = '1'; @@ -23,14 +23,17 @@ export const VULNERABILITIES_DASHBOARD_ROUTE_PATH = export const BENCHMARKS_ROUTE_PATH = '/internal/cloud_security_posture/benchmarks'; export const BENCHMARKS_API_CURRENT_VERSION = '1'; -export const FIND_CSP_RULE_TEMPLATE_ROUTE_PATH = '/internal/cloud_security_posture/rules/_find'; -export const FIND_CSP_RULE_TEMPLATE_API_CURRENT_VERSION = '1'; +export const FIND_CSP_BENCHMARK_RULE_ROUTE_PATH = '/internal/cloud_security_posture/rules/_find'; +export const FIND_CSP_BENCHMARK_RULE_API_CURRENT_VERSION = '1'; -export const DETECTION_RULE_ALERTS_STATUS_API_CURRENT_VERSION = '1'; -export const DETECTION_RULE_RULES_API_CURRENT_VERSION = '2023-10-31'; +export const CSP_BENCHMARK_RULES_BULK_ACTION_ROUTE_PATH = + '/internal/cloud_security_posture/rules/_bulk_action'; +export const CSP_BENCHMARK_RULES_BULK_ACTION_API_CURRENT_VERSION = '1'; export const GET_DETECTION_RULE_ALERTS_STATUS_PATH = '/internal/cloud_security_posture/detection_engine_rules/alerts/_status'; +export const DETECTION_RULE_ALERTS_STATUS_API_CURRENT_VERSION = '1'; +export const DETECTION_RULE_RULES_API_CURRENT_VERSION = '2023-10-31'; export const CLOUD_SECURITY_POSTURE_PACKAGE_NAME = 'cloud_security_posture'; // TODO: REMOVE CSP_LATEST_FINDINGS_DATA_VIEW and replace it with LATEST_FINDINGS_INDEX_PATTERN @@ -78,6 +81,8 @@ export const RULE_FAILED = `failed`; export const POSTURE_TYPE_ALL = 'all'; +export const CSPM_FINDINGS_STATS_INTERVAL = 5; + // A mapping of in-development features to their status. These features should be hidden from users but can be easily // activated via a simple code change in a single location. export const INTERNAL_FEATURE_FLAGS = { @@ -85,7 +90,9 @@ export const INTERNAL_FEATURE_FLAGS = { showFindingFlyoutEvidence: true, } as const; -export const CSP_RULE_TEMPLATE_SAVED_OBJECT_TYPE = 'csp-rule-template'; +export const CSP_BENCHMARK_RULE_SAVED_OBJECT_TYPE = 'csp-rule-template'; +export const INTERNAL_CSP_SETTINGS_SAVED_OBJECT_TYPE = 'cloud-security-posture-settings'; +export const INTERNAL_CSP_SETTINGS_SAVED_OBJECT_ID = 'csp-internal-settings'; export const CLOUDBEAT_VANILLA = 'cloudbeat/cis_k8s'; export const CLOUDBEAT_EKS = 'cloudbeat/cis_eks'; diff --git a/x-pack/plugins/cloud_security_posture/common/schemas/csp_finding.ts b/x-pack/plugins/cloud_security_posture/common/schemas/csp_finding.ts index bfe825b59d31b..bef70384b98cf 100644 --- a/x-pack/plugins/cloud_security_posture/common/schemas/csp_finding.ts +++ b/x-pack/plugins/cloud_security_posture/common/schemas/csp_finding.ts @@ -7,7 +7,7 @@ // TODO: this needs to be defined in a versioned schema import type { EcsEvent } from '@kbn/ecs'; -import type { CspRuleTemplateMetadata } from './csp_rule_template_metadata'; +import { CspBenchmarkRuleMetadata } from '../types/latest'; export interface CspFinding { '@timestamp': string; @@ -16,7 +16,7 @@ export interface CspFinding { cloud?: CspFindingCloud; // only available on CSPM findings result: CspFindingResult; resource: CspFindingResource; - rule: CspRuleTemplateMetadata; + rule: CspBenchmarkRuleMetadata; host: CspFindingHost; event: EcsEvent; agent: CspFindingAgent; diff --git a/x-pack/plugins/cloud_security_posture/common/schemas/csp_rule_template.ts b/x-pack/plugins/cloud_security_posture/common/schemas/csp_rule_template.ts deleted file mode 100644 index 0ed56fb5d7e3b..0000000000000 --- a/x-pack/plugins/cloud_security_posture/common/schemas/csp_rule_template.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { schema as rt, TypeOf } from '@kbn/config-schema'; -import { - cspRuleTemplateMetadataSchemaV840, - cspRuleTemplateMetadataSchemaV870, -} from './csp_rule_template_metadata'; - -export const cspRuleTemplateSchemaV830 = rt.object({ - audit: rt.string(), - benchmark: rt.object({ name: rt.string(), version: rt.string() }), - default_value: rt.maybe(rt.string()), - description: rt.string(), - enabled: rt.boolean(), - id: rt.string(), - impact: rt.maybe(rt.string()), - muted: rt.boolean(), - name: rt.string(), - profile_applicability: rt.string(), - rationale: rt.string(), - references: rt.maybe(rt.string()), - rego_rule_id: rt.string(), - remediation: rt.string(), - section: rt.string(), - tags: rt.arrayOf(rt.string()), - version: rt.string(), -}); - -export const cspRuleTemplateSchemaV840 = rt.object({ - enabled: rt.boolean(), - metadata: cspRuleTemplateMetadataSchemaV840, - muted: rt.boolean(), -}); - -export const cspRuleTemplateSchemaV870 = rt.object({ - metadata: cspRuleTemplateMetadataSchemaV870, -}); - -export type CspRuleTemplateV830 = TypeOf; -export type CspRuleTemplateV840 = TypeOf; -export type CspRuleTemplateV870 = TypeOf; - -export type CspRuleTemplate = CspRuleTemplateV870; diff --git a/x-pack/plugins/cloud_security_posture/common/schemas/csp_rule_template_api/get_csp_rule_template.ts b/x-pack/plugins/cloud_security_posture/common/schemas/csp_rule_template_api/get_csp_rule_template.ts deleted file mode 100644 index 350909e540d4d..0000000000000 --- a/x-pack/plugins/cloud_security_posture/common/schemas/csp_rule_template_api/get_csp_rule_template.ts +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { schema } from '@kbn/config-schema'; - -// this pages follows versioning interface strategy https://docs.elastic.dev/kibana-dev-docs/versioning-interfaces - -const DEFAULT_RULES_TEMPLATE_PER_PAGE = 25; - -export const findCspRuleTemplateRequest = schema.object({ - /** - * An Elasticsearch simple_query_string - */ - search: schema.maybe(schema.string()), - - /** - * The page of objects to return - */ - page: schema.number({ defaultValue: 1, min: 1 }), - - /** - * The number of objects to include in each page - */ - perPage: schema.number({ defaultValue: DEFAULT_RULES_TEMPLATE_PER_PAGE, min: 0 }), - - /** - * Fields to retrieve from CspRuleTemplate saved object - */ - fields: schema.maybe(schema.arrayOf(schema.string())), - - /** - * The fields to perform the parsed query against. - * Valid fields are fields which mapped to 'text' in cspRuleTemplateSavedObjectMapping - */ - searchFields: schema.arrayOf( - schema.oneOf([schema.literal('metadata.name.text'), schema.literal('metadata.section.text')]), - { defaultValue: ['metadata.name.text'] } - ), - - /** - * Sort Field - */ - sortField: schema.oneOf( - [ - schema.literal('metadata.name'), - schema.literal('metadata.section'), - schema.literal('metadata.id'), - schema.literal('metadata.version'), - schema.literal('metadata.benchmark.id'), - schema.literal('metadata.benchmark.name'), - schema.literal('metadata.benchmark.posture_type'), - schema.literal('metadata.benchmark.version'), - schema.literal('metadata.benchmark.rule_number'), - ], - { - defaultValue: 'metadata.benchmark.rule_number', - } - ), - - /** - * The order to sort by - */ - sortOrder: schema.oneOf([schema.literal('asc'), schema.literal('desc')], { - defaultValue: 'asc', - }), - - /** - * benchmark id - */ - benchmarkId: schema.maybe( - schema.oneOf([schema.literal('cis_k8s'), schema.literal('cis_eks'), schema.literal('cis_aws')]) - ), - - /** - * package_policy_id - */ - packagePolicyId: schema.maybe(schema.string()), - - /** - * rule section - */ - section: schema.maybe(schema.string()), -}); diff --git a/x-pack/plugins/cloud_security_posture/common/schemas/csp_rule_template_metadata.ts b/x-pack/plugins/cloud_security_posture/common/schemas/csp_rule_template_metadata.ts deleted file mode 100644 index b466ed7e70d2d..0000000000000 --- a/x-pack/plugins/cloud_security_posture/common/schemas/csp_rule_template_metadata.ts +++ /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 { schema as rt, TypeOf } from '@kbn/config-schema'; -import { CSPM_POLICY_TEMPLATE, KSPM_POLICY_TEMPLATE } from '../constants'; - -export const cspRuleTemplateMetadataSchemaV840 = rt.object({ - audit: rt.string(), - benchmark: rt.object({ - name: rt.string(), - id: rt.string(), - version: rt.string(), - }), - default_value: rt.maybe(rt.string()), - description: rt.string(), - id: rt.string(), - impact: rt.maybe(rt.string()), - name: rt.string(), - profile_applicability: rt.string(), - rationale: rt.string(), - references: rt.maybe(rt.string()), - rego_rule_id: rt.string(), - remediation: rt.string(), - section: rt.string(), - tags: rt.arrayOf(rt.string()), - version: rt.string(), -}); - -export const cspRuleTemplateMetadataSchemaV870 = rt.object({ - audit: rt.string(), - benchmark: rt.object({ - name: rt.string(), - posture_type: rt.maybe( - rt.oneOf([rt.literal(CSPM_POLICY_TEMPLATE), rt.literal(KSPM_POLICY_TEMPLATE)]) - ), - id: rt.string(), - version: rt.string(), - rule_number: rt.maybe(rt.string()), - }), - default_value: rt.maybe(rt.string()), - description: rt.string(), - id: rt.string(), - impact: rt.maybe(rt.string()), - name: rt.string(), - profile_applicability: rt.string(), - rationale: rt.string(), - references: rt.maybe(rt.string()), - rego_rule_id: rt.string(), - remediation: rt.string(), - section: rt.string(), - tags: rt.arrayOf(rt.string()), - version: rt.string(), -}); - -export type CspRuleMetadataV840 = TypeOf; -export type CspRuleMetadataV870 = TypeOf; -export type CspRuleTemplateMetadata = CspRuleMetadataV870; diff --git a/x-pack/plugins/cloud_security_posture/common/schemas/csp_vulnerability_finding.ts b/x-pack/plugins/cloud_security_posture/common/schemas/csp_vulnerability_finding.ts index f650b115316df..efe26dc6648ba 100644 --- a/x-pack/plugins/cloud_security_posture/common/schemas/csp_vulnerability_finding.ts +++ b/x-pack/plugins/cloud_security_posture/common/schemas/csp_vulnerability_finding.ts @@ -7,7 +7,7 @@ // TODO: this needs to be defined in a versioned schema import type { EcsEvent } from '@kbn/ecs'; -import { VulnSeverity } from '../types'; +import { VulnSeverity } from '../types_old'; export interface CspVulnerabilityFinding { '@timestamp': string; diff --git a/x-pack/plugins/cloud_security_posture/common/schemas/index.ts b/x-pack/plugins/cloud_security_posture/common/schemas/index.ts index c7730abc06dba..8c8dcdc52f0f4 100644 --- a/x-pack/plugins/cloud_security_posture/common/schemas/index.ts +++ b/x-pack/plugins/cloud_security_posture/common/schemas/index.ts @@ -4,7 +4,4 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - -export * from './csp_rule_template_metadata'; -export * from './csp_rule_template'; export * from './csp_vulnerability_finding'; diff --git a/x-pack/plugins/cloud_security_posture/common/schemas/benchmark.ts b/x-pack/plugins/cloud_security_posture/common/types/benchmarks/v1.ts similarity index 68% rename from x-pack/plugins/cloud_security_posture/common/schemas/benchmark.ts rename to x-pack/plugins/cloud_security_posture/common/types/benchmarks/v1.ts index 0d30ae992dd11..48c9c1446b0f6 100644 --- a/x-pack/plugins/cloud_security_posture/common/schemas/benchmark.ts +++ b/x-pack/plugins/cloud_security_posture/common/types/benchmarks/v1.ts @@ -4,7 +4,28 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { type TypeOf, schema } from '@kbn/config-schema'; + +import { schema, TypeOf } from '@kbn/config-schema'; +import type { PackagePolicy } from '@kbn/fleet-plugin/common'; +import type { AgentPolicy } from '@kbn/fleet-plugin/common'; + +export type AgentPolicyStatus = Pick & { agents: number }; + +export const benchmarkScoreSchema = schema.object({ + postureScore: schema.number({ defaultValue: 0, min: 0 }), + resourcesEvaluated: schema.number({ defaultValue: 0, min: 0 }), + totalFailed: schema.number({ defaultValue: 0, min: 0 }), + totalFindings: schema.number({ defaultValue: 0, min: 0 }), + totalPassed: schema.number({ defaultValue: 0, min: 0 }), +}); + +export type BenchmarkScore = TypeOf; + +export interface Benchmark { + package_policy: PackagePolicy; + agent_policy: AgentPolicyStatus; + rules_count: number; +} export const DEFAULT_BENCHMARKS_PER_PAGE = 20; export const BENCHMARK_PACKAGE_POLICY_PREFIX = 'package_policy.'; @@ -60,3 +81,10 @@ export const benchmarksQueryParamsSchema = schema.object({ }); export type BenchmarksQueryParams = TypeOf; + +export interface GetBenchmarkResponse { + items: Benchmark[]; + total: number; + page: number; + perPage: number; +} diff --git a/x-pack/plugins/cloud_security_posture/common/types/benchmarks/v2.ts b/x-pack/plugins/cloud_security_posture/common/types/benchmarks/v2.ts new file mode 100644 index 0000000000000..f5052d08f9914 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/common/types/benchmarks/v2.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +// Optionally, re-export the entire set of types. Interfaces and types declared after this will override v1 declarations. +import { schema } from '@kbn/config-schema'; +import type { BenchmarkScore } from './v1'; + +export type { BenchmarkScore } from './v1'; + +export type BenchmarksCisId = 'cis_k8s' | 'cis_azure' | 'cis_aws' | 'cis_eks' | 'cis_gcp'; + +export interface Benchmark { + id: BenchmarksCisId; + name: string; + version: string; + score: BenchmarkScore; + evaluation: number; +} + +export interface GetBenchmarkResponse { + items: Benchmark[]; +} + +export const benchmarkResponseSchema = schema.object({ + items: schema.arrayOf( + schema.object({ + id: schema.oneOf([ + schema.literal('cis_k8s'), + schema.literal('cis_azure'), + schema.literal('cis_aws'), + schema.literal('cis_eks'), + schema.literal('cis_gcp'), + ]), + name: schema.string(), + version: schema.string(), + score: schema.object({ + postureScore: schema.number({ defaultValue: 0, min: 0 }), + resourcesEvaluated: schema.number({ defaultValue: 0, min: 0 }), + totalFailed: schema.number({ defaultValue: 0, min: 0 }), + totalFindings: schema.number({ defaultValue: 0, min: 0 }), + totalPassed: schema.number({ defaultValue: 0, min: 0 }), + }), + evaluation: schema.number({ defaultValue: 0, min: 0 }), + }) + ), +}); diff --git a/x-pack/plugins/cloud_security_posture/common/types/index.ts b/x-pack/plugins/cloud_security_posture/common/types/index.ts new file mode 100644 index 0000000000000..d6e804c33f017 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/common/types/index.ts @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * as rulesV1 from './rules/v1'; +export * as rulesV2 from './rules/v2'; +export * as rulesV3 from './rules/v3'; + +export * as benchmarkV1 from './benchmarks/v1'; +export * as benchmarkV2 from './benchmarks/v2'; + +// Explicit export of everything from latest +export type { + cspBenchmarkRuleMetadataSchema, + CspBenchmarkRuleMetadata, + CspBenchmarkRule, + FindCspBenchmarkRuleRequest, + FindCspBenchmarkRuleResponse, + BenchmarkScore, + Benchmark, + GetBenchmarkResponse, +} from './latest'; diff --git a/x-pack/test/detection_engine_api_integration/utils/get_query_all_signals.ts b/x-pack/plugins/cloud_security_posture/common/types/latest.ts similarity index 77% rename from x-pack/test/detection_engine_api_integration/utils/get_query_all_signals.ts rename to x-pack/plugins/cloud_security_posture/common/types/latest.ts index fd003857fdc1f..9951c7a418ecc 100644 --- a/x-pack/test/detection_engine_api_integration/utils/get_query_all_signals.ts +++ b/x-pack/plugins/cloud_security_posture/common/types/latest.ts @@ -5,6 +5,5 @@ * 2.0. */ -export const getQueryAllSignals = () => ({ - query: { match_all: {} }, -}); +export * from './rules/v3'; +export * from './benchmarks/v2'; diff --git a/x-pack/plugins/cloud_security_posture/common/types/rules/v1.ts b/x-pack/plugins/cloud_security_posture/common/types/rules/v1.ts new file mode 100644 index 0000000000000..afc2b705ab5c3 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/common/types/rules/v1.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { schema, TypeOf } from '@kbn/config-schema'; + +// Since version 8.3.0 +export const cspBenchmarkRuleSchema = schema.object({ + audit: schema.string(), + benchmark: schema.object({ name: schema.string(), version: schema.string() }), + default_value: schema.maybe(schema.string()), + description: schema.string(), + enabled: schema.boolean(), + id: schema.string(), + impact: schema.maybe(schema.string()), + muted: schema.boolean(), + name: schema.string(), + profile_applicability: schema.string(), + rationale: schema.string(), + references: schema.maybe(schema.string()), + rego_rule_id: schema.string(), + remediation: schema.string(), + section: schema.string(), + tags: schema.arrayOf(schema.string()), + version: schema.string(), +}); + +export type CspBenchmarkRule = TypeOf; diff --git a/x-pack/plugins/cloud_security_posture/common/types/rules/v2.ts b/x-pack/plugins/cloud_security_posture/common/types/rules/v2.ts new file mode 100644 index 0000000000000..d88ae6adc089a --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/common/types/rules/v2.ts @@ -0,0 +1,38 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { schema, TypeOf } from '@kbn/config-schema'; + +// Since version 8.4.0 +export const cspBenchmarkRuleMetadataSchema = schema.object({ + audit: schema.string(), + benchmark: schema.object({ + name: schema.string(), + id: schema.string(), + version: schema.string(), + }), + default_value: schema.maybe(schema.string()), + description: schema.string(), + id: schema.string(), + impact: schema.maybe(schema.string()), + name: schema.string(), + profile_applicability: schema.string(), + rationale: schema.string(), + references: schema.maybe(schema.string()), + rego_rule_id: schema.string(), + remediation: schema.string(), + section: schema.string(), + tags: schema.arrayOf(schema.string()), + version: schema.string(), +}); + +export const cspBenchmarkRuleSchema = schema.object({ + enabled: schema.boolean(), + metadata: cspBenchmarkRuleMetadataSchema, + muted: schema.boolean(), +}); + +export type CspBenchmarkRule = TypeOf; diff --git a/x-pack/plugins/cloud_security_posture/common/types/rules/v3.ts b/x-pack/plugins/cloud_security_posture/common/types/rules/v3.ts new file mode 100644 index 0000000000000..cef3e445b91a8 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/common/types/rules/v3.ts @@ -0,0 +1,164 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { schema, TypeOf } from '@kbn/config-schema'; + +import { CSPM_POLICY_TEMPLATE, KSPM_POLICY_TEMPLATE } from '../../constants'; + +const DEFAULT_BENCHMARK_RULES_PER_PAGE = 25; + +// Since version 8.7.0 +export const cspBenchmarkRuleMetadataSchema = schema.object({ + audit: schema.string(), + benchmark: schema.object({ + name: schema.string(), + posture_type: schema.maybe( + schema.oneOf([schema.literal(CSPM_POLICY_TEMPLATE), schema.literal(KSPM_POLICY_TEMPLATE)]) + ), + id: schema.string(), + version: schema.string(), + rule_number: schema.maybe(schema.string()), + }), + default_value: schema.maybe(schema.string()), + description: schema.string(), + id: schema.string(), + impact: schema.maybe(schema.string()), + name: schema.string(), + profile_applicability: schema.string(), + rationale: schema.string(), + references: schema.maybe(schema.string()), + rego_rule_id: schema.string(), + remediation: schema.string(), + section: schema.string(), + tags: schema.arrayOf(schema.string()), + version: schema.string(), +}); + +export type CspBenchmarkRuleMetadata = TypeOf; + +export const cspBenchmarkRuleSchema = schema.object({ + metadata: cspBenchmarkRuleMetadataSchema, +}); + +export type CspBenchmarkRule = TypeOf; + +export const findCspBenchmarkRuleRequestSchema = schema.object({ + /** + * An Elasticsearch simple_query_string + */ + search: schema.maybe(schema.string()), + + /** + * The page of objects to return + */ + page: schema.number({ defaultValue: 1, min: 1 }), + + /** + * The number of objects to include in each page + */ + perPage: schema.number({ defaultValue: DEFAULT_BENCHMARK_RULES_PER_PAGE, min: 0 }), + + /** + * Fields to retrieve from CspBenchmarkRule saved object + */ + fields: schema.maybe(schema.arrayOf(schema.string())), + + /** + * The fields to perform the parsed query against. + * Valid fields are fields which mapped to 'text' in cspBenchmarkRuleSavedObjectMapping + */ + searchFields: schema.arrayOf( + schema.oneOf([schema.literal('metadata.name.text'), schema.literal('metadata.section.text')]), + { defaultValue: ['metadata.name.text'] } + ), + + /** + * Sort Field + */ + sortField: schema.oneOf( + [ + schema.literal('metadata.name'), + schema.literal('metadata.section'), + schema.literal('metadata.id'), + schema.literal('metadata.version'), + schema.literal('metadata.benchmark.id'), + schema.literal('metadata.benchmark.name'), + schema.literal('metadata.benchmark.posture_type'), + schema.literal('metadata.benchmark.version'), + schema.literal('metadata.benchmark.rule_number'), + ], + { + defaultValue: 'metadata.benchmark.rule_number', + } + ), + + /** + * The order to sort by + */ + sortOrder: schema.oneOf([schema.literal('asc'), schema.literal('desc')], { + defaultValue: 'asc', + }), + + /** + * benchmark id + */ + benchmarkId: schema.maybe( + schema.oneOf([schema.literal('cis_k8s'), schema.literal('cis_eks'), schema.literal('cis_aws')]) + ), + + /** + * package_policy_id + */ + packagePolicyId: schema.maybe(schema.string()), + + /** + * rule section + */ + section: schema.maybe(schema.string()), +}); + +export type FindCspBenchmarkRuleRequest = TypeOf; + +export interface FindCspBenchmarkRuleResponse { + items: CspBenchmarkRule[]; + total: number; + page: number; + perPage: number; +} + +export const cspBenchmarkRules = schema.arrayOf( + schema.object({ + benchmark_id: schema.string(), + benchmark_version: schema.string(), + rule_number: schema.string(), + }) +); + +export const cspBenchmarkRulesBulkActionRequestSchema = schema.object({ + action: schema.oneOf([schema.literal('mute'), schema.literal('unmute')]), + rules: cspBenchmarkRules, +}); + +export type CspBenchmarkRules = TypeOf; + +export type CspBenchmarkRulesBulkActionRequestSchema = TypeOf< + typeof cspBenchmarkRulesBulkActionRequestSchema +>; + +const rulesStates = schema.recordOf( + schema.string(), + schema.object({ + muted: schema.boolean(), + }) +); + +export const cspSettingsSchema = schema.object({ + rules: rulesStates, +}); + +export type CspBenchmarkRulesStates = TypeOf; +export type CspSettings = TypeOf; diff --git a/x-pack/plugins/cloud_security_posture/common/types.ts b/x-pack/plugins/cloud_security_posture/common/types_old.ts similarity index 85% rename from x-pack/plugins/cloud_security_posture/common/types.ts rename to x-pack/plugins/cloud_security_posture/common/types_old.ts index aa25c70eb247d..d3706c51469f8 100644 --- a/x-pack/plugins/cloud_security_posture/common/types.ts +++ b/x-pack/plugins/cloud_security_posture/common/types_old.ts @@ -5,13 +5,11 @@ * 2.0. */ import { type TypeOf } from '@kbn/config-schema'; -import type { PackagePolicy, AgentPolicy } from '@kbn/fleet-plugin/common'; import { CspFinding } from './schemas/csp_finding'; import { SUPPORTED_CLOUDBEAT_INPUTS, SUPPORTED_POLICY_TEMPLATES } from './constants'; -import { CspRuleTemplateMetadata } from './schemas/csp_rule_template_metadata'; -import { CspRuleTemplate } from './schemas'; -import { findCspRuleTemplateRequest } from './schemas/csp_rule_template_api/get_csp_rule_template'; + import { getComplianceDashboardSchema } from './schemas/stats'; +import type { CspBenchmarkRuleMetadata } from './types/latest'; export type AwsCredentialsType = | 'assume_role' @@ -32,11 +30,11 @@ export type GcpCredentialsTypeFieldMap = { export type AzureCredentialsType = | 'arm_template' + | 'manual' // TODO: remove for stack version 8.13 | 'service_principal_with_client_secret' | 'service_principal_with_client_certificate' | 'service_principal_with_client_username_and_password' - | 'managed_identity' - | 'manual'; + | 'managed_identity'; export type AzureCredentialsTypeFieldMap = { [key in AzureCredentialsType]: string[]; @@ -143,41 +141,17 @@ export interface BaseCspSetupStatus { export type CspSetupStatus = BaseCspSetupStatus; -export type AgentPolicyStatus = Pick & { agents: number }; - -export interface Benchmark { - package_policy: PackagePolicy; - agent_policy: AgentPolicyStatus; - rules_count: number; -} - -export type BenchmarkId = CspRuleTemplateMetadata['benchmark']['id']; -export type BenchmarkName = CspRuleTemplateMetadata['benchmark']['name']; -export type RuleSection = CspRuleTemplateMetadata['section']; +export type BenchmarkId = CspBenchmarkRuleMetadata['benchmark']['id']; +export type BenchmarkName = CspBenchmarkRuleMetadata['benchmark']['name']; +export type RuleSection = CspBenchmarkRuleMetadata['section']; // Fleet Integration types export type PostureInput = typeof SUPPORTED_CLOUDBEAT_INPUTS[number]; export type CloudSecurityPolicyTemplate = typeof SUPPORTED_POLICY_TEMPLATES[number]; export type PosturePolicyTemplate = Extract; -export interface GetBenchmarkResponse { - items: Benchmark[]; - total: number; - page: number; - perPage: number; -} - -export type GetCspRuleTemplateRequest = TypeOf; - export type GetComplianceDashboardRequest = TypeOf; -export interface GetCspRuleTemplateResponse { - items: CspRuleTemplate[]; - total: number; - page: number; - perPage: number; -} - // CNVM DASHBOARD interface AccountVulnStats { diff --git a/x-pack/plugins/cloud_security_posture/common/utils/helpers.ts b/x-pack/plugins/cloud_security_posture/common/utils/helpers.ts index ba8e6f9813832..2f7706705da45 100644 --- a/x-pack/plugins/cloud_security_posture/common/utils/helpers.ts +++ b/x-pack/plugins/cloud_security_posture/common/utils/helpers.ts @@ -17,7 +17,7 @@ import { import { CLOUD_SECURITY_POSTURE_PACKAGE_NAME, CLOUDBEAT_VANILLA, - CSP_RULE_TEMPLATE_SAVED_OBJECT_TYPE, + CSP_BENCHMARK_RULE_SAVED_OBJECT_TYPE, AWS_CREDENTIALS_TYPE_TO_FIELDS_MAP, GCP_CREDENTIALS_TYPE_TO_FIELDS_MAP, AZURE_CREDENTIALS_TYPE_TO_FIELDS_MAP, @@ -30,7 +30,8 @@ import type { GcpCredentialsType, AzureCredentialsType, RuleSection, -} from '../types'; +} from '../types_old'; +import type { BenchmarksCisId } from '../types/latest'; /** * @example @@ -50,9 +51,9 @@ export const extractErrorMessage = (e: unknown, defaultMessage = 'Unknown Error' }; export const getBenchmarkFilter = (type: BenchmarkId, section?: RuleSection): string => - `${CSP_RULE_TEMPLATE_SAVED_OBJECT_TYPE}.attributes.metadata.benchmark.id: "${type}"${ + `${CSP_BENCHMARK_RULE_SAVED_OBJECT_TYPE}.attributes.metadata.benchmark.id: "${type}"${ section - ? ` AND ${CSP_RULE_TEMPLATE_SAVED_OBJECT_TYPE}.attributes.metadata.section: "${section}"` + ? ` AND ${CSP_BENCHMARK_RULE_SAVED_OBJECT_TYPE}.attributes.metadata.section: "${section}"` : '' }`; @@ -172,3 +173,35 @@ export const cleanupCredentials = (packagePolicy: NewPackagePolicy | UpdatePacka // nothing to do, return unmutated policy return packagePolicy; }; + +export const getBenchmarkCisName = (benchmarkId: BenchmarksCisId) => { + switch (benchmarkId) { + case 'cis_k8s': + return 'CIS Kubernetes'; + case 'cis_azure': + return 'CIS Azure'; + case 'cis_aws': + return 'CIS AWS'; + case 'cis_eks': + return 'CIS EKS'; + case 'cis_gcp': + return 'CIS GCP'; + } + return null; +}; + +export const getBenchmarkApplicableTo = (benchmarkId: BenchmarksCisId) => { + switch (benchmarkId) { + case 'cis_k8s': + return 'Kubernetes'; + case 'cis_azure': + return 'Microsoft Azure'; + case 'cis_aws': + return 'Amazon Web Services'; + case 'cis_eks': + return 'Amazon Elastic Kubernetes Service'; + case 'cis_gcp': + return 'Google Cloud Provider'; + } + return null; +}; diff --git a/x-pack/plugins/cloud_security_posture/public/common/api/use_setup_status_api.ts b/x-pack/plugins/cloud_security_posture/public/common/api/use_setup_status_api.ts index afb7a89c69e6f..35f49282a475e 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/api/use_setup_status_api.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/api/use_setup_status_api.ts @@ -7,7 +7,7 @@ import { useQuery, type UseQueryOptions } from '@tanstack/react-query'; import { useKibana } from '../hooks/use_kibana'; -import { type CspSetupStatus } from '../../../common/types'; +import { type CspSetupStatus } from '../../../common/types_old'; import { STATUS_API_CURRENT_VERSION, STATUS_ROUTE_PATH } from '../../../common/constants'; const getCspSetupStatusQueryKey = 'csp_status_key'; diff --git a/x-pack/plugins/cloud_security_posture/public/common/api/use_stats_api.ts b/x-pack/plugins/cloud_security_posture/public/common/api/use_stats_api.ts index 834a75581519f..dd893ff356865 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/api/use_stats_api.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/api/use_stats_api.ts @@ -7,7 +7,7 @@ import { useQuery, UseQueryOptions } from '@tanstack/react-query'; import { useKibana } from '../hooks/use_kibana'; -import { ComplianceDashboardDataV2, PosturePolicyTemplate } from '../../../common/types'; +import { ComplianceDashboardDataV2, PosturePolicyTemplate } from '../../../common/types_old'; import { CSPM_POLICY_TEMPLATE, KSPM_POLICY_TEMPLATE, diff --git a/x-pack/plugins/cloud_security_posture/public/common/api/use_vulnerability_dashboard_api.ts b/x-pack/plugins/cloud_security_posture/public/common/api/use_vulnerability_dashboard_api.ts index 11113949d0972..4114a49cdf181 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/api/use_vulnerability_dashboard_api.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/api/use_vulnerability_dashboard_api.ts @@ -6,7 +6,7 @@ */ import { useQuery, UseQueryOptions } from '@tanstack/react-query'; -import { CnvmDashboardData } from '../../../common/types'; +import { CnvmDashboardData } from '../../../common/types_old'; import { useKibana } from '../hooks/use_kibana'; import { VULNERABILITIES_DASHBOARD_ROUTE_PATH } from '../../../common/constants'; diff --git a/x-pack/plugins/cloud_security_posture/public/common/constants.ts b/x-pack/plugins/cloud_security_posture/public/common/constants.ts index 60e15f54b1687..404ea0f036017 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/constants.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/constants.ts @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import { euiThemeVars } from '@kbn/ui-theme'; -import type { CloudSecurityPolicyTemplate, PostureInput } from '../../common/types'; +import type { CloudSecurityPolicyTemplate, PostureInput } from '../../common/types_old'; import { CLOUDBEAT_EKS, CLOUDBEAT_VANILLA, 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 ff88d9c38707f..6182a0b47f54e 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 @@ -7,7 +7,7 @@ import { i18n } from '@kbn/i18n'; import { CSPM_POLICY_TEMPLATE, KSPM_POLICY_TEMPLATE } from '../../../common/constants'; -import { PosturePolicyTemplate } from '../../../common/types'; +import { PosturePolicyTemplate } from '../../../common/types_old'; import type { CspBenchmarksPage, CspIntegrationDocNavigationItem, @@ -27,7 +27,7 @@ const NAV_ITEMS_NAMES = { defaultMessage: 'Findings', }), BENCHMARKS: i18n.translate('xpack.csp.navigation.myBenchmarksNavItemLabel', { - defaultMessage: 'Benchmark rules', + defaultMessage: 'Benchmarks', }), RULES: i18n.translate('xpack.csp.navigation.rulesNavItemLabel', { defaultMessage: 'Rules', diff --git a/x-pack/plugins/cloud_security_posture/public/common/navigation/use_csp_integration_link.ts b/x-pack/plugins/cloud_security_posture/public/common/navigation/use_csp_integration_link.ts index fb255a9545ad4..1dac88397e8be 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/navigation/use_csp_integration_link.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/navigation/use_csp_integration_link.ts @@ -6,7 +6,7 @@ */ import { pagePathGetters, pkgKeyFromPackageInfo } from '@kbn/fleet-plugin/public'; -import type { CloudSecurityPolicyTemplate } from '../../../common/types'; +import type { CloudSecurityPolicyTemplate } from '../../../common/types_old'; import { useCisKubernetesIntegration } from '../api/use_cis_kubernetes_integration'; import { useKibana } from '../hooks/use_kibana'; diff --git a/x-pack/plugins/cloud_security_posture/public/common/navigation/use_navigate_to_cis_integration_policies.ts b/x-pack/plugins/cloud_security_posture/public/common/navigation/use_navigate_to_cis_integration_policies.ts index af1dc5e795485..85e4796743cf3 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/navigation/use_navigate_to_cis_integration_policies.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/navigation/use_navigate_to_cis_integration_policies.ts @@ -8,8 +8,8 @@ import { pagePathGetters, pkgKeyFromPackageInfo } from '@kbn/fleet-plugin/public'; import { useCisKubernetesIntegration } from '../api/use_cis_kubernetes_integration'; import { useKibana } from '../hooks/use_kibana'; -import { useCspBenchmarkIntegrations } from '../../pages/benchmarks/use_csp_benchmark_integrations'; -import { PostureTypes } from '../../../common/types'; +import { useCspBenchmarkIntegrationsV1 } from '../../pages/benchmarks/use_csp_benchmark_integrations'; +import { PostureTypes } from '../../../common/types_old'; export const useCISIntegrationPoliciesLink = ({ postureType, @@ -19,7 +19,7 @@ export const useCISIntegrationPoliciesLink = ({ const { http } = useKibana().services; const cisIntegration = useCisKubernetesIntegration(); // using an existing hook to get agent id and package policy id - const cspBenchmarkIntegrations = useCspBenchmarkIntegrations({ + const cspBenchmarkIntegrations = useCspBenchmarkIntegrationsV1({ name: '', page: 1, perPage: 100, @@ -29,6 +29,7 @@ export const useCISIntegrationPoliciesLink = ({ if (!cisIntegration.isSuccess) return; const intergrations = cspBenchmarkIntegrations.data?.items; + const matchedIntegration = intergrations?.find( (integration) => integration?.package_policy?.inputs?.find((input) => input?.enabled)?.policy_template === diff --git a/x-pack/plugins/cloud_security_posture/public/common/utils/get_enabled_csp_integration_details.ts b/x-pack/plugins/cloud_security_posture/public/common/utils/get_enabled_csp_integration_details.ts index 4cf3d7d129f0c..cd0a413648bed 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/utils/get_enabled_csp_integration_details.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/utils/get_enabled_csp_integration_details.ts @@ -6,7 +6,7 @@ */ import type { PackagePolicy } from '@kbn/fleet-plugin/common'; -import type { PostureInput } from '../../../common/types'; +import type { PostureInput } from '../../../common/types_old'; import { SUPPORTED_CLOUDBEAT_INPUTS } from '../../../common/constants'; import { cloudPostureIntegrations, type CloudPostureIntegrations } from '../constants'; diff --git a/x-pack/plugins/cloud_security_posture/public/common/utils/get_vulnerability_colors.ts b/x-pack/plugins/cloud_security_posture/public/common/utils/get_vulnerability_colors.ts index 6cb1423a77511..db5776bb82ccc 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/utils/get_vulnerability_colors.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/utils/get_vulnerability_colors.ts @@ -7,7 +7,7 @@ import { euiThemeVars } from '@kbn/ui-theme'; import { VULNERABILITIES_SEVERITY } from '../../../common/constants'; -import { VulnSeverity } from '../../../common/types'; +import { VulnSeverity } from '../../../common/types_old'; export const getCvsScoreColor = (score: number): string | undefined => { if (score <= 4) { diff --git a/x-pack/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.tsx b/x-pack/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.tsx index 4feee3a5e2287..a9b118be899ab 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/accounts_evaluated_widget.tsx @@ -11,7 +11,7 @@ import { CIS_AWS, CIS_GCP, CIS_AZURE, CIS_K8S, CIS_EKS } from '../../common/cons import { CISBenchmarkIcon } from './cis_benchmark_icon'; import { CompactFormattedNumber } from './compact_formatted_number'; import { useNavigateFindings } from '../common/hooks/use_navigate_findings'; -import { BenchmarkData } from '../../common/types'; +import { BenchmarkData } from '../../common/types_old'; // order in array will determine order of appearance in the dashboard const benchmarks = [ @@ -52,8 +52,8 @@ export const AccountsEvaluatedWidget = ({ }) => { const { euiTheme } = useEuiTheme(); - const filterBenchmarksById = (benchmarkId: string) => { - return benchmarkAssets?.filter((obj) => obj?.meta.benchmarkId === benchmarkId) || []; + const getBenchmarkById = (benchmarkId: string) => { + return benchmarkAssets?.find((obj) => obj?.meta.benchmarkId === benchmarkId); }; const navToFindings = useNavigateFindings(); @@ -67,7 +67,7 @@ export const AccountsEvaluatedWidget = ({ }; const benchmarkElements = benchmarks.map((benchmark) => { - const cloudAssetAmount = filterBenchmarksById(benchmark.type).length; + const cloudAssetAmount = getBenchmarkById(benchmark.type)?.meta?.assetCount || 0; return ( cloudAssetAmount > 0 && ( @@ -85,7 +85,7 @@ export const AccountsEvaluatedWidget = ({ transition: ${euiTheme.animation.normal} ease-in; border-bottom: ${euiTheme.border.thick}; border-color: transparent; - + text-wrap: nowrap; :hover { cursor: pointer; border-color: ${euiTheme.colors.darkestShade}; diff --git a/x-pack/plugins/cloud_security_posture/public/components/cis_benchmark_icon.tsx b/x-pack/plugins/cloud_security_posture/public/components/cis_benchmark_icon.tsx index 50f9ca1b15d9d..b0b5703937c90 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/cis_benchmark_icon.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/cis_benchmark_icon.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { EuiIcon, EuiToolTip, IconSize } from '@elastic/eui'; import { CSSInterpolation } from '@emotion/serialize'; -import type { BenchmarkId } from '../../common/types'; +import type { BenchmarkId } from '../../common/types_old'; import cisEksIcon from '../assets/icons/cis_eks_logo.svg'; import googleCloudLogo from '../assets/icons/google_cloud_logo.svg'; diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/aws_credentials_form/aws_credentials_form.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/aws_credentials_form/aws_credentials_form.tsx index 4be4ac082f60f..3f717a9f4cb22 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/aws_credentials_form/aws_credentials_form.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/aws_credentials_form/aws_credentials_form.tsx @@ -4,18 +4,16 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React from 'react'; +import React, { ReactNode } from 'react'; import { - EuiFieldText, - EuiFieldPassword, + EuiCallOut, EuiFormRow, + EuiHorizontalRule, EuiLink, + EuiSelect, EuiSpacer, EuiText, EuiTitle, - EuiSelect, - EuiCallOut, - EuiHorizontalRule, } from '@elastic/eui'; import type { NewPackagePolicy } from '@kbn/fleet-plugin/public'; import { NewPackagePolicyInput, PackageInfo } from '@kbn/fleet-plugin/common'; @@ -23,25 +21,24 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { css } from '@emotion/react'; import { i18n } from '@kbn/i18n'; import { + AwsCredentialsTypeOptions, getAwsCredentialsFormManualOptions, - AwsOptions, - DEFAULT_MANUAL_AWS_CREDENTIALS_TYPE, } from './get_aws_credentials_form_options'; -import { RadioGroup } from '../csp_boxed_radio_group'; -import { - getCspmCloudFormationDefaultValue, - getPosturePolicy, - NewPackagePolicyPostureInput, -} from '../utils'; +import { CspRadioOption, RadioGroup } from '../csp_boxed_radio_group'; +import { getPosturePolicy, NewPackagePolicyPostureInput } from '../utils'; import { SetupFormat, useAwsCredentialsForm } from './hooks'; import { AWS_ORGANIZATION_ACCOUNT } from '../policy_template_form'; -import { AwsCredentialsType } from '../../../../common/types'; +import { AwsCredentialsType } from '../../../../common/types_old'; +import { AwsInputVarFields } from './aws_input_var_fields'; +import { + AWS_CREDENTIALS_TYPE_OPTIONS_TEST_SUBJ, + AWS_CREDENTIALS_TYPE_SELECTOR_TEST_SUBJ, +} from '../../test_subjects'; interface AWSSetupInfoContentProps { - integrationLink: string; + info: ReactNode; } - -const AWSSetupInfoContent = ({ integrationLink }: AWSSetupInfoContentProps) => { +export const AWSSetupInfoContent = ({ info }: AWSSetupInfoContentProps) => { return ( <> @@ -55,48 +52,28 @@ const AWSSetupInfoContent = ({ integrationLink }: AWSSetupInfoContentProps) => { - - - - ), - }} - /> + {info} ); }; -const getSetupFormatOptions = (): Array<{ id: SetupFormat; label: string }> => [ +const getSetupFormatOptions = (): CspRadioOption[] => [ { id: 'cloud_formation', label: 'CloudFormation', + testId: AWS_CREDENTIALS_TYPE_OPTIONS_TEST_SUBJ.CLOUDFORMATION, }, { id: 'manual', label: i18n.translate('xpack.csp.awsIntegration.setupFormatOptions.manual', { defaultMessage: 'Manual', }), + testId: AWS_CREDENTIALS_TYPE_OPTIONS_TEST_SUBJ.MANUAL, }, ]; -export const getDefaultAwsVarsGroup = (packageInfo: PackageInfo): AwsCredentialsType => { - const hasCloudFormationTemplate = !!getCspmCloudFormationDefaultValue(packageInfo); - if (hasCloudFormationTemplate) { - return 'cloud_formation'; - } - - return DEFAULT_MANUAL_AWS_CREDENTIALS_TYPE; -}; - -interface Props { +export interface AwsFormProps { newPolicy: NewPackagePolicy; input: Extract; updatePolicy(updatedPolicy: NewPackagePolicy): void; @@ -217,7 +194,7 @@ export const AwsCredentialsForm = ({ onChange, setIsValid, disabled, -}: Props) => { +}: AwsFormProps) => { const { awsCredentialsType, setupFormat, @@ -237,7 +214,24 @@ export const AwsCredentialsForm = ({ return ( <> - + + + + ), + }} + /> + } + /> { updatePolicy( @@ -281,60 +279,26 @@ export const AwsCredentialsForm = ({ ); }; -const AwsCredentialTypeSelector = ({ +export const AwsCredentialTypeSelector = ({ type, onChange, + label, + options, }: { onChange(type: AwsCredentialsType): void; type: AwsCredentialsType; + label: string; + options: AwsCredentialsTypeOptions; }) => ( - + { onChange(optionElem.target.value as AwsCredentialsType); }} + data-test-subj={AWS_CREDENTIALS_TYPE_SELECTOR_TEST_SUBJ} /> ); - -const AwsInputVarFields = ({ - fields, - onChange, -}: { - fields: Array; - onChange: (key: string, value: string) => void; -}) => ( -
- {fields.map((field) => ( - - <> - {field.type === 'password' && ( - onChange(field.id, event.target.value)} - /> - )} - {field.type === 'text' && ( - onChange(field.id, event.target.value)} - /> - )} - - - ))} -
-); diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/aws_credentials_form/aws_credentials_form_agentless.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/aws_credentials_form/aws_credentials_form_agentless.tsx new file mode 100644 index 0000000000000..b0ab98db98cda --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/aws_credentials_form/aws_credentials_form_agentless.tsx @@ -0,0 +1,83 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiLink, EuiSpacer } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { i18n } from '@kbn/i18n'; +import { cspIntegrationDocsNavigation } from '../../../common/navigation/constants'; +import { + DEFAULT_AGENTLESS_AWS_CREDENTIALS_TYPE, + getAwsCredentialsFormAgentlessOptions, + getAwsCredentialsFormOptions, + getInputVarsFields, +} from './get_aws_credentials_form_options'; +import { getAwsCredentialsType, getPosturePolicy } from '../utils'; +import { AwsInputVarFields } from './aws_input_var_fields'; +import { + AwsFormProps, + ReadDocumentation, + AWSSetupInfoContent, + AwsCredentialTypeSelector, +} from './aws_credentials_form'; + +export const AwsCredentialsFormAgentless = ({ input, newPolicy, updatePolicy }: AwsFormProps) => { + const awsCredentialsType = getAwsCredentialsType(input) || DEFAULT_AGENTLESS_AWS_CREDENTIALS_TYPE; + const options = getAwsCredentialsFormOptions(); + const group = options[awsCredentialsType]; + const fields = getInputVarsFields(input, group.fields); + const integrationLink = cspIntegrationDocsNavigation.cspm.getStartedPath; + + return ( + <> + + + + ), + }} + /> + } + /> + + { + updatePolicy( + getPosturePolicy(newPolicy, input.type, { + 'aws.credentials.type': { value: optionId }, + }) + ); + }} + /> + + {group.info} + + + + { + updatePolicy(getPosturePolicy(newPolicy, input.type, { [key]: { value } })); + }} + /> + + ); +}; diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/aws_credentials_form/aws_input_var_fields.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/aws_credentials_form/aws_input_var_fields.tsx new file mode 100644 index 0000000000000..5ac54872a583b --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/aws_credentials_form/aws_input_var_fields.tsx @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { EuiFieldPassword, EuiFieldText, EuiFormRow } from '@elastic/eui'; +import { AwsOptions } from './get_aws_credentials_form_options'; + +export const AwsInputVarFields = ({ + fields, + onChange, +}: { + fields: Array; + onChange: (key: string, value: string) => void; +}) => ( +
+ {fields.map((field) => ( + + <> + {field.type === 'password' && ( + onChange(field.id, event.target.value)} + /> + )} + {field.type === 'text' && ( + onChange(field.id, event.target.value)} + /> + )} + + + ))} +
+); diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/aws_credentials_form/get_aws_credentials_form_options.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/aws_credentials_form/get_aws_credentials_form_options.tsx index 71882d4cc67cf..f9d6fe06e5a8b 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/aws_credentials_form/get_aws_credentials_form_options.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/aws_credentials_form/get_aws_credentials_form_options.tsx @@ -10,7 +10,7 @@ import { EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { NewPackagePolicyInput } from '@kbn/fleet-plugin/common'; -import { AwsCredentialsType } from '../../../../common/types'; +import { AwsCredentialsType } from '../../../../common/types_old'; const AssumeRoleDescription = (
@@ -92,20 +92,33 @@ export const getInputVarsFields = (input: NewPackagePolicyInput, fields: AwsCred }); export type AwsOptions = Record; - -export const getAwsCredentialsFormManualOptions = (): Array<{ +export type AwsCredentialsTypeOptions = Array<{ value: AwsCredentialsType; text: string; -}> => { +}>; + +const getAwsCredentialsTypeSelectorOptions = ( + filterFn: ({ value }: { value: AwsCredentialsType }) => boolean +): AwsCredentialsTypeOptions => { return Object.entries(getAwsCredentialsFormOptions()) .map(([key, value]) => ({ value: key as AwsCredentialsType, text: value.label, })) - .filter(({ value }) => value !== 'cloud_formation'); + .filter(filterFn); }; +export const getAwsCredentialsFormManualOptions = (): AwsCredentialsTypeOptions => + getAwsCredentialsTypeSelectorOptions(({ value }) => value !== 'cloud_formation'); + +export const getAwsCredentialsFormAgentlessOptions = (): AwsCredentialsTypeOptions => + getAwsCredentialsTypeSelectorOptions( + ({ value }) => value === 'direct_access_keys' || value === 'temporary_keys' + ); + +export const DEFAULT_AWS_CREDENTIALS_TYPE = 'cloud_formation'; export const DEFAULT_MANUAL_AWS_CREDENTIALS_TYPE = 'assume_role'; +export const DEFAULT_AGENTLESS_AWS_CREDENTIALS_TYPE = 'direct_access_keys'; export const getAwsCredentialsFormOptions = (): AwsOptions => ({ assume_role: { diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/aws_credentials_form/hooks.ts b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/aws_credentials_form/hooks.ts index 190ae47a61aec..3735a585268e1 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/aws_credentials_form/hooks.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/aws_credentials_form/hooks.ts @@ -12,6 +12,7 @@ import { getCspmCloudFormationDefaultValue, getPosturePolicy, NewPackagePolicyPostureInput, + getAwsCredentialsType, } from '../utils'; import { DEFAULT_MANUAL_AWS_CREDENTIALS_TYPE, @@ -19,7 +20,7 @@ import { getInputVarsFields, } from './get_aws_credentials_form_options'; import { CLOUDBEAT_AWS } from '../../../../common/constants'; -import { AwsCredentialsType } from '../../../../common/types'; +import { AwsCredentialsType } from '../../../../common/types_old'; /** * Update CloudFormation template and stack name in the Agent Policy * based on the selected policy template @@ -43,10 +44,6 @@ const getSetupFormatFromInput = ( return 'cloud_formation'; }; -const getAwsCredentialsType = ( - input: Extract -): AwsCredentialsType | undefined => input.streams[0].vars?.['aws.credentials.type'].value; - export const useAwsCredentialsForm = ({ newPolicy, input, diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/azure_credentials_form/azure_credentials_form.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/azure_credentials_form/azure_credentials_form.tsx index ab8770b20f0cf..06de63c3cff0c 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/azure_credentials_form/azure_credentials_form.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/azure_credentials_form/azure_credentials_form.tsx @@ -29,7 +29,7 @@ import { AzureOptions, getAzureCredentialsFormManualOptions, } from './get_azure_credentials_form_options'; -import { AzureCredentialsType } from '../../../../common/types'; +import { AzureCredentialsType } from '../../../../common/types_old'; import { SetupFormat, useAzureCredentialsForm } from './hooks'; import { getPosturePolicy, NewPackagePolicyPostureInput } from '../utils'; import { CspRadioOption, RadioGroup } from '../csp_boxed_radio_group'; @@ -248,18 +248,6 @@ const TemporaryManualSetup = ({ integrationLink }: { integrationLink: string }) const AZURE_MINIMUM_PACKAGE_VERSION = '1.6.0'; const AZURE_MANUAL_FIELDS_PACKAGE_VERSION = '1.7.0'; -export const getDefaultAzureManualCredentialType = (packageInfo: PackageInfo) => { - const packageSemanticVersion = semverValid(packageInfo.version); - const cleanPackageVersion = semverCoerce(packageSemanticVersion) || ''; - - const isPackageVersionValidForManualFields = !semverLt( - cleanPackageVersion, - AZURE_MANUAL_FIELDS_PACKAGE_VERSION - ); - - return isPackageVersionValidForManualFields ? 'managed_identity' : 'manual'; -}; - const AzureInputVarFields = ({ fields, onChange, diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/azure_credentials_form/get_azure_credentials_form_options.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/azure_credentials_form/get_azure_credentials_form_options.tsx index 455fe9352ba00..d6044118f1afd 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/azure_credentials_form/get_azure_credentials_form_options.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/azure_credentials_form/get_azure_credentials_form_options.tsx @@ -10,7 +10,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiText } from '@elastic/eui'; -import { AzureCredentialsType } from '../../../../common/types'; +import { AzureCredentialsType } from '../../../../common/types_old'; export type AzureCredentialsFields = Record; @@ -26,12 +26,15 @@ export const getAzureCredentialsFormManualOptions = (): Array<{ value: AzureCredentialsType; text: string; }> => { - return Object.entries(getAzureCredentialsFormOptions()) - .map(([key, value]) => ({ - value: key as AzureCredentialsType, - text: value.label, - })) - .filter(({ value }) => value !== 'arm_template'); + return ( + Object.entries(getAzureCredentialsFormOptions()) + .map(([key, value]) => ({ + value: key as AzureCredentialsType, + text: value.label, + })) + // TODO: remove 'manual' for stack version 8.13 + .filter(({ value }) => value !== 'arm_template' && value !== 'manual') + ); }; export const getInputVarsFields = (input: NewPackagePolicyInput, fields: AzureCredentialsFields) => @@ -75,6 +78,12 @@ export const getAzureCredentialsFormOptions = (): AzureOptions => ({ info: [], fields: {}, }, + // TODO: remove for stack version 8.13 + manual: { + label: 'Manual', + info: [], + fields: {}, + }, service_principal_with_client_secret: { label: i18n.translate('xpack.csp.azureIntegration.servicePrincipalWithClientSecretLabel', { defaultMessage: 'Service principal with Client Secret', @@ -131,18 +140,4 @@ export const getAzureCredentialsFormOptions = (): AzureOptions => ({ }, }, }, - manual: { - label: i18n.translate('xpack.csp.azureIntegration.credentialType.manualLabel', { - defaultMessage: 'Manual', - }), - info: ( - - - - ), - fields: {}, - }, }); diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/azure_credentials_form/hooks.ts b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/azure_credentials_form/hooks.ts index b68828be58117..2eb56fc6ace83 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/azure_credentials_form/hooks.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/azure_credentials_form/hooks.ts @@ -7,10 +7,7 @@ import { useEffect, useRef } from 'react'; import { NewPackagePolicy, PackageInfo } from '@kbn/fleet-plugin/common'; -import { - AZURE_ARM_TEMPLATE_CREDENTIAL_TYPE, - getDefaultAzureManualCredentialType, -} from './azure_credentials_form'; +import { AZURE_ARM_TEMPLATE_CREDENTIAL_TYPE } from './azure_credentials_form'; import { cspIntegrationDocsNavigation } from '../../../common/navigation/constants'; import { getArmTemplateUrlFromCspmPackage, @@ -22,7 +19,7 @@ import { getInputVarsFields, } from './get_azure_credentials_form_options'; import { CLOUDBEAT_AZURE } from '../../../../common/constants'; -import { AzureCredentialsType } from '../../../../common/types'; +import { AzureCredentialsType } from '../../../../common/types_old'; export type SetupFormat = 'arm_template' | 'manual'; @@ -151,7 +148,7 @@ export const useAzureCredentialsForm = ({ setupFormat, }); - const defaultAzureManualCredentialType = getDefaultAzureManualCredentialType(packageInfo); + const defaultAzureManualCredentialType = 'managed_identity'; const onSetupFormatChange = (newSetupFormat: SetupFormat) => { if (newSetupFormat === AZURE_ARM_TEMPLATE_CREDENTIAL_TYPE) { diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/custom_assets_extension.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/custom_assets_extension.tsx index 774acadbd03ba..c7708cba837cc 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/custom_assets_extension.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/custom_assets_extension.tsx @@ -10,7 +10,7 @@ import { type CustomAssetsAccordionProps, CustomAssetsAccordion } from '@kbn/fle import { i18n } from '@kbn/i18n'; import { useParams } from 'react-router-dom'; import { EuiSpacer } from '@elastic/eui'; -import { CloudSecurityPolicyTemplate } from '../../../common/types'; +import { CloudSecurityPolicyTemplate } from '../../../common/types_old'; import { VULN_MGMT_POLICY_TEMPLATE } from '../../../common/constants'; import { useKibana } from '../../common/hooks/use_kibana'; import { benchmarksNavigation, cloudPosturePages } from '../../common/navigation/constants'; diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/eks_credentials_form.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/eks_credentials_form.tsx index 41f4b857e13f8..fda6ae2f631fa 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/eks_credentials_form.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/eks_credentials_form.tsx @@ -5,22 +5,14 @@ * 2.0. */ import React from 'react'; -import { - EuiFieldText, - EuiFieldPassword, - EuiFormRow, - EuiLink, - EuiSpacer, - EuiText, - EuiTitle, - EuiHorizontalRule, -} from '@elastic/eui'; +import { EuiLink, EuiSpacer, EuiText, EuiTitle, EuiHorizontalRule } from '@elastic/eui'; import type { NewPackagePolicy } from '@kbn/fleet-plugin/public'; import { NewPackagePolicyInput } from '@kbn/fleet-plugin/common'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; import { RadioGroup } from './csp_boxed_radio_group'; import { getPosturePolicy, NewPackagePolicyPostureInput } from './utils'; +import { AwsInputVarFields } from './aws_credentials_form/aws_input_var_fields'; const AWSSetupInfoContent = () => ( <> @@ -279,37 +271,3 @@ const AwsCredentialTypeSelector = ({ onChange={(id) => onChange(id as AwsCredentialsType)} /> ); - -const AwsInputVarFields = ({ - fields, - onChange, -}: { - fields: Array; - onChange: (key: string, value: string) => void; -}) => ( -
- {fields.map((field) => ( - - <> - {field.type === 'password' && ( - onChange(field.id, event.target.value)} - /> - )} - {field.type === 'text' && ( - onChange(field.id, event.target.value)} - /> - )} - - - ))} -
-); diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/gcp_credential_form.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/gcp_credential_form.tsx index 4f0adecb60e5b..4039d458548bb 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/gcp_credential_form.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/gcp_credential_form.tsx @@ -25,13 +25,13 @@ import type { NewPackagePolicy } from '@kbn/fleet-plugin/public'; import { NewPackagePolicyInput, PackageInfo } from '@kbn/fleet-plugin/common'; import { FormattedMessage } from '@kbn/i18n-react'; import { i18n } from '@kbn/i18n'; -import { GcpCredentialsType } from '../../../common/types'; +import { GcpCredentialsType } from '../../../common/types_old'; import { CLOUDBEAT_GCP, SETUP_ACCESS_CLOUD_SHELL, SETUP_ACCESS_MANUAL, } from '../../../common/constants'; -import { RadioGroup } from './csp_boxed_radio_group'; +import { CspRadioOption, RadioGroup } from './csp_boxed_radio_group'; import { getCspmCloudShellDefaultValue, getPosturePolicy, @@ -231,12 +231,7 @@ export const gcpField: GcpInputFields = { }, }; -const getSetupFormatOptions = (): Array<{ - id: SetupFormatGCP; - label: string; - disabled: boolean; - testId: string; -}> => [ +const getSetupFormatOptions = (): CspRadioOption[] => [ { id: SETUP_ACCESS_CLOUD_SHELL, label: i18n.translate('xpack.csp.gcpIntegration.setupFormatOptions.googleCloudShell', { diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/mocks.ts b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/mocks.ts index 4c1b04a260df1..f893545024d78 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/mocks.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/mocks.ts @@ -6,7 +6,7 @@ */ import type { NewPackagePolicy } from '@kbn/fleet-plugin/public'; import type { PackageInfo } from '@kbn/fleet-plugin/common'; -import { createNewPackagePolicyMock } from '@kbn/fleet-plugin/common/mocks'; +import { createNewPackagePolicyMock, createAgentPolicyMock } from '@kbn/fleet-plugin/common/mocks'; import { CLOUDBEAT_GCP, CLOUDBEAT_AZURE, @@ -15,7 +15,7 @@ import { CLOUDBEAT_AWS, CLOUDBEAT_VULN_MGMT_AWS, } from '../../../common/constants'; -import type { PostureInput } from '../../../common/types'; +import type { PostureInput } from '../../../common/types_old'; export const getMockPolicyAWS = () => getPolicyMock(CLOUDBEAT_AWS, 'cspm', 'aws'); export const getMockPolicyGCP = () => getPolicyMock(CLOUDBEAT_GCP, 'cspm', 'gcp'); @@ -24,6 +24,9 @@ export const getMockPolicyK8s = () => getPolicyMock(CLOUDBEAT_VANILLA, 'kspm', ' export const getMockPolicyEKS = () => getPolicyMock(CLOUDBEAT_EKS, 'kspm', 'eks'); export const getMockPolicyVulnMgmtAWS = () => getPolicyMock(CLOUDBEAT_VULN_MGMT_AWS, 'vuln_mgmt', 'aws'); +export const getMockAgentlessAgentPolicy = () => { + return createAgentPolicyMock({ id: 'agentless' }); +}; export const getMockPackageInfoVulnMgmtAWS = () => { return { diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx index d0c1f4d454b4f..3b549dcc9d9ed 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.test.tsx @@ -5,7 +5,8 @@ * 2.0. */ import React from 'react'; -import { render } from '@testing-library/react'; +import { render, waitFor, within } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import { CspPolicyTemplateForm, AWS_ORGANIZATION_ACCOUNT, @@ -15,6 +16,7 @@ import { } from './policy_template_form'; import { TestProvider } from '../../test/test_provider'; import { + getMockAgentlessAgentPolicy, getMockPackageInfoCspmAWS, getMockPackageInfoCspmAzure, getMockPackageInfoCspmGCP, @@ -32,7 +34,6 @@ import type { PackageInfo, PackagePolicy, } from '@kbn/fleet-plugin/common'; -import userEvent from '@testing-library/user-event'; import { getPosturePolicy } from './utils'; import { CLOUDBEAT_AWS, @@ -45,6 +46,13 @@ import { createReactQueryResponse } from '../../test/fixtures/react_query'; import { useCspSetupStatusApi } from '../../common/api/use_setup_status_api'; import { usePackagePolicyList } from '../../common/api/use_package_policy_list'; import { CIS_GCP_INPUT_FIELDS_TEST_SUBJECTS } from './gcp_credential_form'; +import { waitForEuiPopoverOpen } from '@elastic/eui/lib/test/rtl'; +import { + AWS_CREDENTIALS_TYPE_OPTIONS_TEST_SUBJ, + AWS_CREDENTIALS_TYPE_SELECTOR_TEST_SUBJ, + SETUP_TECHNOLOGY_SELECTOR_ACCORDION_TEST_SUBJ, + SETUP_TECHNOLOGY_SELECTOR_TEST_SUBJ, +} from '../test_subjects'; // mock useParams jest.mock('react-router-dom', () => ({ @@ -94,12 +102,14 @@ describe('', () => { edit = false, agentPolicy, packageInfo = {} as PackageInfo, + agentlessPolicy, }: { edit?: boolean; newPolicy: NewPackagePolicy; agentPolicy?: AgentPolicy; packageInfo?: PackageInfo; onChange?: jest.Mock; + agentlessPolicy?: AgentPolicy; }) => ( {edit && ( @@ -110,6 +120,7 @@ describe('', () => { packageInfo={packageInfo} isEditPage={true} agentPolicy={agentPolicy} + agentlessPolicy={agentlessPolicy} /> )} {!edit && ( @@ -119,6 +130,7 @@ describe('', () => { packageInfo={packageInfo} isEditPage={false} agentPolicy={agentPolicy} + agentlessPolicy={agentlessPolicy} /> )} @@ -141,7 +153,7 @@ describe('', () => { }); }); - it('renders and updates name field', () => { + it('renders and updates name field', async () => { const policy = getMockPolicyK8s(); const { getByLabelText } = render(); const name = getByLabelText('Name'); @@ -149,15 +161,15 @@ describe('', () => { userEvent.type(name, '1'); - // Listen to the 2nd triggered by the test. - // The 1st is done on mount to ensure initial state is valid. - expect(onChange).toHaveBeenNthCalledWith(2, { - isValid: true, - updatedPolicy: { ...policy, name: `${policy.name}1` }, + await waitFor(() => { + expect(onChange).toHaveBeenCalledWith({ + isValid: true, + updatedPolicy: { ...policy, name: `${policy.name}1` }, + }); }); }); - it('renders and updates description field', () => { + it('renders and updates description field', async () => { const policy = getMockPolicyK8s(); const { getByLabelText } = render(); const description = getByLabelText('Description'); @@ -165,11 +177,11 @@ describe('', () => { userEvent.type(description, '1'); - // Listen to the 2nd triggered by the test. - // The 1st is done on mount to ensure initial state is valid. - expect(onChange).toHaveBeenNthCalledWith(2, { - isValid: true, - updatedPolicy: { ...policy, description: `${policy.description}1` }, + await waitFor(() => { + expect(onChange).toHaveBeenCalledWith({ + isValid: true, + updatedPolicy: { ...policy, description: `${policy.description}1` }, + }); }); }); @@ -186,7 +198,7 @@ describe('', () => { expect(option1).toBeChecked(); }); - it('updates selected KSPM input', () => { + it('updates selected KSPM input', async () => { const k8sPolicy = getMockPolicyK8s(); const eksPolicy = getMockPolicyEKS(); @@ -194,11 +206,11 @@ describe('', () => { const option = getByLabelText('EKS'); userEvent.click(option); - // Listen to the 2nd triggered by the test. - // The 1st is done on mount to ensure initial state is valid. - expect(onChange).toHaveBeenNthCalledWith(2, { - isValid: true, - updatedPolicy: eksPolicy, + await waitFor(() => { + expect(onChange).toHaveBeenCalledWith({ + isValid: true, + updatedPolicy: eksPolicy, + }); }); }); @@ -1330,6 +1342,7 @@ describe('', () => { ).toBeInTheDocument(); }); + // TODO: remove for stack version 8.13 it(`doesnt render ${CLOUDBEAT_AZURE} Manual fields when version is not at least version 1.7.0`, () => { let policy = getMockPolicyAzure(); policy = getPosturePolicy(policy, CLOUDBEAT_AZURE, { @@ -1435,6 +1448,119 @@ describe('', () => { }); }); + describe('Agentless', () => { + it('should render setup technology selector for AWS and allow to select agent-based', async () => { + const agentlessPolicy = getMockAgentlessAgentPolicy(); + const newPackagePolicy = getMockPolicyAWS(); + + const { getByTestId, getByRole } = render( + + ); + + const setupTechnologySelectorAccordion = getByTestId( + SETUP_TECHNOLOGY_SELECTOR_ACCORDION_TEST_SUBJ + ); + const setupTechnologySelector = getByTestId(SETUP_TECHNOLOGY_SELECTOR_TEST_SUBJ); + const awsCredentialsTypeSelector = getByTestId(AWS_CREDENTIALS_TYPE_SELECTOR_TEST_SUBJ); + const options: HTMLOptionElement[] = within(awsCredentialsTypeSelector).getAllByRole( + 'option' + ); + const optionValues = options.map((option) => option.value); + + // default state + expect(setupTechnologySelectorAccordion).toBeInTheDocument(); + expect(setupTechnologySelector).toBeInTheDocument(); + expect(setupTechnologySelector).toHaveTextContent(/agentless/i); + expect(options).toHaveLength(2); + expect(optionValues).toEqual( + expect.arrayContaining(['direct_access_keys', 'temporary_keys']) + ); + + // select agent-based and check for cloudformation option + userEvent.click(setupTechnologySelector); + const agentBasedOption = getByRole('option', { name: /agent-based/i }); + await waitForEuiPopoverOpen(); + userEvent.click(agentBasedOption); + await waitFor(() => { + expect( + getByTestId(AWS_CREDENTIALS_TYPE_OPTIONS_TEST_SUBJ.CLOUDFORMATION) + ).toBeInTheDocument(); + expect(getByTestId(AWS_CREDENTIALS_TYPE_OPTIONS_TEST_SUBJ.MANUAL)).toBeInTheDocument(); + }); + }); + + it('should not render setup technology selector for KSPM', () => { + const agentlessPolicy = getMockAgentlessAgentPolicy(); + const newPackagePolicy = getMockPolicyEKS(); + + const { queryByTestId } = render( + + ); + + const setupTechnologySelectorAccordion = queryByTestId( + SETUP_TECHNOLOGY_SELECTOR_ACCORDION_TEST_SUBJ + ); + + expect(setupTechnologySelectorAccordion).not.toBeInTheDocument(); + }); + + it('should not render setup technology selector for CNVM', () => { + const agentlessPolicy = getMockAgentlessAgentPolicy(); + const newPackagePolicy = getMockPolicyVulnMgmtAWS(); + + const { queryByTestId } = render( + + ); + + const setupTechnologySelectorAccordion = queryByTestId( + SETUP_TECHNOLOGY_SELECTOR_ACCORDION_TEST_SUBJ + ); + + expect(setupTechnologySelectorAccordion).not.toBeInTheDocument(); + }); + + it('should not render setup technology selector for CSPM GCP', () => { + const agentlessPolicy = getMockAgentlessAgentPolicy(); + const newPackagePolicy = getMockPolicyGCP(); + + const { queryByTestId } = render( + + ); + + const setupTechnologySelectorAccordion = queryByTestId( + SETUP_TECHNOLOGY_SELECTOR_ACCORDION_TEST_SUBJ + ); + + expect(setupTechnologySelectorAccordion).not.toBeInTheDocument(); + }); + + it('should not render setup technology selector for CSPM Azure', () => { + const agentlessPolicy = getMockAgentlessAgentPolicy(); + let newPackagePolicy = getMockPolicyAzure(); + newPackagePolicy = getPosturePolicy(newPackagePolicy, CLOUDBEAT_AZURE, { + 'azure.credentials.type': { value: 'service_principal_with_client_certificate' }, + }); + + const { queryByTestId } = render( + + ); + + const setupTechnologySelectorAccordion = queryByTestId( + SETUP_TECHNOLOGY_SELECTOR_ACCORDION_TEST_SUBJ + ); + + expect(setupTechnologySelectorAccordion).not.toBeInTheDocument(); + }); + }); + it(`renders Service principal with Client Certificate fields`, () => { let policy = getMockPolicyAzure(); policy = getPosturePolicy(policy, CLOUDBEAT_AZURE, { diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx index 163857b27e0f9..303df35545db8 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_form.tsx @@ -21,6 +21,7 @@ import { EuiTitle, } from '@elastic/eui'; import type { NewPackagePolicy } from '@kbn/fleet-plugin/public'; +import { SetupTechnology } from '@kbn/fleet-plugin/public'; import { FormattedMessage } from '@kbn/i18n-react'; import type { NewPackagePolicyInput, @@ -32,7 +33,7 @@ import { i18n } from '@kbn/i18n'; import { AZURE_ARM_TEMPLATE_CREDENTIAL_TYPE } from './azure_credentials_form/azure_credentials_form'; import { CspRadioGroupProps, RadioGroup } from './csp_boxed_radio_group'; import { assert } from '../../../common/utils/helpers'; -import type { PostureInput, CloudSecurityPolicyTemplate } from '../../../common/types'; +import type { CloudSecurityPolicyTemplate, PostureInput } from '../../../common/types_old'; import { CLOUDBEAT_AWS, CLOUDBEAT_VANILLA, @@ -41,14 +42,14 @@ import { SUPPORTED_POLICY_TEMPLATES, } from '../../../common/constants'; import { - getPosturePolicy, + getMaxPackageName, getPostureInputHiddenVars, + getPosturePolicy, getVulnMgmtCloudFormationDefaultValue, - POSTURE_NAMESPACE, - type NewPackagePolicyPostureInput, isPostureInput, - getMaxPackageName, isBelowMinVersion, + type NewPackagePolicyPostureInput, + POSTURE_NAMESPACE, } from './utils'; import { PolicyTemplateInfo, @@ -58,6 +59,8 @@ import { } from './policy_template_selectors'; import { usePackagePolicyList } from '../../common/api/use_package_policy_list'; import { gcpField, getInputVarsFields } from './gcp_credential_form'; +import { SetupTechnologySelector } from './setup_technology_selector/setup_technology_selector'; +import { useSetupTechnology } from './setup_technology_selector/use_setup_technology'; const DEFAULT_INPUT_TYPE = { kspm: CLOUDBEAT_VANILLA, @@ -440,7 +443,7 @@ const AzureAccountTypeSelect = ({ updatePolicy( getPosturePolicy(newPolicy, input.type, { 'azure.account_type': { - value: AZURE_SINGLE_ACCOUNT, + value: isAzureOrganizationDisabled ? AZURE_SINGLE_ACCOUNT : AZURE_ORGANIZATION_ACCOUNT, type: 'text', }, 'azure.credentials.type': { @@ -520,7 +523,16 @@ const IntegrationSettings = ({ onChange, fields }: IntegrationInfoFieldsProps) = ); export const CspPolicyTemplateForm = memo( - ({ newPolicy, onChange, validationResults, isEditPage, packageInfo }) => { + ({ + agentPolicy, + newPolicy, + onChange, + validationResults, + isEditPage, + packageInfo, + handleSetupTechnologyChange, + agentlessPolicy, + }) => { const integrationParam = useParams<{ integration: CloudSecurityPolicyTemplate }>().integration; const integration = SUPPORTED_POLICY_TEMPLATES.includes(integrationParam) ? integrationParam @@ -528,6 +540,16 @@ export const CspPolicyTemplateForm = memo { @@ -541,11 +563,11 @@ export const CspPolicyTemplateForm = memo { - const inputVars = getPostureInputHiddenVars(inputType, packageInfo); + const inputVars = getPostureInputHiddenVars(inputType, packageInfo, setupTechnology); const policy = getPosturePolicy(newPolicy, inputType, inputVars); updatePolicy(policy); }, - [newPolicy, updatePolicy, packageInfo] + [setupTechnology, packageInfo, newPolicy, updatePolicy] ); // search for non null fields of the validation?.vars object @@ -584,6 +606,15 @@ export const CspPolicyTemplateForm = memo { + if (isEditPage) { + return; + } + + setEnabledPolicyInput(input.type); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [setupTechnology]); + useEnsureDefaultNamespace({ newPolicy, input, updatePolicy }); useCloudFormationTemplate({ @@ -722,6 +753,14 @@ export const CspPolicyTemplateForm = memo updatePolicy({ ...newPolicy, [field]: value })} /> + {shouldRenderAgentlessSelector && ( + + )} + {/* Defines the vars of the enabled input of the active policy template */} diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_selectors.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_selectors.tsx index ef21868dd404d..7a045e7f82c0c 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_selectors.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/policy_template_selectors.tsx @@ -8,6 +8,7 @@ import React from 'react'; import { EuiCallOut, EuiSpacer, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import type { NewPackagePolicy, PackageInfo } from '@kbn/fleet-plugin/common'; +import { SetupTechnology } from '@kbn/fleet-plugin/public'; import { PackagePolicyReplaceDefineStepExtensionComponentProps } from '@kbn/fleet-plugin/public/types'; import { CSPM_POLICY_TEMPLATE, @@ -15,11 +16,12 @@ import { VULN_MGMT_POLICY_TEMPLATE, CNVM_POLICY_TEMPLATE, } from '../../../common/constants'; -import type { PostureInput, CloudSecurityPolicyTemplate } from '../../../common/types'; +import type { PostureInput, CloudSecurityPolicyTemplate } from '../../../common/types_old'; import { getPolicyTemplateInputOptions, type NewPackagePolicyPostureInput } from './utils'; import { RadioGroup } from './csp_boxed_radio_group'; import { AzureCredentialsForm } from './azure_credentials_form/azure_credentials_form'; import { AwsCredentialsForm } from './aws_credentials_form/aws_credentials_form'; +import { AwsCredentialsFormAgentless } from './aws_credentials_form/aws_credentials_form_agentless'; import { EksCredentialsForm } from './eks_credentials_form'; import { GcpCredentialsForm } from './gcp_credential_form'; @@ -74,11 +76,20 @@ interface PolicyTemplateVarsFormProps { onChange: PackagePolicyReplaceDefineStepExtensionComponentProps['onChange']; setIsValid: (isValid: boolean) => void; disabled: boolean; + setupTechnology: SetupTechnology; } -export const PolicyTemplateVarsForm = ({ input, ...props }: PolicyTemplateVarsFormProps) => { +export const PolicyTemplateVarsForm = ({ + input, + setupTechnology, + ...props +}: PolicyTemplateVarsFormProps) => { switch (input.type) { case 'cloudbeat/cis_aws': + if (setupTechnology === SetupTechnology.AGENTLESS) { + return ; + } + return ; case 'cloudbeat/cis_eks': return ; diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/setup_technology_selector/setup_technology_selector.tsx b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/setup_technology_selector/setup_technology_selector.tsx new file mode 100644 index 0000000000000..03ca7ab21150e --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/setup_technology_selector/setup_technology_selector.tsx @@ -0,0 +1,139 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { SetupTechnology } from '@kbn/fleet-plugin/public'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { + EuiAccordion, + EuiFormRow, + EuiLink, + EuiSpacer, + EuiSuperSelect, + EuiText, + useGeneratedHtmlId, +} from '@elastic/eui'; +import { + SETUP_TECHNOLOGY_SELECTOR_ACCORDION_TEST_SUBJ, + SETUP_TECHNOLOGY_SELECTOR_TEST_SUBJ, +} from '../../test_subjects'; + +export const SetupTechnologySelector = ({ + disabled, + setupTechnology, + onSetupTechnologyChange, +}: { + disabled: boolean; + setupTechnology: SetupTechnology; + onSetupTechnologyChange: (value: SetupTechnology) => void; +}) => { + const options = [ + { + value: SetupTechnology.AGENTLESS, + inputDisplay: ( + + ), + dropdownDisplay: ( + <> + + + + +

+ +

+
+ + ), + }, + { + value: SetupTechnology.AGENT_BASED, + inputDisplay: ( + + ), + dropdownDisplay: ( + <> + + + + +

+ +

+
+ + ), + }, + ]; + + return ( + <> + + + + + } + data-test-subj={SETUP_TECHNOLOGY_SELECTOR_ACCORDION_TEST_SUBJ} + > + + + } + > + + } + onChange={onSetupTechnologyChange} + itemLayoutAlign="top" + hasDividers + fullWidth + data-test-subj={SETUP_TECHNOLOGY_SELECTOR_TEST_SUBJ} + /> + + + + ); +}; diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/setup_technology_selector/use_setup_technology.test.ts b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/setup_technology_selector/use_setup_technology.test.ts new file mode 100644 index 0000000000000..744a87a009f98 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/setup_technology_selector/use_setup_technology.test.ts @@ -0,0 +1,135 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook, act } from '@testing-library/react-hooks'; + +import { SetupTechnology } from '@kbn/fleet-plugin/public'; +import { AgentPolicy, NewPackagePolicyInput } from '@kbn/fleet-plugin/common'; + +import { CLOUDBEAT_AWS } from '../../../../common/constants'; +import { useSetupTechnology } from './use_setup_technology'; + +describe('useSetupTechnology', () => { + describe('create page flow', () => { + const isEditPage = false; + + it('initializes with AGENT_BASED technology', () => { + const { result } = renderHook(() => + useSetupTechnology({ + input: { type: 'cloudbeat/no-agentless-support' } as NewPackagePolicyInput, + isEditPage, + }) + ); + expect(result.current.setupTechnology).toBe(SetupTechnology.AGENT_BASED); + }); + + it('sets to AGENTLESS when agentless is available', () => { + const agentlessPolicy = { id: 'agentlessPolicyId' } as AgentPolicy; + const input = { type: CLOUDBEAT_AWS } as NewPackagePolicyInput; + const { result } = renderHook(() => + useSetupTechnology({ input, agentlessPolicy, isEditPage }) + ); + expect(result.current.isAgentlessAvailable).toBeTruthy(); + expect(result.current.setupTechnology).toBe(SetupTechnology.AGENTLESS); + }); + + it('sets to AGENT_BASED when agentPolicyId differs from agentlessPolicyId', () => { + const input = { type: CLOUDBEAT_AWS } as NewPackagePolicyInput; + const agentPolicy = { id: 'agentPolicyId' } as AgentPolicy; + const agentlessPolicy = { id: 'agentlessPolicyId' } as AgentPolicy; + const { result } = renderHook(() => + useSetupTechnology({ input, agentPolicy, agentlessPolicy, isEditPage }) + ); + expect(result.current.setupTechnology).toBe(SetupTechnology.AGENT_BASED); + }); + + it('calls handleSetupTechnologyChange when setupTechnology changes', () => { + const handleSetupTechnologyChangeMock = jest.fn(); + const { result } = renderHook(() => + useSetupTechnology({ + input: { type: 'someType' } as NewPackagePolicyInput, + handleSetupTechnologyChange: handleSetupTechnologyChangeMock, + isEditPage, + }) + ); + + act(() => { + result.current.setSetupTechnology(SetupTechnology.AGENTLESS); + }); + + expect(handleSetupTechnologyChangeMock).toHaveBeenCalledWith(SetupTechnology.AGENTLESS); + }); + }); + + describe('edit page flow', () => { + const isEditPage = true; + + it('initializes with AGENT_BASED technology', () => { + const { result } = renderHook(() => + useSetupTechnology({ + input: { type: 'cloudbeat/no-agentless-support' } as NewPackagePolicyInput, + isEditPage, + }) + ); + expect(result.current.setupTechnology).toBe(SetupTechnology.AGENT_BASED); + }); + + it('initializes with AGENTLESS technology if the agent policy id is "agentless"', () => { + const input = { type: CLOUDBEAT_AWS } as NewPackagePolicyInput; + const agentPolicy = { id: 'agentless' } as AgentPolicy; + const { result } = renderHook(() => + useSetupTechnology({ + input, + agentPolicy, + isEditPage, + }) + ); + expect(result.current.setupTechnology).toBe(SetupTechnology.AGENTLESS); + }); + + it('should not call handleSetupTechnologyChange when setupTechnology changes', () => { + const handleSetupTechnologyChangeMock = jest.fn(); + const input = { type: CLOUDBEAT_AWS } as NewPackagePolicyInput; + const { result } = renderHook(() => + useSetupTechnology({ + input, + handleSetupTechnologyChange: handleSetupTechnologyChangeMock, + isEditPage, + }) + ); + + act(() => { + result.current.setSetupTechnology(SetupTechnology.AGENTLESS); + }); + + expect(handleSetupTechnologyChangeMock).not.toHaveBeenCalled(); + }); + + it('should not update setupTechnology when agentlessPolicyId becomes available', () => { + const input = { type: CLOUDBEAT_AWS } as NewPackagePolicyInput; + const agentlessPolicy = { id: 'agentlessPolicyId' } as AgentPolicy; + const { result, rerender } = renderHook(() => + useSetupTechnology({ + input, + isEditPage, + }) + ); + + expect(result.current.setupTechnology).toBe(SetupTechnology.AGENT_BASED); + + act(() => { + rerender({ + input, + agentlessPolicy, + isEditPage, + }); + }); + + expect(result.current.setupTechnology).toBe(SetupTechnology.AGENT_BASED); + }); + }); +}); diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/setup_technology_selector/use_setup_technology.ts b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/setup_technology_selector/use_setup_technology.ts new file mode 100644 index 0000000000000..4201ffde1b2a8 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/setup_technology_selector/use_setup_technology.ts @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { useEffect, useState } from 'react'; + +import { AgentPolicy, NewPackagePolicyInput } from '@kbn/fleet-plugin/common'; +import { SetupTechnology } from '@kbn/fleet-plugin/public'; +import { CLOUDBEAT_AWS } from '../../../../common/constants'; + +export const useSetupTechnology = ({ + input, + agentPolicy, + agentlessPolicy, + handleSetupTechnologyChange, + isEditPage, +}: { + input: NewPackagePolicyInput; + agentPolicy?: AgentPolicy; + agentlessPolicy?: AgentPolicy; + handleSetupTechnologyChange?: (value: SetupTechnology) => void; + isEditPage: boolean; +}) => { + const isCspmAws = input.type === CLOUDBEAT_AWS; + const isAgentlessAvailable = Boolean(isCspmAws && agentlessPolicy); + const agentPolicyId = agentPolicy?.id; + const agentlessPolicyId = agentlessPolicy?.id; + const [setupTechnology, setSetupTechnology] = useState(() => { + if (isEditPage && agentPolicyId === SetupTechnology.AGENTLESS) { + return SetupTechnology.AGENTLESS; + } + + return SetupTechnology.AGENT_BASED; + }); + + useEffect(() => { + if (isEditPage) { + return; + } + + if (agentPolicyId && agentPolicyId !== agentlessPolicyId) { + /* + handle case when agent policy is coming from outside, + e.g. from the get param or when coming to integration from a specific agent policy + */ + setSetupTechnology(SetupTechnology.AGENT_BASED); + } else if (isAgentlessAvailable) { + /* + preselecting agentless when available + and resetting to agent-based when switching to another integration type, which doesn't support agentless + */ + setSetupTechnology(SetupTechnology.AGENTLESS); + } else { + setSetupTechnology(SetupTechnology.AGENT_BASED); + } + }, [agentPolicyId, agentlessPolicyId, isAgentlessAvailable, isEditPage]); + + useEffect(() => { + if (isEditPage) { + return; + } + + if (handleSetupTechnologyChange) { + handleSetupTechnologyChange(setupTechnology); + } + }, [handleSetupTechnologyChange, isEditPage, setupTechnology]); + + return { + isAgentlessAvailable, + setupTechnology, + setSetupTechnology, + }; +}; diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.test.ts b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.test.ts index 266cd98ab0450..b05c7fe096516 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.test.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.test.ts @@ -4,6 +4,8 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import type { PackageInfo } from '@kbn/fleet-plugin/common'; +import { SetupTechnology } from '@kbn/fleet-plugin/public'; import { getMaxPackageName, @@ -11,9 +13,9 @@ import { getPosturePolicy, getCspmCloudShellDefaultValue, isBelowMinVersion, + getDefaultAwsCredentialsType, } from './utils'; import { getMockPolicyAWS, getMockPolicyK8s, getMockPolicyEKS } from './mocks'; -import type { PackageInfo } from '@kbn/fleet-plugin/common'; describe('getPosturePolicy', () => { for (const [name, getPolicy, expectedVars] of [ @@ -22,7 +24,11 @@ describe('getPosturePolicy', () => { ['cloudbeat/cis_k8s', getMockPolicyK8s, null], ] as const) { it(`updates package policy with hidden vars for ${name}`, () => { - const inputVars = getPostureInputHiddenVars(name, {} as any); + const inputVars = getPostureInputHiddenVars( + name, + {} as PackageInfo, + SetupTechnology.AGENT_BASED + ); const policy = getPosturePolicy(getPolicy(), name, inputVars); const enabledInputs = policy.inputs.filter( @@ -280,3 +286,67 @@ describe('isBelowMinVersion', () => { } }); }); + +describe('getDefaultAwsCredentialsType', () => { + let packageInfo: PackageInfo; + + beforeEach(() => { + packageInfo = { + policy_templates: [ + { + name: 'cspm', + inputs: [ + { + vars: [ + { + name: 'cloud_formation_template', + default: 'http://example.com/cloud_formation_template', + }, + ], + }, + ], + }, + ], + } as PackageInfo; + }); + + it('should return "direct_access_key" for agentless', () => { + const setupTechnology = SetupTechnology.AGENTLESS; + const result = getDefaultAwsCredentialsType(packageInfo, setupTechnology); + + expect(result).toBe('direct_access_keys'); + }); + + it('should return "assume_role" for agent-based, when cloudformation is not available', () => { + const setupTechnology = SetupTechnology.AGENT_BASED; + packageInfo = { + policy_templates: [ + { + name: 'cspm', + inputs: [ + { + vars: [ + { + name: 'cloud_shell', + default: 'http://example.com/cloud_shell', + }, + ], + }, + ], + }, + ], + } as PackageInfo; + + const result = getDefaultAwsCredentialsType({} as PackageInfo, setupTechnology); + + expect(result).toBe('assume_role'); + }); + + it('should return "cloud_formation" for agent-based, when cloudformation is available', () => { + const setupTechnology = SetupTechnology.AGENT_BASED; + + const result = getDefaultAwsCredentialsType(packageInfo, setupTechnology); + + expect(result).toBe('cloud_formation'); + }); +}); diff --git a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts index ae7c02ade79d1..e3b8055497e22 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/fleet_extensions/utils.ts @@ -12,27 +12,36 @@ import type { RegistryPolicyTemplate, RegistryVarsEntry, } from '@kbn/fleet-plugin/common'; +import { SetupTechnology } from '@kbn/fleet-plugin/public'; import merge from 'lodash/merge'; import semverValid from 'semver/functions/valid'; import semverCoerce from 'semver/functions/coerce'; import semverLt from 'semver/functions/lt'; import { CLOUDBEAT_AWS, + CLOUDBEAT_AZURE, CLOUDBEAT_EKS, - CLOUDBEAT_VANILLA, CLOUDBEAT_GCP, - CLOUDBEAT_AZURE, + CLOUDBEAT_VANILLA, CLOUDBEAT_VULN_MGMT_AWS, - SUPPORTED_POLICY_TEMPLATES, - SUPPORTED_CLOUDBEAT_INPUTS, CSPM_POLICY_TEMPLATE, KSPM_POLICY_TEMPLATE, + SUPPORTED_CLOUDBEAT_INPUTS, + SUPPORTED_POLICY_TEMPLATES, VULN_MGMT_POLICY_TEMPLATE, } from '../../../common/constants'; -import { getDefaultAwsVarsGroup } from './aws_credentials_form/aws_credentials_form'; -import type { PostureInput, CloudSecurityPolicyTemplate } from '../../../common/types'; +import type { + AwsCredentialsType, + PostureInput, + CloudSecurityPolicyTemplate, +} from '../../../common/types_old'; import { cloudPostureIntegrations } from '../../common/constants'; import { DEFAULT_EKS_VARS_GROUP } from './eks_credentials_form'; +import { + DEFAULT_AGENTLESS_AWS_CREDENTIALS_TYPE, + DEFAULT_AWS_CREDENTIALS_TYPE, + DEFAULT_MANUAL_AWS_CREDENTIALS_TYPE, +} from './aws_credentials_form/get_aws_credentials_form_options'; // Posture policies only support the default namespace export const POSTURE_NAMESPACE = 'default'; @@ -203,14 +212,36 @@ export const getArmTemplateUrlFromCspmPackage = (packageInfo: PackageInfo): stri return armTemplateUrl; }; +export const getDefaultAwsCredentialsType = ( + packageInfo: PackageInfo, + setupTechnology?: SetupTechnology +): AwsCredentialsType => { + if (setupTechnology && setupTechnology === SetupTechnology.AGENTLESS) { + return DEFAULT_AGENTLESS_AWS_CREDENTIALS_TYPE; + } + + const hasCloudFormationTemplate = !!getCspmCloudFormationDefaultValue(packageInfo); + if (hasCloudFormationTemplate) { + return DEFAULT_AWS_CREDENTIALS_TYPE; + } + + return DEFAULT_MANUAL_AWS_CREDENTIALS_TYPE; +}; /** * Input vars that are hidden from the user */ -export const getPostureInputHiddenVars = (inputType: PostureInput, packageInfo: PackageInfo) => { +export const getPostureInputHiddenVars = ( + inputType: PostureInput, + packageInfo: PackageInfo, + setupTechnology: SetupTechnology +) => { switch (inputType) { case 'cloudbeat/cis_aws': return { - 'aws.credentials.type': { value: getDefaultAwsVarsGroup(packageInfo), type: 'text' }, + 'aws.credentials.type': { + value: getDefaultAwsCredentialsType(packageInfo, setupTechnology), + type: 'text', + }, }; case 'cloudbeat/cis_eks': return { 'aws.credentials.type': { value: DEFAULT_EKS_VARS_GROUP, type: 'text' } }; @@ -267,6 +298,10 @@ export const getCspmCloudShellDefaultValue = (packageInfo: PackageInfo): string return cloudShellUrl; }; +export const getAwsCredentialsType = ( + input: Extract +): AwsCredentialsType | undefined => input.streams[0].vars?.['aws.credentials.type'].value; + export const isBelowMinVersion = (version: string, minVersion: string) => { const semanticVersion = semverValid(version); const versionNumberOnly = semverCoerce(semanticVersion) || ''; diff --git a/x-pack/plugins/cloud_security_posture/public/components/no_findings_states.tsx b/x-pack/plugins/cloud_security_posture/public/components/no_findings_states.tsx index 422695c7ff576..f070b1e0ad345 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/no_findings_states.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/no_findings_states.tsx @@ -30,7 +30,7 @@ import { } from './test_subjects'; import { CloudPosturePage, PACKAGE_NOT_INSTALLED_TEST_SUBJECT } from './cloud_posture_page'; import { useCspSetupStatusApi } from '../common/api/use_setup_status_api'; -import type { IndexDetails, PostureTypes } from '../../common/types'; +import type { IndexDetails, PostureTypes } from '../../common/types_old'; import { cspIntegrationDocsNavigation } from '../common/navigation/constants'; import noDataIllustration from '../assets/illustrations/no_data_illustration.svg'; import { useCspIntegrationLink } from '../common/navigation/use_csp_integration_link'; diff --git a/x-pack/plugins/cloud_security_posture/public/components/no_vulnerabilities_states.tsx b/x-pack/plugins/cloud_security_posture/public/components/no_vulnerabilities_states.tsx index 7dee3e94e70a6..14872bcc19210 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/no_vulnerabilities_states.tsx +++ b/x-pack/plugins/cloud_security_posture/public/components/no_vulnerabilities_states.tsx @@ -25,7 +25,7 @@ import { VULN_MGMT_POLICY_TEMPLATE } from '../../common/constants'; import { FullSizeCenteredPage } from './full_size_centered_page'; import { CloudPosturePage } from './cloud_posture_page'; import { useCspSetupStatusApi } from '../common/api/use_setup_status_api'; -import type { IndexDetails } from '../../common/types'; +import type { IndexDetails } from '../../common/types_old'; import { NO_VULNERABILITIES_STATUS_TEST_SUBJ, CNVM_NOT_INSTALLED_ACTION_SUBJ, @@ -33,7 +33,7 @@ import { import noDataIllustration from '../assets/illustrations/no_data_illustration.svg'; import { useCspIntegrationLink } from '../common/navigation/use_csp_integration_link'; import { useCISIntegrationPoliciesLink } from '../common/navigation/use_navigate_to_cis_integration_policies'; -import { PostureTypes } from '../../common/types'; +import { PostureTypes } from '../../common/types_old'; const REFETCH_INTERVAL_MS = 20000; diff --git a/x-pack/plugins/cloud_security_posture/public/components/test_subjects.ts b/x-pack/plugins/cloud_security_posture/public/components/test_subjects.ts index 1f603a67ae1fc..c51d562fec44a 100644 --- a/x-pack/plugins/cloud_security_posture/public/components/test_subjects.ts +++ b/x-pack/plugins/cloud_security_posture/public/components/test_subjects.ts @@ -43,3 +43,12 @@ export const CREATE_RULE_ACTION_SUBJ = 'csp:create_rule'; export const CSP_GROUPING = 'cloudSecurityGrouping'; export const CSP_GROUPING_LOADING = 'cloudSecurityGroupingLoading'; export const CSP_FINDINGS_COMPLIANCE_SCORE = 'cloudSecurityFindingsComplianceScore'; + +export const AWS_CREDENTIALS_TYPE_SELECTOR_TEST_SUBJ = 'aws-credentials-type-selector'; +export const AWS_CREDENTIALS_TYPE_OPTIONS_TEST_SUBJ = { + CLOUDFORMATION: 'aws-cloudformation-setup-option', + MANUAL: 'aws-manual-setup-option', +}; + +export const SETUP_TECHNOLOGY_SELECTOR_ACCORDION_TEST_SUBJ = 'setup-technology-selector-accordion'; +export const SETUP_TECHNOLOGY_SELECTOR_TEST_SUBJ = 'setup-technology-selector'; 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 b94efbd2de1a0..20b1326d65526 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 @@ -10,7 +10,7 @@ import React from 'react'; import { css } from '@emotion/react'; import { float } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { getCvsScoreColor, getSeverityStatusColor } from '../common/utils/get_vulnerability_colors'; -import { VulnSeverity } from '../../common/types'; +import { VulnSeverity } from '../../common/types_old'; import { VULNERABILITIES_CVSS_SCORE_BADGE_SUBJ } from './test_subjects'; interface CVSScoreBadgeProps { diff --git a/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks.test.tsx index a1ed381e6df77..e5fed03fc9676 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks.test.tsx @@ -13,7 +13,7 @@ import { createReactQueryResponse } from '../../test/fixtures/react_query'; import { TestProvider } from '../../test/test_provider'; import { Benchmarks } from './benchmarks'; import * as TEST_SUBJ from './test_subjects'; -import { useCspBenchmarkIntegrations } from './use_csp_benchmark_integrations'; +import { useCspBenchmarkIntegrationsV2 } from './use_csp_benchmark_integrations'; import { useCspSetupStatusApi } from '../../common/api/use_setup_status_api'; import { useSubscriptionStatus } from '../../common/hooks/use_subscription_status'; import { useCspIntegrationLink } from '../../common/navigation/use_csp_integration_link'; @@ -65,7 +65,7 @@ describe('', () => { const renderBenchmarks = ( queryResponse: Partial = createReactQueryResponse() ) => { - (useCspBenchmarkIntegrations as jest.Mock).mockImplementation(() => queryResponse); + (useCspBenchmarkIntegrationsV2 as jest.Mock).mockImplementation(() => queryResponse); return render( diff --git a/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks.tsx b/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks.tsx index b39bf81a5d31a..2f848868c54d3 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks.tsx @@ -26,10 +26,10 @@ import { CloudPosturePageTitle } from '../../components/cloud_posture_page_title import { CloudPosturePage } from '../../components/cloud_posture_page'; import { BenchmarksTable } from './benchmarks_table'; import { - useCspBenchmarkIntegrations, + useCspBenchmarkIntegrationsV2, UseCspBenchmarkIntegrationsProps, } from './use_csp_benchmark_integrations'; -import { extractErrorMessage } from '../../../common/utils/helpers'; +import { extractErrorMessage, getBenchmarkCisName } from '../../../common/utils/helpers'; import * as TEST_SUBJ from './test_subjects'; import { LOCAL_STORAGE_PAGE_SIZE_BENCHMARK_KEY } from '../../common/constants'; import { usePageSize } from '../../common/hooks/use_page_size'; @@ -102,7 +102,7 @@ const TotalIntegrationsCount = ({ @@ -126,7 +126,7 @@ const BenchmarkSearchField = ({ isLoading={isLoading} placeholder={i18n.translate( 'xpack.csp.benchmarks.benchmarkSearchField.searchPlaceholder', - { defaultMessage: 'Search by Integration Name' } + { defaultMessage: 'Search by benchmark Name' } )} incremental /> @@ -145,8 +145,11 @@ export const Benchmarks = () => { sortOrder: 'asc', }); - const queryResult = useCspBenchmarkIntegrations(query); - const totalItemCount = queryResult.data?.total || 0; + const queryResult = useCspBenchmarkIntegrationsV2(); + const benchmarkResult = + queryResult.data?.items.filter((obj) => getBenchmarkCisName(obj.id)?.includes(query.name)) || + []; + const totalItemCount = queryResult.data?.items.length || 0; return ( @@ -154,8 +157,8 @@ export const Benchmarks = () => { data-test-subj={TEST_SUBJ.BENCHMARKS_PAGE_HEADER} pageTitle={ } @@ -174,7 +177,7 @@ export const Benchmarks = () => { /> { })); }} noItemsMessage={ - queryResult.isSuccess && !queryResult.data.total ? ( - - ) : undefined + queryResult.isSuccess ? : undefined } /> diff --git a/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks_table.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks_table.test.tsx index dc980df0d67ae..9463c65bc63eb 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks_table.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks_table.test.tsx @@ -5,16 +5,13 @@ * 2.0. */ import React from 'react'; -import Chance from 'chance'; import { render, screen } from '@testing-library/react'; -import moment from 'moment'; import { createCspBenchmarkIntegrationFixture } from '../../test/fixtures/csp_benchmark_integration'; import { BenchmarksTable } from './benchmarks_table'; import { TestProvider } from '../../test/test_provider'; +import { getBenchmarkCisName, getBenchmarkApplicableTo } from '../../../common/utils/helpers'; describe('', () => { - const chance = new Chance(); - const tableProps = { pageIndex: 1, pageSize: 10, @@ -23,9 +20,10 @@ describe('', () => { setQuery: jest.fn(), }; - it('renders integration name', () => { + it('renders cis integration name', () => { const item = createCspBenchmarkIntegrationFixture(); const benchmarks = [item]; + const benchmarkCisIntegrationName = getBenchmarkCisName(item.id) || ''; render( @@ -39,17 +37,12 @@ describe('', () => { ); - expect(screen.getByText(item.package_policy.name)).toBeInTheDocument(); + expect(screen.getByText(benchmarkCisIntegrationName)).toBeInTheDocument(); }); - it('renders agent policy name', () => { - const agentPolicy = { - id: chance.guid(), - name: chance.sentence(), - agents: chance.integer({ min: 1 }), - }; - - const benchmarks = [createCspBenchmarkIntegrationFixture({ agent_policy: agentPolicy })]; + it('renders benchmark version', () => { + const item = createCspBenchmarkIntegrationFixture(); + const benchmarks = [item]; render( @@ -63,13 +56,13 @@ describe('', () => { ); - expect(screen.getByText(agentPolicy.name)).toBeInTheDocument(); + expect(screen.getByText(item.version)).toBeInTheDocument(); }); - it('renders number of agents', () => { + it('renders applicable to', () => { const item = createCspBenchmarkIntegrationFixture(); const benchmarks = [item]; - + const benchmarkApplicableTo = getBenchmarkApplicableTo(item.id) || ''; render( ', () => { ); - // TODO too loose - expect(screen.getByText(item.agent_policy.agents as number)).toBeInTheDocument(); + expect(screen.getByText(benchmarkApplicableTo)).toBeInTheDocument(); }); - it('renders created by', () => { + it('renders evaluated', () => { const item = createCspBenchmarkIntegrationFixture(); const benchmarks = [item]; @@ -101,11 +93,10 @@ describe('', () => { /> ); - - expect(screen.getByText(item.package_policy.created_by)).toBeInTheDocument(); + expect(screen.getByText(benchmarks[0].evaluation + ' accounts')).toBeInTheDocument(); }); - it('renders created at', () => { + it('renders compliance', () => { const item = createCspBenchmarkIntegrationFixture(); const benchmarks = [item]; @@ -121,6 +112,6 @@ describe('', () => { ); - expect(screen.getByText(moment(item.package_policy.created_at).fromNow())).toBeInTheDocument(); + expect(screen.getByText(item.score.postureScore + '%')).toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks_table.tsx b/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks_table.tsx index e37a9d6fcbe9c..be04bcd48bce3 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks_table.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/benchmarks_table.tsx @@ -11,25 +11,20 @@ import { type EuiBasicTableProps, type Pagination, type CriteriaWithPagination, - EuiLink, - EuiToolTip, - EuiAvatar, EuiEmptyPrompt, + EuiFlexGroup, + EuiFlexItem, } from '@elastic/eui'; import React from 'react'; -import { generatePath } from 'react-router-dom'; -import { pagePathGetters } from '@kbn/fleet-plugin/public'; import { i18n } from '@kbn/i18n'; -import type { PackagePolicy } from '@kbn/fleet-plugin/common'; import { FormattedMessage } from '@kbn/i18n-react'; -import { TimestampTableCell } from '../../components/timestamp_table_cell'; -import type { Benchmark } from '../../../common/types'; -import { useKibana } from '../../common/hooks/use_kibana'; -import { benchmarksNavigation } from '../../common/navigation/constants'; +import type { BenchmarkScore, Benchmark, BenchmarksCisId } from '../../../common/types/latest'; import * as TEST_SUBJ from './test_subjects'; -import { getEnabledCspIntegrationDetails } from '../../common/utils/get_enabled_csp_integration_details'; import { isCommonError } from '../../components/cloud_posture_page'; import { FullSizeCenteredPage } from '../../components/full_size_centered_page'; +import { ComplianceScoreBar } from '../../components/compliance_score_bar'; +import { getBenchmarkCisName, getBenchmarkApplicableTo } from '../../../common/utils/helpers'; +import { CISBenchmarkIcon } from '../../components/cis_benchmark_icon'; export const ERROR_STATE_TEST_SUBJECT = 'benchmark_page_error'; @@ -41,36 +36,49 @@ interface BenchmarksTableProps 'data-test-subj'?: string; } -const AgentPolicyButtonLink = ({ name, id: policyId }: { name: string; id: string }) => { - const { http } = useKibana().services; - const [fleetBase, path] = pagePathGetters.policy_details({ policyId }); - - return {name}; -}; - -const IntegrationButtonLink = ({ - packageName, - policyId, - packagePolicyId, -}: { - packageName: string; - packagePolicyId: string; - policyId: string; -}) => { - const { application } = useKibana().services; - - return ( - - {packageName} - - ); +export const getBenchmarkPlurals = (benchmarkId: string, accountEvaluation: number) => { + switch (benchmarkId) { + case 'cis_k8s': + return ( + + ); + case 'cis_azure': + return ( + + ); + case 'cis_aws': + return ( + + ); + case 'cis_eks': + return ( + + ); + case 'cis_gcp': + return ( + + ); + } }; const ErrorMessageComponent = (error: { error: unknown }) => ( @@ -108,106 +116,84 @@ const ErrorMessageComponent = (error: { error: unknown }) => ( const BENCHMARKS_TABLE_COLUMNS: Array> = [ { - field: 'package_policy.name', - name: i18n.translate('xpack.csp.benchmarks.benchmarksTable.integrationNameColumnTitle', { - defaultMessage: 'Integration Name', + field: 'id', + name: i18n.translate('xpack.csp.benchmarks.benchmarksTable.integrationBenchmarkCisName', { + defaultMessage: 'Benchmark', }), - render: (packageName, benchmark) => ( - - ), truncateText: true, + width: '17.5%', sortable: true, - 'data-test-subj': TEST_SUBJ.BENCHMARKS_TABLE_COLUMNS.INTEGRATION_NAME, + render: (benchmarkId: BenchmarksCisId) => { + return getBenchmarkCisName(benchmarkId); + }, + 'data-test-subj': TEST_SUBJ.BENCHMARKS_TABLE_COLUMNS.CIS_NAME, }, { - field: 'rules_count', - name: i18n.translate('xpack.csp.benchmarks.benchmarksTable.rulesColumnTitle', { - defaultMessage: 'Rules', + field: 'version', + name: i18n.translate('xpack.csp.benchmarks.benchmarksTable.integrationBenchmarkVersion', { + defaultMessage: 'Version', }), truncateText: true, - 'data-test-subj': TEST_SUBJ.BENCHMARKS_TABLE_COLUMNS.RULES, + sortable: true, + width: '17.5%', + 'data-test-subj': TEST_SUBJ.BENCHMARKS_TABLE_COLUMNS.VERSION, }, { - field: 'package_policy', - name: i18n.translate('xpack.csp.benchmarks.benchmarksTable.integrationColumnTitle', { - defaultMessage: 'Integration', + field: 'id', + name: i18n.translate('xpack.csp.benchmarks.benchmarksTable.applicableTo', { + defaultMessage: 'Applicable To', }), - dataType: 'string', truncateText: true, - 'data-test-subj': TEST_SUBJ.BENCHMARKS_TABLE_COLUMNS.INTEGRATION, - render: (field: PackagePolicy) => { - const enabledIntegration = getEnabledCspIntegrationDetails(field); - return enabledIntegration?.integration?.shortName || ' '; + width: '30%', + 'data-test-subj': TEST_SUBJ.BENCHMARKS_TABLE_COLUMNS.APPLICABLE_TO, + render: (benchmarkId: BenchmarksCisId) => { + return ( + <> + + + + + {getBenchmarkApplicableTo(benchmarkId)} + + + ); }, }, { - field: 'package_policy', - name: i18n.translate('xpack.csp.benchmarks.benchmarksTable.monitoringColumnTitle', { - defaultMessage: 'Monitoring', + field: 'evaluation', + name: i18n.translate('xpack.csp.benchmarks.benchmarksTable.evaluated', { + defaultMessage: 'Evaluated', }), dataType: 'string', truncateText: true, - 'data-test-subj': TEST_SUBJ.BENCHMARKS_TABLE_COLUMNS.MONITORING, - render: (field: PackagePolicy) => { - const enabledIntegration = getEnabledCspIntegrationDetails(field); - return enabledIntegration?.enabledIntegrationOption?.name || ' '; + width: '17.5%', + 'data-test-subj': TEST_SUBJ.BENCHMARKS_TABLE_COLUMNS.EVALUATED, + render: (complianceScore: Benchmark['evaluation'], data) => { + return getBenchmarkPlurals(data.id, data.evaluation); }, }, { - field: 'agent_policy.name', - name: i18n.translate('xpack.csp.benchmarks.benchmarksTable.agentPolicyColumnTitle', { - defaultMessage: 'Agent Policy', - }), - render: (name, benchmark) => ( - - ), - truncateText: true, - 'data-test-subj': TEST_SUBJ.BENCHMARKS_TABLE_COLUMNS.AGENT_POLICY, - }, - { - field: 'agent_policy.agents', - name: i18n.translate('xpack.csp.benchmarks.benchmarksTable.numberOfAgentsColumnTitle', { - defaultMessage: 'Number of Agents', - }), - truncateText: true, - 'data-test-subj': TEST_SUBJ.BENCHMARKS_TABLE_COLUMNS.NUMBER_OF_AGENTS, - }, - { - field: 'package_policy.created_by', - name: i18n.translate('xpack.csp.benchmarks.benchmarksTable.createdByColumnTitle', { - defaultMessage: 'Created by', + field: 'score', + name: i18n.translate('xpack.csp.benchmarks.benchmarksTable.score', { + defaultMessage: 'Compliance', }), dataType: 'string', truncateText: true, - sortable: true, - 'data-test-subj': TEST_SUBJ.BENCHMARKS_TABLE_COLUMNS.CREATED_BY, - render: (createdBy: Benchmark['package_policy']['created_by']) => { + width: '7.5%', + 'data-test-subj': TEST_SUBJ.BENCHMARKS_TABLE_COLUMNS.COMPLIANCE, + render: (data: BenchmarkScore) => { + if (data.totalFindings > 0) + return ( + + ); return ( - - - {createdBy} - - + ); }, }, - { - field: 'package_policy.created_at', - name: i18n.translate('xpack.csp.benchmarks.benchmarksTable.createdAtColumnTitle', { - defaultMessage: 'Created', - }), - dataType: 'date', - truncateText: true, - render: (timestamp: Benchmark['package_policy']['created_at']) => ( - - ), - sortable: true, - 'data-test-subj': TEST_SUBJ.BENCHMARKS_TABLE_COLUMNS.CREATED_AT, - }, ]; export const BenchmarksTable = ({ @@ -228,8 +214,8 @@ export const BenchmarksTable = ({ totalItemCount, }; - const onChange = ({ page, sort }: CriteriaWithPagination) => { - setQuery({ page: { ...page, index: page.index + 1 }, sort }); + const onChange = ({ page }: CriteriaWithPagination) => { + setQuery({ page: { ...page, index: page.index + 1 } }); }; if (error) { @@ -241,14 +227,15 @@ export const BenchmarksTable = ({ data-test-subj={rest['data-test-subj']} items={benchmarks} columns={BENCHMARKS_TABLE_COLUMNS} - itemId={(item) => [item.agent_policy.id, item.package_policy.id].join('/')} + itemId={(item) => [item.id, item.version].join('/')} pagination={pagination} onChange={onChange} tableLayout="fixed" loading={loading} noItemsMessage={noItemsMessage} error={error} - sorting={sorting} + /* Disabled Sorting until we have the final Benchmark table */ + // sorting={sorting} /> ); }; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/test_subjects.ts b/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/test_subjects.ts index 3e75715abf32f..fb8c5e3733d88 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/test_subjects.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/test_subjects.ts @@ -9,12 +9,9 @@ export const BENCHMARKS_PAGE_HEADER = 'benchmarks-page-header'; export const BENCHMARKS_TABLE_DATA_TEST_SUBJ = 'csp_benchmarks_table'; export const ADD_INTEGRATION_TEST_SUBJ = 'csp_add_integration'; export const BENCHMARKS_TABLE_COLUMNS = { - INTEGRATION_NAME: 'benchmarks-table-column-integration-name', - MONITORING: 'benchmarks-table-column-monitoring', - RULES: 'benchmarks-table-column-rules', - INTEGRATION: 'benchmarks-table-column-integration', - AGENT_POLICY: 'benchmarks-table-column-agent-policy', - NUMBER_OF_AGENTS: 'benchmarks-table-column-number-of-agents', - CREATED_BY: 'benchmarks-table-column-created-by', - CREATED_AT: 'benchmarks-table-column-created-at', + CIS_NAME: 'benchmark-table-column-cis-name', + VERSION: 'benchmark-table-column-version', + APPLICABLE_TO: 'benchmark-table-column-applicable-to', + EVALUATED: 'benchmark-table-column-evaluated', + COMPLIANCE: 'benchmark-table-column-compliance', }; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/use_csp_benchmark_integrations.ts b/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/use_csp_benchmark_integrations.ts index 95d955c69047e..1683c3e63d2bd 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/use_csp_benchmark_integrations.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/benchmarks/use_csp_benchmark_integrations.ts @@ -6,13 +6,14 @@ */ import { useQuery } from '@tanstack/react-query'; -import type { ListResult } from '@kbn/fleet-plugin/common'; -import { BENCHMARKS_API_CURRENT_VERSION, BENCHMARKS_ROUTE_PATH } from '../../../common/constants'; -import type { BenchmarksQueryParams } from '../../../common/schemas/benchmark'; +import { BENCHMARKS_ROUTE_PATH } from '../../../common/constants'; +import type { BenchmarksQueryParams } from '../../../common/types/benchmarks/v1'; import { useKibana } from '../../common/hooks/use_kibana'; -import type { Benchmark } from '../../../common/types'; +import type { GetBenchmarkResponse } from '../../../common/types/latest'; +import type { GetBenchmarkResponse as GetBenchmarkResponseV1 } from '../../../common/types/benchmarks/v1'; -const QUERY_KEY = 'csp_benchmark_integrations'; +const QUERY_KEY_V1 = 'csp_benchmark_integrations_v1'; +const QUERY_KEY_V2 = 'csp_benchmark_integrations_v2'; export interface UseCspBenchmarkIntegrationsProps { name: string; @@ -22,7 +23,7 @@ export interface UseCspBenchmarkIntegrationsProps { sortOrder: BenchmarksQueryParams['sort_order']; } -export const useCspBenchmarkIntegrations = ({ +export const useCspBenchmarkIntegrationsV1 = ({ name, perPage, page, @@ -39,11 +40,24 @@ export const useCspBenchmarkIntegrations = ({ }; return useQuery( - [QUERY_KEY, query], + [QUERY_KEY_V1, query], () => - http.get>(BENCHMARKS_ROUTE_PATH, { + http.get(BENCHMARKS_ROUTE_PATH, { query, - version: BENCHMARKS_API_CURRENT_VERSION, + version: '1', + }), + { keepPreviousData: true } + ); +}; + +export const useCspBenchmarkIntegrationsV2 = () => { + const { http } = useKibana().services; + + return useQuery( + [QUERY_KEY_V2], + () => + http.get(BENCHMARKS_ROUTE_PATH, { + version: '2', }), { keepPreviousData: true } ); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/compliance_score_chart.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/compliance_score_chart.tsx index 2067a9c98fd23..d986d5a97c2f6 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/compliance_score_chart.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/compliance_score_chart.tsx @@ -35,7 +35,7 @@ import { DASHBOARD_COMPLIANCE_SCORE_CHART } from '../test_subjects'; import { statusColors } from '../../../common/constants'; import { RULE_FAILED, RULE_PASSED } from '../../../../common/constants'; import { CompactFormattedNumber } from '../../../components/compact_formatted_number'; -import type { Evaluation, PostureTrend, Stats } from '../../../../common/types'; +import type { Evaluation, PostureTrend, Stats } from '../../../../common/types_old'; import { useKibana } from '../../../common/hooks/use_kibana'; interface ComplianceScoreChartProps { @@ -132,7 +132,7 @@ const CompactPercentageLabels = ({ ); -const NonCompactPercentageLabels = ({ +const PercentageLabels = ({ onEvalCounterClick, stats, }: { @@ -209,7 +209,6 @@ const ComplianceTrendChart = ({ trend }: { trend: PostureTrend[] }) => { )} /> ) : ( - diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/risks_table.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/risks_table.tsx index ac7048533ead5..d8fce2e0bfaa0 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/risks_table.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_charts/risks_table.tsx @@ -17,7 +17,7 @@ import { import { i18n } from '@kbn/i18n'; import { css } from '@emotion/react'; import { ComplianceScoreBar } from '../../../components/compliance_score_bar'; -import { ComplianceDashboardData, GroupedFindingsEvaluation } from '../../../../common/types'; +import { ComplianceDashboardData, GroupedFindingsEvaluation } from '../../../../common/types_old'; export interface RisksTableProps { data: ComplianceDashboardData['groupedFindingsEvaluation']; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.test.tsx index 18e5118f772e5..adf85dd5f84db 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.test.tsx @@ -36,7 +36,7 @@ import { BaseCspSetupStatus, ComplianceDashboardDataV2, CspStatusCode, -} from '../../../common/types'; +} from '../../../common/types_old'; jest.mock('../../common/api/use_setup_status_api'); jest.mock('../../common/api/use_stats_api'); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx index 9e3b8df0c1d3e..61a56ecd3187c 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/compliance_dashboard.tsx @@ -17,7 +17,7 @@ import type { PosturePolicyTemplate, ComplianceDashboardDataV2, BaseCspSetupStatus, -} from '../../../common/types'; +} from '../../../common/types_old'; import { CloudPosturePageTitle } from '../../components/cloud_posture_page_title'; import { CloudPosturePage, diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmark_details_box.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmark_details_box.tsx index de982b50f48f6..dc140fef121b3 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmark_details_box.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmark_details_box.tsx @@ -18,7 +18,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import React from 'react'; import { i18n } from '@kbn/i18n'; import { getBenchmarkIdQuery } from './benchmarks_section'; -import { BenchmarkData } from '../../../../common/types'; +import { BenchmarkData } from '../../../../common/types_old'; import { useNavigateFindings } from '../../../common/hooks/use_navigate_findings'; import { CISBenchmarkIcon } from '../../../components/cis_benchmark_icon'; import cisLogoIcon from '../../../assets/icons/cis_logo.svg'; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.tsx b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.tsx index 2ac91288475de..c9c7ca2dad07c 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/compliance_dashboard/dashboard_sections/benchmarks_section.tsx @@ -17,7 +17,7 @@ import type { ComplianceDashboardDataV2, Evaluation, PosturePolicyTemplate, -} from '../../../../common/types'; +} from '../../../../common/types_old'; import { RisksTable } from '../compliance_charts/risks_table'; import { RULE_FAILED } from '../../../../common/constants'; import { LOCAL_STORAGE_DASHBOARD_BENCHMARK_SORT_KEY } from '../../../common/constants'; @@ -150,7 +150,7 @@ export const BenchmarksSection = ({ {benchmarks.map((benchmark) => ( ({ ...mockDashboardData, diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/findings_flyout/findings_flyout.tsx b/x-pack/plugins/cloud_security_posture/public/pages/configurations/findings_flyout/findings_flyout.tsx index 2542c57aab44f..edc8e7f35bb35 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/configurations/findings_flyout/findings_flyout.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/findings_flyout/findings_flyout.tsx @@ -35,9 +35,9 @@ import { TableTab } from './table_tab'; import { JsonTab } from './json_tab'; import { OverviewTab } from './overview_tab'; import { RuleTab } from './rule_tab'; -import type { BenchmarkId } from '../../../../common/types'; +import type { BenchmarkId } from '../../../../common/types_old'; import { CISBenchmarkIcon } from '../../../components/cis_benchmark_icon'; -import { BenchmarkName } from '../../../../common/types'; +import { BenchmarkName } from '../../../../common/types_old'; import { FINDINGS_FLYOUT } from '../test_subjects'; import { createDetectionRuleFromFinding } from '../utils/create_detection_rule_from_finding'; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings_grouping.tsx b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings_grouping.tsx index be5253cc710b7..9d092de673edf 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings_grouping.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings_grouping.tsx @@ -15,7 +15,7 @@ import { } from '@kbn/securitysolution-grouping/src'; import { useMemo } from 'react'; import { DataView } from '@kbn/data-views-plugin/common'; -import { Evaluation } from '../../../../common/types'; +import { Evaluation } from '../../../../common/types_old'; import { LATEST_FINDINGS_RETENTION_POLICY } from '../../../../common/constants'; import { FindingsGroupingAggregation, diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings_table.tsx b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings_table.tsx index 2bcc895ddd4b0..50ebb01f363f0 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings_table.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings/use_latest_findings_table.tsx @@ -9,7 +9,7 @@ import { DataView } from '@kbn/data-views-plugin/common'; import { Filter } from '@kbn/es-query'; import { useMemo } from 'react'; import { FindingsBaseURLQuery } from '../../../common/types'; -import { Evaluation } from '../../../../common/types'; +import { Evaluation } from '../../../../common/types_old'; import { LOCAL_STORAGE_DATA_TABLE_PAGE_SIZE_KEY } from '../../../common/constants'; import { useCloudPostureDataTable } from '../../../common/hooks/use_cloud_posture_data_table'; import { getFilters } from '../utils/get_filters'; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings_by_resource/findings_by_resource_container.tsx b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings_by_resource/findings_by_resource_container.tsx index 3054ad352a5bd..85095b149bce4 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings_by_resource/findings_by_resource_container.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings_by_resource/findings_by_resource_container.tsx @@ -10,7 +10,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { TrackApplicationView } from '@kbn/usage-collection-plugin/public'; import { CspFinding } from '../../../../common/schemas/csp_finding'; -import type { Evaluation } from '../../../../common/types'; +import type { Evaluation } from '../../../../common/types_old'; import { FindingsSearchBar } from '../layout/findings_search_bar'; import * as TEST_SUBJECTS from '../test_subjects'; import { usePageSlice } from '../../../common/hooks/use_page_slice'; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings_by_resource/resource_findings/resource_findings_container.tsx b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings_by_resource/resource_findings/resource_findings_container.tsx index f606241862ee2..bc6e67b887096 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings_by_resource/resource_findings/resource_findings_container.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/latest_findings_by_resource/resource_findings/resource_findings_container.tsx @@ -17,7 +17,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { generatePath } from 'react-router-dom'; import { i18n } from '@kbn/i18n'; import { CspInlineDescriptionList } from '../../../../components/csp_inline_description_list'; -import type { Evaluation } from '../../../../../common/types'; +import type { Evaluation } from '../../../../../common/types_old'; import { CspFinding } from '../../../../../common/schemas/csp_finding'; import { CloudPosturePageTitle } from '../../../../components/cloud_posture_page_title'; import * as TEST_SUBJECTS from '../../test_subjects'; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/configurations/layout/findings_distribution_bar.tsx b/x-pack/plugins/cloud_security_posture/public/pages/configurations/layout/findings_distribution_bar.tsx index 1cf084220ea29..35f6fd008d4b9 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/configurations/layout/findings_distribution_bar.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/configurations/layout/findings_distribution_bar.tsx @@ -20,7 +20,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { getAbbreviatedNumber } from '../../../common/utils/get_abbreviated_number'; import { RULE_FAILED, RULE_PASSED } from '../../../../common/constants'; import { statusColors } from '../../../common/constants'; -import type { Evaluation } from '../../../../common/types'; +import type { Evaluation } from '../../../../common/types_old'; interface Props { passed: number; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.test.tsx index 6b0998d4ce218..6301c9cc20b7c 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_container.test.tsx @@ -9,19 +9,19 @@ import React from 'react'; import { RulesContainer } from './rules_container'; import { render, screen } from '@testing-library/react'; import { QueryClient } from '@tanstack/react-query'; -import { useFindCspRuleTemplates } from './use_csp_rules'; +import { useFindCspBenchmarkRule } from './use_csp_benchmark_rules'; import * as TEST_SUBJECTS from './test_subjects'; import { Chance } from 'chance'; import { TestProvider } from '../../test/test_provider'; +import type { CspBenchmarkRule } from '../../../common/types/latest'; import { useParams } from 'react-router-dom'; import { coreMock } from '@kbn/core/public/mocks'; -import { CspRuleTemplate } from '../../../common/schemas'; const chance = new Chance(); -jest.mock('./use_csp_rules', () => ({ - useFindCspRuleTemplates: jest.fn(), - useBulkUpdateCspRuleTemplates: jest.fn(), +jest.mock('./use_csp_benchmark_rules', () => ({ + useFindCspBenchmarkRule: jest.fn(), + useBulkUpdateCspBenchmarkRule: jest.fn(), })); jest.mock('react-router-dom', () => ({ @@ -52,7 +52,7 @@ const getWrapper = return {children}; }; -const getRuleMock = (id = chance.guid()): CspRuleTemplate => +const getRuleMock = (id = chance.guid()): CspBenchmarkRule => ({ metadata: { audit: chance.sentence(), @@ -75,7 +75,7 @@ const getRuleMock = (id = chance.guid()): CspRuleTemplate => tags: [chance.word(), chance.word()], version: chance.sentence(), }, - } as CspRuleTemplate); + } as CspBenchmarkRule); const params = { packagePolicyId: chance.guid(), @@ -93,7 +93,7 @@ describe('', () => { const Wrapper = getWrapper(); const rule1 = getRuleMock(); - (useFindCspRuleTemplates as jest.Mock).mockReturnValue({ + (useFindCspBenchmarkRule as jest.Mock).mockReturnValue({ status: 'success', data: { total: 1, 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 81472e29b6ee7..5ec1971b1f0ac 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 @@ -7,20 +7,23 @@ import React, { useState, useMemo } from 'react'; import { EuiPanel, EuiSpacer } from '@elastic/eui'; import { useParams } from 'react-router-dom'; -import { CspRuleTemplate } from '../../../common/schemas'; import { extractErrorMessage } from '../../../common/utils/helpers'; import { RulesTable } from './rules_table'; import { RulesTableHeader } from './rules_table_header'; -import { useFindCspRuleTemplates, type RulesQuery, type RulesQueryResult } from './use_csp_rules'; +import { + useFindCspBenchmarkRule, + type RulesQuery, + type RulesQueryResult, +} from './use_csp_benchmark_rules'; import * as TEST_SUBJECTS from './test_subjects'; import { RuleFlyout } from './rules_flyout'; import { LOCAL_STORAGE_PAGE_SIZE_RULES_KEY } from '../../common/constants'; import { usePageSize } from '../../common/hooks/use_page_size'; - +import type { CspBenchmarkRule } from '../../../common/types/latest'; interface RulesPageData { - rules_page: CspRuleTemplate[]; - all_rules: CspRuleTemplate[]; - rules_map: Map; + rules_page: CspBenchmarkRule[]; + all_rules: CspBenchmarkRule[]; + rules_map: Map; total: number; error?: string; loading: boolean; @@ -32,7 +35,7 @@ const getRulesPageData = ( { status, data, error }: Pick, query: RulesQuery ): RulesPageData => { - const rules = data?.items || ([] as CspRuleTemplate[]); + const rules = data?.items || ([] as CspBenchmarkRule[]); const page = getPage(rules, query); @@ -46,7 +49,7 @@ const getRulesPageData = ( }; }; -const getPage = (data: CspRuleTemplate[], { page, perPage }: RulesQuery) => +const getPage = (data: CspBenchmarkRule[], { page, perPage }: RulesQuery) => data.slice(page * perPage, (page + 1) * perPage); const MAX_ITEMS_PER_PAGE = 10000; @@ -64,7 +67,7 @@ export const RulesContainer = () => { perPage: pageSize || 10, }); - const { data, status, error } = useFindCspRuleTemplates( + const { data, status, error } = useFindCspBenchmarkRule( { section: rulesQuery.section, search: rulesQuery.search, @@ -80,7 +83,7 @@ export const RulesContainer = () => { ); // We need to make this call again without the filters. this way the section list is always full - const allRules = useFindCspRuleTemplates( + const allRules = useFindCspBenchmarkRule( { page: 1, perPage: MAX_ITEMS_PER_PAGE, 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 49ab9bb79fab8..da9fe29ee26c7 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 @@ -18,14 +18,15 @@ import { EuiFlexGroup, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { CspRuleTemplate, CspRuleTemplateMetadata } from '../../../common/schemas'; + +import { CspBenchmarkRule, CspBenchmarkRuleMetadata } from '../../../common/types/latest'; import { getRuleList } from '../configurations/findings_flyout/rule_tab'; import { getRemediationList } from '../configurations/findings_flyout/overview_tab'; import * as TEST_SUBJECTS from './test_subjects'; interface RuleFlyoutProps { onClose(): void; - rule: CspRuleTemplate; + rule: CspBenchmarkRule; } const tabs = [ @@ -85,7 +86,7 @@ export const RuleFlyout = ({ onClose, rule }: RuleFlyoutProps) => { ); }; -const RuleOverviewTab = ({ rule }: { rule: CspRuleTemplateMetadata }) => ( +const RuleOverviewTab = ({ rule }: { rule: CspBenchmarkRuleMetadata }) => ( diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_table.tsx b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_table.tsx index 167589024529b..c6ce3485f68db 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_table.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/rules_table.tsx @@ -14,7 +14,7 @@ import { useEuiTheme, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { CspRuleTemplate } from '../../../common/schemas'; +import { CspBenchmarkRule } from '../../../common/types/latest'; import type { RulesState } from './rules_container'; import * as TEST_SUBJECTS from './test_subjects'; @@ -41,20 +41,20 @@ export const RulesTable = ({ const { euiTheme } = useEuiTheme(); const columns = useMemo(() => getColumns({ setSelectedRuleId }), [setSelectedRuleId]); - const euiPagination: EuiBasicTableProps['pagination'] = { + const euiPagination: EuiBasicTableProps['pagination'] = { pageIndex: page, pageSize, totalItemCount: total, pageSizeOptions: [10, 25, 100], }; - const onTableChange = ({ page: pagination }: Criteria) => { + const onTableChange = ({ page: pagination }: Criteria) => { if (!pagination) return; setPagination({ page: pagination.index, perPage: pagination.size }); }; - const rowProps = (row: CspRuleTemplate) => ({ - ['data-test-subj']: TEST_SUBJECTS.getCspRuleTemplatesTableRowItemTestId(row.metadata.id), + const rowProps = (row: CspBenchmarkRule) => ({ + ['data-test-subj']: TEST_SUBJECTS.getCspBenchmarkRuleTableRowItemTestId(row.metadata.id), style: { background: row.metadata.id === selectedRuleId ? euiTheme.colors.highlight : undefined, }, @@ -79,7 +79,7 @@ type GetColumnProps = Pick; const getColumns = ({ setSelectedRuleId, -}: GetColumnProps): Array> => [ +}: GetColumnProps): Array> => [ { field: 'metadata.benchmark.rule_number', name: i18n.translate('xpack.csp.rules.rulesTable.ruleNumberColumnLabel', { diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/test_subjects.ts b/x-pack/plugins/cloud_security_posture/public/pages/rules/test_subjects.ts index dbbc6877e2dcf..5f7de36484609 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/rules/test_subjects.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/test_subjects.ts @@ -13,5 +13,5 @@ export const CSP_RULES_TABLE = 'csp_rules_table'; export const CSP_RULES_TABLE_ROW_ITEM_NAME = 'csp_rules_table_row_item_name'; export const CSP_RULES_FLYOUT_CONTAINER = 'csp_rules_flyout_container'; -export const getCspRuleTemplatesTableRowItemTestId = (id: string) => +export const getCspBenchmarkRuleTableRowItemTestId = (id: string) => `${CSP_RULES_TABLE_ROW_ITEM_NAME}_${id}`; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/use_csp_benchmark_rules.ts b/x-pack/plugins/cloud_security_posture/public/pages/rules/use_csp_benchmark_rules.ts new file mode 100644 index 0000000000000..8dca9b63c1930 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/use_csp_benchmark_rules.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 { useQuery } from '@tanstack/react-query'; +import { + FindCspBenchmarkRuleRequest, + FindCspBenchmarkRuleResponse, +} from '../../../common/types/latest'; +import { useKibana } from '../../common/hooks/use_kibana'; + +import { + CSP_BENCHMARK_RULE_SAVED_OBJECT_TYPE, + FIND_CSP_BENCHMARK_RULE_API_CURRENT_VERSION, + FIND_CSP_BENCHMARK_RULE_ROUTE_PATH, +} from '../../../common/constants'; + +export type RulesQuery = Pick< + FindCspBenchmarkRuleRequest, + 'section' | 'search' | 'page' | 'perPage' +>; +export type RulesQueryResult = ReturnType; + +export const useFindCspBenchmarkRule = ( + { search, page, perPage, section }: RulesQuery, + packagePolicyId: string +) => { + const { http } = useKibana().services; + + return useQuery( + [CSP_BENCHMARK_RULE_SAVED_OBJECT_TYPE, { section, search, page, perPage, packagePolicyId }], + () => { + return http.get(FIND_CSP_BENCHMARK_RULE_ROUTE_PATH, { + query: { packagePolicyId, page, perPage, search, section }, + version: FIND_CSP_BENCHMARK_RULE_API_CURRENT_VERSION, + }); + } + ); +}; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/use_csp_integration.tsx b/x-pack/plugins/cloud_security_posture/public/pages/rules/use_csp_integration.tsx index 6d1a01b3d3c2e..76477b1705e97 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/rules/use_csp_integration.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/rules/use_csp_integration.tsx @@ -18,7 +18,7 @@ import { useKibana } from '../../common/hooks/use_kibana'; export const useCspIntegrationInfo = ({ packagePolicyId, policyId }: PageUrlParams) => { const { http } = useKibana().services; - return useQuery(['cspRulesInfo', { packagePolicyId, policyId }], () => + return useQuery(['cspBenchmarkRuleInfo', { packagePolicyId, policyId }], () => Promise.all([ http .get(packagePolicyRouteService.getInfoPath(packagePolicyId), { diff --git a/x-pack/plugins/cloud_security_posture/public/pages/rules/use_csp_rules.ts b/x-pack/plugins/cloud_security_posture/public/pages/rules/use_csp_rules.ts deleted file mode 100644 index ac150e05bf95e..0000000000000 --- a/x-pack/plugins/cloud_security_posture/public/pages/rules/use_csp_rules.ts +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { useQuery } from '@tanstack/react-query'; -import { GetCspRuleTemplateRequest, GetCspRuleTemplateResponse } from '../../../common/types'; -import { useKibana } from '../../common/hooks/use_kibana'; - -import { - CSP_RULE_TEMPLATE_SAVED_OBJECT_TYPE, - FIND_CSP_RULE_TEMPLATE_API_CURRENT_VERSION, - FIND_CSP_RULE_TEMPLATE_ROUTE_PATH, -} from '../../../common/constants'; - -export type RulesQuery = Pick; -export type RulesQueryResult = ReturnType; - -export const useFindCspRuleTemplates = ( - { search, page, perPage, section }: RulesQuery, - packagePolicyId: string -) => { - const { http } = useKibana().services; - - return useQuery( - [CSP_RULE_TEMPLATE_SAVED_OBJECT_TYPE, { section, search, page, perPage, packagePolicyId }], - () => { - return http.get(FIND_CSP_RULE_TEMPLATE_ROUTE_PATH, { - query: { packagePolicyId, page, perPage, search, section }, - version: FIND_CSP_RULE_TEMPLATE_API_CURRENT_VERSION, - }); - } - ); -}; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_by_resource/severity_map.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_by_resource/severity_map.tsx index 94ebaf30572b3..af47c20ddc50f 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_by_resource/severity_map.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerabilities/vulnerabilities_by_resource/severity_map.tsx @@ -17,7 +17,7 @@ import { import { PaletteColorStop } from '@elastic/eui/src/components/color_picker/color_palette_picker'; import { i18n } from '@kbn/i18n'; import { getSeverityStatusColor } from '../../../common/utils/get_vulnerability_colors'; -import { VulnSeverity } from '../../../../common/types'; +import { VulnSeverity } from '../../../../common/types_old'; import { SeverityStatusBadge } from '../../../components/vulnerability_badges'; interface Props { diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerability_dashboard/_mocks_/vulnerability_dashboard.mock.ts b/x-pack/plugins/cloud_security_posture/public/pages/vulnerability_dashboard/_mocks_/vulnerability_dashboard.mock.ts index 6d7f909a82e53..0148105bbc83a 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerability_dashboard/_mocks_/vulnerability_dashboard.mock.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerability_dashboard/_mocks_/vulnerability_dashboard.mock.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { CnvmDashboardData } from '../../../../common/types'; +import { CnvmDashboardData } from '../../../../common/types_old'; export const mockCnvmDashboardData: CnvmDashboardData = { cnvmStatistics: { diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerability_dashboard/vulnerability_table_panel_section.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerability_dashboard/vulnerability_table_panel_section.tsx index 14c3451fd2731..1ba912b1d125b 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerability_dashboard/vulnerability_table_panel_section.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerability_dashboard/vulnerability_table_panel_section.tsx @@ -21,7 +21,7 @@ import { VulnerabilityStat, VulnerableResourceStat, VulnSeverity, -} from '../../../common/types'; +} from '../../../common/types_old'; import { DASHBOARD_TABLE_TYPES } from './vulnerability_table_panel.config'; import { VulnerabilityTablePanel } from './vulnerability_table_panel'; import { diff --git a/x-pack/plugins/cloud_security_posture/public/pages/vulnerability_dashboard/vulnerability_trend_graph.tsx b/x-pack/plugins/cloud_security_posture/public/pages/vulnerability_dashboard/vulnerability_trend_graph.tsx index a8c528f7f8804..92cffe08d9b0b 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/vulnerability_dashboard/vulnerability_trend_graph.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/vulnerability_dashboard/vulnerability_trend_graph.tsx @@ -14,13 +14,14 @@ import { timeFormatter, niceTimeFormatByDay, PartialTheme, + LEGACY_LIGHT_THEME, } from '@elastic/charts'; import { EuiButton, EuiComboBox } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { truthy } from '../../../common/utils/helpers'; import { useNavigateVulnerabilities } from '../../common/hooks/use_navigate_findings'; -import { VulnStatsTrend, VulnSeverity } from '../../../common/types'; +import { VulnStatsTrend, VulnSeverity } from '../../../common/types_old'; import { useVulnerabilityDashboardApi } from '../../common/api/use_vulnerability_dashboard_api'; import { getSeverityStatusColor } from '../../common/utils/get_vulnerability_colors'; import { ChartPanel } from '../../components/chart_panel'; @@ -206,7 +207,14 @@ export const VulnerabilityTrendGraph = () => { >
- + ; -export const createCspBenchmarkIntegrationFixture = ({ - chance = new Chance(), - package_policy = { - revision: chance?.integer(), - enabled: true, - id: chance.guid(), - name: chance.string(), - policy_id: chance.guid(), - namespace: chance.string(), - updated_at: chance.date().toISOString(), - updated_by: chance.word(), - created_at: chance.date().toISOString(), - created_by: chance.word(), - inputs: [ - { - type: 'cloudbeat/cis_k8s', - policy_template: 'kspm', - enabled: true, - streams: [ - { - id: chance?.guid(), - enabled: true, - data_stream: { - type: 'logs', - dataset: 'cloud_security_posture.findings', - }, - }, - ], - }, - ], - package: { - name: chance.string(), - title: chance.string(), - version: chance.string(), +export const createCspBenchmarkIntegrationFixture = + ({}: CreateCspBenchmarkIntegrationFixtureInput = {}): Benchmark => ({ + id: 'cis_aws', + version: '1.0.1', + evaluation: 2, + score: { + postureScore: 85, + resourcesEvaluated: 183, + totalFailed: 66, + totalFindings: 440, + totalPassed: 374, }, - }, - agent_policy = { - id: chance.guid(), - name: chance.sentence(), - agents: chance.integer({ min: 0 }), - }, - rules_count = chance.integer({ min: 0, max: 10 }), -}: CreateCspBenchmarkIntegrationFixtureInput = {}): Benchmark => ({ - package_policy, - agent_policy, - rules_count, -}); + name: 'CIS Amazon Web Services Foundations', + }); diff --git a/x-pack/plugins/cloud_security_posture/server/lib/check_index_status.ts b/x-pack/plugins/cloud_security_posture/server/lib/check_index_status.ts index ebce74d89b2dc..67eb611f9557e 100644 --- a/x-pack/plugins/cloud_security_posture/server/lib/check_index_status.ts +++ b/x-pack/plugins/cloud_security_posture/server/lib/check_index_status.ts @@ -7,7 +7,7 @@ import { ElasticsearchClient, type Logger } from '@kbn/core/server'; import { getSafePostureTypeRuntimeMapping } from '../../common/runtime_mappings/get_safe_posture_type_runtime_mapping'; -import { IndexStatus, PostureTypes } from '../../common/types'; +import { IndexStatus, PostureTypes } from '../../common/types_old'; export interface PostureTypeAndRetention { postureType?: PostureTypes; diff --git a/x-pack/plugins/cloud_security_posture/server/lib/fleet_util.ts b/x-pack/plugins/cloud_security_posture/server/lib/fleet_util.ts index cd67f29cef1b4..fd388967ee9b6 100644 --- a/x-pack/plugins/cloud_security_posture/server/lib/fleet_util.ts +++ b/x-pack/plugins/cloud_security_posture/server/lib/fleet_util.ts @@ -19,7 +19,7 @@ import type { PackagePolicyInput, } from '@kbn/fleet-plugin/common'; import { errors } from '@elastic/elasticsearch'; -import { CloudSecurityPolicyTemplate, PostureTypes } from '../../common/types'; +import { CloudSecurityPolicyTemplate, PostureTypes } from '../../common/types_old'; import { SUPPORTED_POLICY_TEMPLATES, CLOUD_SECURITY_POSTURE_PACKAGE_NAME, @@ -31,7 +31,7 @@ import { BENCHMARK_PACKAGE_POLICY_PREFIX, BenchmarksQueryParams, DEFAULT_BENCHMARKS_PER_PAGE, -} from '../../common/schemas/benchmark'; +} from '../../common/types/benchmarks/v1'; export const PACKAGE_POLICY_SAVED_OBJECT_TYPE = 'ingest-package-policies'; diff --git a/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/types.ts b/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/types.ts index cb8d6eec64dcb..f9eb6094277a4 100644 --- a/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/types.ts +++ b/x-pack/plugins/cloud_security_posture/server/lib/telemetry/collectors/types.ts @@ -6,7 +6,7 @@ */ import { AggregationsMultiBucketBase } from '@elastic/elasticsearch/lib/api/types'; -import { CspStatusCode } from '../../../../common/types'; +import { CspStatusCode } from '../../../../common/types_old'; export type CloudSecurityUsageCollectorType = | 'Indices' diff --git a/x-pack/plugins/cloud_security_posture/server/plugin.ts b/x-pack/plugins/cloud_security_posture/server/plugin.ts index 8b77581efd39d..58a143b220857 100755 --- a/x-pack/plugins/cloud_security_posture/server/plugin.ts +++ b/x-pack/plugins/cloud_security_posture/server/plugin.ts @@ -35,7 +35,7 @@ import type { CspServerPluginStartServices, } from './types'; import { setupRoutes } from './routes/setup_routes'; -import { setupSavedObjects } from './saved_objects'; +import { cspBenchmarkRule, cspSettings } from './saved_objects'; import { initializeCspIndices } from './create_indices/create_indices'; import { initializeCspTransforms } from './create_transforms/create_transforms'; import { @@ -50,6 +50,7 @@ import { } from './tasks/findings_stats_task'; import { registerCspmUsageCollector } from './lib/telemetry/collectors/register'; import { CloudSecurityPostureConfig } from './config'; +import { CspBenchmarkRule, CspSettings } from '../common/types/latest'; export class CspPlugin implements @@ -80,7 +81,8 @@ export class CspPlugin core: CoreSetup, plugins: CspServerPluginSetupDeps ): CspServerPluginSetup { - setupSavedObjects(core.savedObjects); + core.savedObjects.registerType(cspBenchmarkRule); + core.savedObjects.registerType(cspSettings); setupRoutes({ core, diff --git a/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/bulk_action/bulk_action.test.ts b/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/bulk_action/bulk_action.test.ts new file mode 100644 index 0000000000000..1bb2232d2834d --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/bulk_action/bulk_action.test.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import expect from 'expect'; +import { setRulesStates, buildRuleKey } from './utils'; + +describe('CSP Rule State Management', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should set rules states correctly', () => { + const ruleIds = ['rule1', 'rule3']; + const newState = true; + + const updatedRulesStates = setRulesStates(ruleIds, newState); + + expect(updatedRulesStates).toEqual({ + rule1: { muted: true }, + rule3: { muted: true }, + }); + }); + + it('should build a rule key with the provided benchmarkId, benchmarkVersion, and ruleNumber', () => { + const benchmarkId = 'randomId'; + const benchmarkVersion = 'v1'; + const ruleNumber = '001'; + + const result = buildRuleKey(benchmarkId, benchmarkVersion, ruleNumber); + + expect(result).toBe(`${benchmarkId};${benchmarkVersion};${ruleNumber}`); + }); +}); diff --git a/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/bulk_action/bulk_action.ts b/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/bulk_action/bulk_action.ts new file mode 100644 index 0000000000000..a87df39341741 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/bulk_action/bulk_action.ts @@ -0,0 +1,68 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { transformError } from '@kbn/securitysolution-es-utils'; +import { + CspBenchmarkRulesBulkActionRequestSchema, + CspBenchmarkRulesStates, + cspBenchmarkRulesBulkActionRequestSchema, +} from '../../../../common/types/rules/v3'; +import { CspRouter } from '../../../types'; + +import { CSP_BENCHMARK_RULES_BULK_ACTION_ROUTE_PATH } from '../../../../common/constants'; +import { bulkActionBenchmarkRulesHandler } from './v1'; + +export const defineBulkActionCspBenchmarkRulesRoute = (router: CspRouter) => + router.versioned + .post({ + access: 'internal', + path: CSP_BENCHMARK_RULES_BULK_ACTION_ROUTE_PATH, + }) + .addVersion( + { + version: '1', + validate: { + request: { + body: cspBenchmarkRulesBulkActionRequestSchema, + }, + }, + }, + async (context, request, response) => { + if (!(await context.fleet).authz.fleet.all) { + return response.forbidden(); + } + const cspContext = await context.csp; + + try { + const requestBody: CspBenchmarkRulesBulkActionRequestSchema = request.body; + + const benchmarkRulesToUpdate = requestBody.rules; + + const handlerResponse = await bulkActionBenchmarkRulesHandler( + cspContext.encryptedSavedObjects, + benchmarkRulesToUpdate, + requestBody.action + ); + + const updatedBenchmarkRules: CspBenchmarkRulesStates = handlerResponse; + return response.ok({ + body: { + updated_benchmark_rules: updatedBenchmarkRules, + message: 'The bulk operation has been executed successfully.', + }, + }); + } catch (err) { + const error = transformError(err); + + cspContext.logger.error(`Bulk action failed: ${error.message}`); + return response.customError({ + body: { message: error.message }, + statusCode: error.statusCode || 500, // Default to 500 if no specific status code is provided + }); + } + } + ); diff --git a/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/bulk_action/utils.ts b/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/bulk_action/utils.ts new file mode 100644 index 0000000000000..3442b3f050b90 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/bulk_action/utils.ts @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { + SavedObjectsClientContract, + SavedObjectsUpdateResponse, +} from '@kbn/core-saved-objects-api-server'; +import { CspBenchmarkRulesStates, CspSettings } from '../../../../common/types/rules/v3'; + +import { + INTERNAL_CSP_SETTINGS_SAVED_OBJECT_ID, + INTERNAL_CSP_SETTINGS_SAVED_OBJECT_TYPE, +} from '../../../../common/constants'; + +export const updateRulesStates = async ( + encryptedSoClient: SavedObjectsClientContract, + newRulesStates: CspBenchmarkRulesStates +): Promise> => { + return await encryptedSoClient.update( + INTERNAL_CSP_SETTINGS_SAVED_OBJECT_TYPE, + INTERNAL_CSP_SETTINGS_SAVED_OBJECT_ID, + { rules: newRulesStates }, + // if there is no saved object yet, insert a new SO + { upsert: { rules: newRulesStates } } + ); +}; + +export const setRulesStates = (ruleIds: string[], state: boolean): CspBenchmarkRulesStates => { + const rulesStates: CspBenchmarkRulesStates = {}; + ruleIds.forEach((ruleId) => { + rulesStates[ruleId] = { muted: state }; + }); + return rulesStates; +}; + +export const buildRuleKey = (benchmarkId: string, benchmarkVersion: string, ruleNumber: string) => { + return `${benchmarkId};${benchmarkVersion};${ruleNumber}`; +}; diff --git a/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/bulk_action/v1.ts b/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/bulk_action/v1.ts new file mode 100644 index 0000000000000..42895b5eb694d --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/bulk_action/v1.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; +import { CspBenchmarkRules, CspBenchmarkRulesStates } from '../../../../common/types/rules/v3'; +import { buildRuleKey, setRulesStates, updateRulesStates } from './utils'; + +const muteStatesMap = { + mute: true, + unmute: false, +}; + +export const bulkActionBenchmarkRulesHandler = async ( + encryptedSoClient: SavedObjectsClientContract, + rulesToUpdate: CspBenchmarkRules, + action: 'mute' | 'unmute' +): Promise => { + const ruleKeys = rulesToUpdate.map((rule) => + buildRuleKey(rule.benchmark_id, rule.benchmark_version, rule.rule_number) + ); + + const newRulesStates = setRulesStates(ruleKeys, muteStatesMap[action]); + + const newCspSettings = await updateRulesStates(encryptedSoClient, newRulesStates); + + return newCspSettings.attributes.rules!; +}; diff --git a/x-pack/plugins/cloud_security_posture/server/routes/csp_rule_template/get_csp_rule_templates.test.ts b/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/find/find.test.ts similarity index 68% rename from x-pack/plugins/cloud_security_posture/server/routes/csp_rule_template/get_csp_rule_templates.test.ts rename to x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/find/find.test.ts index f894d031c4bc0..2a0130c0f0fd7 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/csp_rule_template/get_csp_rule_templates.test.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/find/find.test.ts @@ -5,12 +5,12 @@ * 2.0. */ -import { getSortedCspRulesTemplates } from './get_csp_rule_template'; -import { CspRuleTemplate } from '../../../common/schemas'; +import { getSortedCspBenchmarkRulesTemplates } from './utils'; +import { CspBenchmarkRule } from '../../../../common/types/latest'; -describe('getSortedCspRulesTemplates', () => { +describe('getSortedCspBenchmarkRules', () => { it('sorts by metadata.benchmark.rule_number, invalid semantic version still should still get sorted and empty values should be sorted last', () => { - const cspRulesTemplates = [ + const cspBenchmarkRules = [ { metadata: { benchmark: { rule_number: '1.0.0' } } }, { metadata: { benchmark: { rule_number: '2.0.0' } } }, { metadata: { benchmark: { rule_number: '1.1.0' } } }, @@ -18,11 +18,11 @@ describe('getSortedCspRulesTemplates', () => { { metadata: { benchmark: { rule_number: 'invalid' } } }, { metadata: { benchmark: { rule_number: '3.0' } } }, { metadata: { benchmark: {} } }, - ] as CspRuleTemplate[]; + ] as CspBenchmarkRule[]; - const sortedCspRulesTemplates = getSortedCspRulesTemplates(cspRulesTemplates); + const sortedCspBenchmarkRules = getSortedCspBenchmarkRulesTemplates(cspBenchmarkRules); - expect(sortedCspRulesTemplates).toEqual([ + expect(sortedCspBenchmarkRules).toEqual([ { metadata: { benchmark: { rule_number: '1.0.0' } } }, { metadata: { benchmark: { rule_number: '1.0.1' } } }, { metadata: { benchmark: { rule_number: '1.1.0' } } }, @@ -34,36 +34,36 @@ describe('getSortedCspRulesTemplates', () => { }); it('edge case - returns empty array if input is empty', () => { - const cspRulesTemplates: CspRuleTemplate[] = []; + const cspBenchmarkRules: CspBenchmarkRule[] = []; - const sortedCspRulesTemplates = getSortedCspRulesTemplates(cspRulesTemplates); + const sortedCspBenchmarkRules = getSortedCspBenchmarkRulesTemplates(cspBenchmarkRules); - expect(sortedCspRulesTemplates).toEqual([]); + expect(sortedCspBenchmarkRules).toEqual([]); }); it('edge case - returns sorted array even if input only has one element', () => { - const cspRulesTemplates = [ + const cspBenchmarkRules = [ { metadata: { benchmark: { rule_number: '1.0.0' } } }, - ] as CspRuleTemplate[]; + ] as CspBenchmarkRule[]; - const sortedCspRulesTemplates = getSortedCspRulesTemplates(cspRulesTemplates); + const sortedCspBenchmarkRules = getSortedCspBenchmarkRulesTemplates(cspBenchmarkRules); - expect(sortedCspRulesTemplates).toEqual([ + expect(sortedCspBenchmarkRules).toEqual([ { metadata: { benchmark: { rule_number: '1.0.0' } } }, ]); }); it('returns sorted array even with undefined or null properties', () => { - const cspRulesTemplates = [ + const cspBenchmarkRules = [ { metadata: { benchmark: { rule_number: '1.0.0' } } }, { metadata: { benchmark: { rule_number: undefined } } }, { metadata: { benchmark: { rule_number: '2.0.0' } } }, { metadata: { benchmark: { rule_number: null } } }, - ] as CspRuleTemplate[]; + ] as CspBenchmarkRule[]; - const sortedCspRulesTemplates = getSortedCspRulesTemplates(cspRulesTemplates); + const sortedCspBenchmarkRules = getSortedCspBenchmarkRulesTemplates(cspBenchmarkRules); - expect(sortedCspRulesTemplates).toEqual([ + expect(sortedCspBenchmarkRules).toEqual([ { metadata: { benchmark: { rule_number: '1.0.0' } } }, { metadata: { benchmark: { rule_number: '2.0.0' } } }, { metadata: { benchmark: { rule_number: null } } }, @@ -72,15 +72,15 @@ describe('getSortedCspRulesTemplates', () => { }); it('returns sorted array with invalid semantic versions', () => { - const cspRulesTemplates = [ + const cspBenchmarkRules = [ { metadata: { benchmark: { rule_number: '1.0.0' } } }, { metadata: { benchmark: { rule_number: '2.0' } } }, { metadata: { benchmark: { rule_number: '3.0.0' } } }, - ] as CspRuleTemplate[]; + ] as CspBenchmarkRule[]; - const sortedCspRulesTemplates = getSortedCspRulesTemplates(cspRulesTemplates); + const sortedCspBenchmarkRules = getSortedCspBenchmarkRulesTemplates(cspBenchmarkRules); - expect(sortedCspRulesTemplates).toEqual([ + expect(sortedCspBenchmarkRules).toEqual([ { metadata: { benchmark: { rule_number: '1.0.0' } } }, { metadata: { benchmark: { rule_number: '2.0' } } }, { metadata: { benchmark: { rule_number: '3.0.0' } } }, diff --git a/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/find/find.ts b/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/find/find.ts new file mode 100644 index 0000000000000..728fca1c0ae7c --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/find/find.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 { transformError } from '@kbn/securitysolution-es-utils'; +import { + FindCspBenchmarkRuleRequest, + FindCspBenchmarkRuleResponse, + findCspBenchmarkRuleRequestSchema, +} from '../../../../common/types/rules/v3'; +import { FIND_CSP_BENCHMARK_RULE_ROUTE_PATH } from '../../../../common/constants'; +import { CspRouter } from '../../../types'; +import { findBenchmarkRuleHandler as findRuleHandlerV1 } from './v1'; + +export const defineFindCspBenchmarkRuleRoute = (router: CspRouter) => + router.versioned + .get({ + access: 'internal', + path: FIND_CSP_BENCHMARK_RULE_ROUTE_PATH, + }) + .addVersion( + { + version: '1', + validate: { + request: { + query: findCspBenchmarkRuleRequestSchema, + }, + }, + }, + async (context, request, response) => { + if (!(await context.fleet).authz.fleet.all) { + return response.forbidden(); + } + + const requestBody: FindCspBenchmarkRuleRequest = request.query; + const cspContext = await context.csp; + + try { + const cspBenchmarkRules: FindCspBenchmarkRuleResponse = await findRuleHandlerV1( + cspContext.soClient, + requestBody + ); + return response.ok({ body: cspBenchmarkRules }); + } catch (err) { + const error = transformError(err); + cspContext.logger.error(`Failed to fetch csp rules templates ${err}`); + return response.customError({ + body: { message: error.message }, + statusCode: error.statusCode, + }); + } + } + ); diff --git a/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/find/utils.ts b/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/find/utils.ts new file mode 100644 index 0000000000000..0601a4eb25577 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/find/utils.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 semverValid from 'semver/functions/valid'; +import semverCompare from 'semver/functions/compare'; +import { NewPackagePolicy } from '@kbn/fleet-plugin/common'; +import { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; +import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '../../benchmarks/benchmarks'; +import { getBenchmarkFromPackagePolicy } from '../../../../common/utils/helpers'; + +import type { CspBenchmarkRule } from '../../../../common/types/latest'; + +export const getSortedCspBenchmarkRulesTemplates = (cspBenchmarkRules: CspBenchmarkRule[]) => { + return cspBenchmarkRules.slice().sort((a, b) => { + const ruleNumberA = a?.metadata?.benchmark?.rule_number; + const ruleNumberB = b?.metadata?.benchmark?.rule_number; + + const versionA = semverValid(ruleNumberA); + const versionB = semverValid(ruleNumberB); + + if (versionA !== null && versionB !== null) { + return semverCompare(versionA, versionB); + } else { + return String(ruleNumberA).localeCompare(String(ruleNumberB)); + } + }); +}; + +export const getBenchmarkIdFromPackagePolicyId = async ( + soClient: SavedObjectsClientContract, + packagePolicyId: string +): Promise => { + const res = await soClient.get( + PACKAGE_POLICY_SAVED_OBJECT_TYPE, + packagePolicyId + ); + return getBenchmarkFromPackagePolicy(res.attributes.inputs); +}; diff --git a/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/find/v1.ts b/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/find/v1.ts new file mode 100644 index 0000000000000..4971098cb8067 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/server/routes/benchmark_rules/find/v1.ts @@ -0,0 +1,56 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; +import { getBenchmarkFilter } from '../../../../common/utils/helpers'; +import { CSP_BENCHMARK_RULE_SAVED_OBJECT_TYPE } from '../../../../common/constants'; +import { getBenchmarkIdFromPackagePolicyId, getSortedCspBenchmarkRulesTemplates } from './utils'; +import type { CspBenchmarkRule } from '../../../../common/types/latest'; +import type { + FindCspBenchmarkRuleRequest, + FindCspBenchmarkRuleResponse, +} from '../../../../common/types/rules/v3'; + +export const findBenchmarkRuleHandler = async ( + soClient: SavedObjectsClientContract, + options: FindCspBenchmarkRuleRequest +): Promise => { + if ( + (!options.packagePolicyId && !options.benchmarkId) || + (options.packagePolicyId && options.benchmarkId) + ) { + throw new Error('Please provide either benchmarkId or packagePolicyId, but not both'); + } + + const benchmarkId = options.benchmarkId + ? options.benchmarkId + : await getBenchmarkIdFromPackagePolicyId(soClient, options.packagePolicyId!); + + const cspCspBenchmarkRulesSo = await soClient.find({ + type: CSP_BENCHMARK_RULE_SAVED_OBJECT_TYPE, + searchFields: options.searchFields, + search: options.search ? `"${options.search}"*` : '', + page: options.page, + perPage: options.perPage, + sortField: options.sortField, + fields: options?.fields, + filter: getBenchmarkFilter(benchmarkId, options.section), + }); + + const cspBenchmarkRules = cspCspBenchmarkRulesSo.saved_objects.map( + (cspBenchmarkRule) => cspBenchmarkRule.attributes + ); + + // Semantic version sorting using semver for valid versions and custom comparison for invalid versions + const sortedCspBenchmarkRules = getSortedCspBenchmarkRulesTemplates(cspBenchmarkRules); + + return { + items: sortedCspBenchmarkRules, + total: cspCspBenchmarkRulesSo.total, + page: options.page, + perPage: options.perPage, + }; +}; diff --git a/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.test.ts b/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.test.ts index 25fc95e1f8a20..52a112aae5a53 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.test.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.test.ts @@ -8,10 +8,10 @@ import { httpServerMock, httpServiceMock, savedObjectsClientMock } from '@kbn/co import { benchmarksQueryParamsSchema, DEFAULT_BENCHMARKS_PER_PAGE, -} from '../../../common/schemas/benchmark'; +} from '../../../common/types/benchmarks/v1'; import { getCspAgentPolicies } from '../../lib/fleet_util'; -import { defineGetBenchmarksRoute, getRulesCountForPolicy } from './benchmarks'; - +import { defineGetBenchmarksRoute } from './benchmarks'; +import { getRulesCountForPolicy } from './utilities'; import { SavedObjectsClientContract, SavedObjectsFindResponse } from '@kbn/core/server'; import { createMockAgentPolicyService } from '@kbn/fleet-plugin/server/mocks'; import { createPackagePolicyMock } from '@kbn/fleet-plugin/common/mocks'; @@ -57,7 +57,6 @@ describe('benchmarks API', () => { defineGetBenchmarksRoute(router); const versionedRouter = router.versioned.get.mock.results[0].value; - const handler = versionedRouter.addVersion.mock.calls[0][1]; const mockContext = createCspRequestHandlerContextMock(); @@ -174,7 +173,7 @@ describe('benchmarks API', () => { }); }); - describe('test addPackagePolicyCspRuleTemplates', () => { + describe('test addPackagePolicyCspBenchmarkRule', () => { it('should retrieve the rules count by the filtered benchmark type', async () => { const benchmark = 'cis_k8s'; mockSoClient.find.mockResolvedValueOnce({ diff --git a/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.ts b/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.ts index 4c78265c1c15e..8ba6d0449be6a 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/benchmarks.ts @@ -4,86 +4,17 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import type { SavedObjectsClientContract } from '@kbn/core/server'; + import { transformError } from '@kbn/securitysolution-es-utils'; -import type { AgentPolicy, ListResult, PackagePolicy } from '@kbn/fleet-plugin/common'; -import { CspRuleTemplate } from '../../../common/schemas'; -import { CSP_RULE_TEMPLATE_SAVED_OBJECT_TYPE } from '../../../common/constants'; -import { - BENCHMARKS_ROUTE_PATH, - CLOUD_SECURITY_POSTURE_PACKAGE_NAME, - POSTURE_TYPE_ALL, -} from '../../../common/constants'; -import { benchmarksQueryParamsSchema } from '../../../common/schemas/benchmark'; -import type { Benchmark } from '../../../common/types'; -import { - getBenchmarkFromPackagePolicy, - getBenchmarkFilter, - isNonNullable, -} from '../../../common/utils/helpers'; +import { BENCHMARKS_ROUTE_PATH } from '../../../common/constants'; +import { benchmarksQueryParamsSchema } from '../../../common/types/benchmarks/v1'; import { CspRouter } from '../../types'; -import { - getAgentStatusesByAgentPolicies, - type AgentStatusByAgentPolicyMap, - getCspAgentPolicies, - getCspPackagePolicies, -} from '../../lib/fleet_util'; -import { BenchmarkId } from '../../../common/types'; +import { getBenchmarks as getBenchmarksV1 } from './v1'; +import { getBenchmarks as getBenchmarksV2 } from './v2'; +import { benchmarkResponseSchema } from '../../../common/types/latest'; export const PACKAGE_POLICY_SAVED_OBJECT_TYPE = 'ingest-package-policies'; -export const getRulesCountForPolicy = async ( - soClient: SavedObjectsClientContract, - benchmarkId: BenchmarkId -): Promise => { - const rules = await soClient.find({ - type: CSP_RULE_TEMPLATE_SAVED_OBJECT_TYPE, - filter: getBenchmarkFilter(benchmarkId), - perPage: 0, - }); - - return rules.total; -}; - -const createBenchmarks = ( - soClient: SavedObjectsClientContract, - agentPolicies: AgentPolicy[], - agentStatusByAgentPolicyId: AgentStatusByAgentPolicyMap, - cspPackagePolicies: PackagePolicy[] -): Promise => { - const cspPackagePoliciesMap = new Map( - cspPackagePolicies.map((packagePolicy) => [packagePolicy.id, packagePolicy]) - ); - - return Promise.all( - agentPolicies.flatMap((agentPolicy) => { - const cspPackagesOnAgent = - agentPolicy.package_policies - ?.map(({ id: pckPolicyId }) => { - return cspPackagePoliciesMap.get(pckPolicyId); - }) - .filter(isNonNullable) ?? []; - - const benchmarks = cspPackagesOnAgent.map(async (cspPackage) => { - const benchmarkId = getBenchmarkFromPackagePolicy(cspPackage.inputs); - const rulesCount = await getRulesCountForPolicy(soClient, benchmarkId); - const agentPolicyStatus = { - id: agentPolicy.id, - name: agentPolicy.name, - agents: agentStatusByAgentPolicyId[agentPolicy.id]?.total, - }; - return { - package_policy: cspPackage, - agent_policy: agentPolicyStatus, - rules_count: rulesCount, - }; - }); - - return benchmarks; - }) - ); -}; - export const defineGetBenchmarksRoute = (router: CspRouter) => router.versioned .get({ @@ -106,45 +37,54 @@ export const defineGetBenchmarksRoute = (router: CspRouter) => if (!(await context.fleet).authz.fleet.all) { return response.forbidden(); } - const cspContext = await context.csp; - const excludeVulnMgmtPackages = true; try { - const packagePolicies: ListResult = await getCspPackagePolicies( + const cspBenchmarks = await getBenchmarksV1( cspContext.soClient, cspContext.packagePolicyService, - CLOUD_SECURITY_POSTURE_PACKAGE_NAME, request.query, - POSTURE_TYPE_ALL, - excludeVulnMgmtPackages - ); - - const agentPolicies = await getCspAgentPolicies( - cspContext.soClient, - packagePolicies.items, - cspContext.agentPolicyService - ); - - const agentStatusesByAgentPolicyId = await getAgentStatusesByAgentPolicies( + cspContext.agentPolicyService, cspContext.agentService, - agentPolicies, cspContext.logger ); - - const benchmarks = await createBenchmarks( + return response.ok({ + body: cspBenchmarks, + }); + } catch (err) { + const error = transformError(err); + cspContext.logger.error(`Failed to fetch benchmarks ${err}`); + return response.customError({ + body: { message: error.message }, + statusCode: error.statusCode, + }); + } + } + ) + .addVersion( + { + version: '2', + validate: { + response: { + 200: { + body: benchmarkResponseSchema, + }, + }, + }, + }, + async (context, request, response) => { + if (!(await context.fleet).authz.fleet.all) { + return response.forbidden(); + } + const cspContext = await context.csp; + const esClient = cspContext.esClient.asCurrentUser; + try { + const cspBenchmarks = await getBenchmarksV2( + esClient, cspContext.soClient, - agentPolicies, - agentStatusesByAgentPolicyId, - packagePolicies.items + cspContext.logger ); - - const getBenchmarkResponse = { - ...packagePolicies, - items: benchmarks, - }; - return response.ok({ - body: getBenchmarkResponse, + body: cspBenchmarks, }); } catch (err) { const error = transformError(err); diff --git a/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/utilities.ts b/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/utilities.ts new file mode 100644 index 0000000000000..d7ce82da9d98c --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/utilities.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; + +import { CSP_BENCHMARK_RULE_SAVED_OBJECT_TYPE } from '../../../common/constants'; + +import { CspBenchmarkRule } from '../../../common/types/latest'; +import { BenchmarkId } from '../../../common/types_old'; +import { getBenchmarkFilter } from '../../../common/utils/helpers'; + +export const getRulesCountForPolicy = async ( + soClient: SavedObjectsClientContract, + benchmarkId: BenchmarkId +): Promise => { + const rules = await soClient.find({ + type: CSP_BENCHMARK_RULE_SAVED_OBJECT_TYPE, + filter: getBenchmarkFilter(benchmarkId), + perPage: 0, + }); + + return rules.total; +}; diff --git a/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/v1.ts b/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/v1.ts new file mode 100644 index 0000000000000..611a58436e995 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/v1.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 { ListResult, PackagePolicy, AgentPolicy } from '@kbn/fleet-plugin/common'; +import type { Logger } from '@kbn/core/server'; +import { + PackagePolicyClient, + AgentPolicyServiceInterface, + AgentService, +} from '@kbn/fleet-plugin/server'; +import { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; +import { CLOUD_SECURITY_POSTURE_PACKAGE_NAME, POSTURE_TYPE_ALL } from '../../../common/constants'; +import { isNonNullable, getBenchmarkFromPackagePolicy } from '../../../common/utils/helpers'; +import { AgentStatusByAgentPolicyMap } from '../../lib/fleet_util'; +import { + getCspPackagePolicies, + getCspAgentPolicies, + getAgentStatusesByAgentPolicies, +} from '../../lib/fleet_util'; +import { getRulesCountForPolicy } from './utilities'; +import { Benchmark } from '../../../common/types/benchmarks/v1'; + +export const getBenchmarksData = ( + soClient: SavedObjectsClientContract, + agentPolicies: AgentPolicy[], + agentStatusByAgentPolicyId: AgentStatusByAgentPolicyMap, + cspPackagePolicies: PackagePolicy[] +): Promise => { + const cspPackagePoliciesMap = new Map( + cspPackagePolicies.map((packagePolicy) => [packagePolicy.id, packagePolicy]) + ); + + return Promise.all( + agentPolicies.flatMap((agentPolicy) => { + const cspPackagesOnAgent = + agentPolicy.package_policies + ?.map(({ id: pckPolicyId }) => { + return cspPackagePoliciesMap.get(pckPolicyId); + }) + .filter(isNonNullable) ?? []; + + const benchmarks = cspPackagesOnAgent.map(async (cspPackage) => { + const benchmarkId = getBenchmarkFromPackagePolicy(cspPackage.inputs); + const rulesCount = await getRulesCountForPolicy(soClient, benchmarkId); + const agentPolicyStatus = { + id: agentPolicy.id, + name: agentPolicy.name, + agents: agentStatusByAgentPolicyId[agentPolicy.id]?.total, + }; + return { + package_policy: cspPackage, + agent_policy: agentPolicyStatus, + rules_count: rulesCount, + }; + }); + + return benchmarks; + }) + ); +}; + +export const getBenchmarks = async ( + soClient: SavedObjectsClientContract, + packagePolicyService: PackagePolicyClient, + query: any, + agentPolicyService: AgentPolicyServiceInterface, + agentService: AgentService, + logger: Logger +) => { + const excludeVulnMgmtPackages = true; + + const packagePolicies: ListResult = await getCspPackagePolicies( + soClient, + packagePolicyService, + CLOUD_SECURITY_POSTURE_PACKAGE_NAME, + query, + POSTURE_TYPE_ALL, + excludeVulnMgmtPackages + ); + + const agentPolicies = await getCspAgentPolicies( + soClient, + packagePolicies.items, + agentPolicyService + ); + + const agentStatusesByAgentPolicyId = await getAgentStatusesByAgentPolicies( + agentService, + agentPolicies, + logger + ); + + const benchmarks = await getBenchmarksData( + soClient, + agentPolicies, + agentStatusesByAgentPolicyId, + packagePolicies.items + ); + + const getBenchmarkResponse = { + ...packagePolicies, + items: benchmarks, + }; + + return getBenchmarkResponse; +}; diff --git a/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/v2.ts b/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/v2.ts new file mode 100644 index 0000000000000..5e0a4c5014365 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/server/routes/benchmarks/v2.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 { QueryDslQueryContainer } from '@kbn/data-views-plugin/common/types'; +import { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { Logger } from '@kbn/core/server'; +import { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; +import { + CSP_BENCHMARK_RULE_SAVED_OBJECT_TYPE, + LATEST_FINDINGS_INDEX_DEFAULT_NS, +} from '../../../common/constants'; + +import { CspBenchmarkRule, Benchmark } from '../../../common/types/latest'; +import { getClusters } from '../compliance_dashboard/get_clusters'; +import { getStats } from '../compliance_dashboard/get_stats'; +import { getSafePostureTypeRuntimeMapping } from '../../../common/runtime_mappings/get_safe_posture_type_runtime_mapping'; + +export const getBenchmarksData = async ( + soClient: SavedObjectsClientContract, + esClient: any, + logger: Logger +): Promise => { + // Returns a list of benchmark based on their Version and Benchmark ID + + const benchmarksResponse = await soClient.find({ + type: CSP_BENCHMARK_RULE_SAVED_OBJECT_TYPE, + aggs: { + benchmark_id: { + terms: { + field: `${CSP_BENCHMARK_RULE_SAVED_OBJECT_TYPE}.attributes.metadata.benchmark.id`, + }, + aggs: { + name: { + terms: { + field: `${CSP_BENCHMARK_RULE_SAVED_OBJECT_TYPE}.attributes.metadata.benchmark.name`, + }, + aggs: { + version: { + terms: { + field: `${CSP_BENCHMARK_RULE_SAVED_OBJECT_TYPE}.attributes.metadata.benchmark.version`, + }, + }, + }, + }, + }, + }, + }, + perPage: 0, + }); + + const benchmarkAgg: any = benchmarksResponse.aggregations; + + const { id: pitId } = await esClient.openPointInTime({ + index: LATEST_FINDINGS_INDEX_DEFAULT_NS, + keep_alive: '30s', + }); + // Transform response to a benchmark row: {id, name, version} + // For each Benchmark entry : Calculate Score, Get amount of enrolled agents + const result = await Promise.all( + benchmarkAgg.benchmark_id.buckets.flatMap(async (benchmark: any) => { + const benchmarkId = benchmark.key; + const benchmarkName = benchmark.name.buckets[0].key; + + const benchmarksTableObjects = await Promise.all( + benchmark?.name?.buckets[0]?.version?.buckets.flatMap(async (benchmarkObj: any) => { + const benchmarkVersion = benchmarkObj.key; + const postureType = + benchmarkId === 'cis_eks' || benchmarkId === 'cis_k8s' ? 'kspm' : 'cspm'; + const runtimeMappings: MappingRuntimeFields = getSafePostureTypeRuntimeMapping(); + const query: QueryDslQueryContainer = { + bool: { + filter: [ + { term: { 'rule.benchmark.id': benchmarkId } }, + { term: { 'rule.benchmark.version': benchmarkVersion } }, + { term: { safe_posture_type: postureType } }, + ], + }, + }; + const benchmarkScore = await getStats(esClient, query, pitId, runtimeMappings, logger); + const benchmarkEvaluation = await getClusters( + esClient, + query, + pitId, + runtimeMappings, + logger + ); + + return { + id: benchmarkId, + name: benchmarkName, + version: benchmarkVersion.replace('v', ''), + score: benchmarkScore, + evaluation: benchmarkEvaluation.length, + }; + }) + ); + + return benchmarksTableObjects; + }) + ); + return result.flat(); +}; + +export const getBenchmarks = async ( + esClient: any, + soClient: SavedObjectsClientContract, + logger: Logger +) => { + const benchmarks = await getBenchmarksData(soClient, esClient, logger); + const getBenchmarkResponse = { + items: benchmarks, + }; + return getBenchmarkResponse; +}; diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/compliance_dashboard.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/compliance_dashboard.ts index 88c7afd5aca11..614f85dcf9374 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/compliance_dashboard.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/compliance_dashboard.ts @@ -15,7 +15,7 @@ import type { ComplianceDashboardData, GetComplianceDashboardRequest, ComplianceDashboardDataV2, -} from '../../../common/types'; +} from '../../../common/types_old'; import { LATEST_FINDINGS_INDEX_DEFAULT_NS, STATS_ROUTE_PATH } from '../../../common/constants'; import { getGroupedFindingsEvaluation } from './get_grouped_findings_evaluation'; import { ClusterWithoutTrend, getClusters } from './get_clusters'; diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_benchmarks.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_benchmarks.ts index b1f8335a61866..130af8d20a2b8 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_benchmarks.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_benchmarks.ts @@ -13,7 +13,7 @@ import type { } from '@elastic/elasticsearch/lib/api/types'; import type { Logger } from '@kbn/core/server'; import { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types'; -import type { BenchmarkData } from '../../../common/types'; +import type { BenchmarkData } from '../../../common/types_old'; import { failedFindingsAggQuery, BenchmarkVersionQueryResult, diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.ts index 51d5c71673ed1..e793863621340 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_clusters.ts @@ -16,7 +16,7 @@ import type { import type { Logger } from '@kbn/core/server'; import { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types'; import { CspFinding } from '../../../common/schemas/csp_finding'; -import type { Cluster } from '../../../common/types'; +import type { Cluster } from '../../../common/types_old'; import { getPostureStatsFromAggs, failedFindingsAggQuery } from './get_grouped_findings_evaluation'; import type { FailedFindingsQueryResult } from './get_grouped_findings_evaluation'; import { findingsEvaluationAggsQuery, getStatsFromFindingsEvaluationsAggs } from './get_stats'; diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_grouped_findings_evaluation.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_grouped_findings_evaluation.ts index 74b239f14d242..ec915d0d62330 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_grouped_findings_evaluation.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_grouped_findings_evaluation.ts @@ -14,7 +14,7 @@ import type { import type { Logger } from '@kbn/core/server'; import { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types'; import { calculatePostureScore } from '../../../common/utils/helpers'; -import type { ComplianceDashboardData } from '../../../common/types'; +import type { ComplianceDashboardData } from '../../../common/types_old'; import { KeyDocCount } from './compliance_dashboard'; export interface FailedFindingsQueryResult { diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_stats.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_stats.ts index f639f8a7e1421..e02b355f27e18 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_stats.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_stats.ts @@ -10,7 +10,7 @@ import type { QueryDslQueryContainer, SearchRequest } from '@elastic/elasticsear import { MappingRuntimeFields } from '@elastic/elasticsearch/lib/api/types'; import type { Logger } from '@kbn/core/server'; import { calculatePostureScore } from '../../../common/utils/helpers'; -import type { ComplianceDashboardData } from '../../../common/types'; +import type { ComplianceDashboardData } from '../../../common/types_old'; export interface FindingsEvaluationsQueryResult { failed_findings: { diff --git a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_trends.ts b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_trends.ts index 00acd14d960fa..f0369dd3a562a 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_trends.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/compliance_dashboard/get_trends.ts @@ -7,10 +7,12 @@ import { ElasticsearchClient, Logger } from '@kbn/core/server'; import { calculatePostureScore } from '../../../common/utils/helpers'; -import { BENCHMARK_SCORE_INDEX_DEFAULT_NS } from '../../../common/constants'; -import type { PosturePolicyTemplate, Stats } from '../../../common/types'; +import { + BENCHMARK_SCORE_INDEX_DEFAULT_NS, + CSPM_FINDINGS_STATS_INTERVAL, +} from '../../../common/constants'; +import type { PosturePolicyTemplate, Stats } from '../../../common/types_old'; import { toBenchmarkDocFieldKey } from '../../lib/mapping_field_util'; -import { CSPM_FINDINGS_STATS_INTERVAL } from '../../tasks/findings_stats_task'; interface FindingsDetails { total_findings: number; diff --git a/x-pack/plugins/cloud_security_posture/server/routes/csp_rule_template/get_csp_rule_template.ts b/x-pack/plugins/cloud_security_posture/server/routes/csp_rule_template/get_csp_rule_template.ts deleted file mode 100644 index 0b249e9f3656e..0000000000000 --- a/x-pack/plugins/cloud_security_posture/server/routes/csp_rule_template/get_csp_rule_template.ts +++ /dev/null @@ -1,131 +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 { NewPackagePolicy } from '@kbn/fleet-plugin/common'; -import { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; -import { transformError } from '@kbn/securitysolution-es-utils'; -import semverCompare from 'semver/functions/compare'; -import semverValid from 'semver/functions/valid'; -import { GetCspRuleTemplateRequest, GetCspRuleTemplateResponse } from '../../../common/types'; -import { CspRuleTemplate } from '../../../common/schemas'; -import { findCspRuleTemplateRequest } from '../../../common/schemas/csp_rule_template_api/get_csp_rule_template'; -import { getBenchmarkFromPackagePolicy, getBenchmarkFilter } from '../../../common/utils/helpers'; - -import { - CSP_RULE_TEMPLATE_SAVED_OBJECT_TYPE, - FIND_CSP_RULE_TEMPLATE_ROUTE_PATH, -} from '../../../common/constants'; -import { CspRouter } from '../../types'; -import { PACKAGE_POLICY_SAVED_OBJECT_TYPE } from '../benchmarks/benchmarks'; - -export const getSortedCspRulesTemplates = (cspRulesTemplates: CspRuleTemplate[]) => { - return cspRulesTemplates.slice().sort((a, b) => { - const ruleNumberA = a?.metadata?.benchmark?.rule_number; - const ruleNumberB = b?.metadata?.benchmark?.rule_number; - - const versionA = semverValid(ruleNumberA); - const versionB = semverValid(ruleNumberB); - - if (versionA !== null && versionB !== null) { - return semverCompare(versionA, versionB); - } else { - return String(ruleNumberA).localeCompare(String(ruleNumberB)); - } - }); -}; - -const getBenchmarkIdFromPackagePolicyId = async ( - soClient: SavedObjectsClientContract, - packagePolicyId: string -): Promise => { - const res = await soClient.get( - PACKAGE_POLICY_SAVED_OBJECT_TYPE, - packagePolicyId - ); - return getBenchmarkFromPackagePolicy(res.attributes.inputs); -}; - -const findCspRuleTemplateHandler = async ( - soClient: SavedObjectsClientContract, - options: GetCspRuleTemplateRequest -): Promise => { - if ( - (!options.packagePolicyId && !options.benchmarkId) || - (options.packagePolicyId && options.benchmarkId) - ) { - throw new Error('Please provide either benchmarkId or packagePolicyId, but not both'); - } - - const benchmarkId = options.benchmarkId - ? options.benchmarkId - : await getBenchmarkIdFromPackagePolicyId(soClient, options.packagePolicyId!); - - const cspRulesTemplatesSo = await soClient.find({ - type: CSP_RULE_TEMPLATE_SAVED_OBJECT_TYPE, - searchFields: options.searchFields, - search: options.search ? `"${options.search}"*` : '', - page: options.page, - perPage: options.perPage, - sortField: options.sortField, - fields: options?.fields, - filter: getBenchmarkFilter(benchmarkId, options.section), - }); - - const cspRulesTemplates = cspRulesTemplatesSo.saved_objects.map( - (cspRuleTemplate) => cspRuleTemplate.attributes - ); - - // Semantic version sorting using semver for valid versions and custom comparison for invalid versions - const sortedCspRulesTemplates = getSortedCspRulesTemplates(cspRulesTemplates); - - return { - items: sortedCspRulesTemplates, - total: cspRulesTemplatesSo.total, - page: options.page, - perPage: options.perPage, - }; -}; - -export const defineFindCspRuleTemplateRoute = (router: CspRouter) => - router.versioned - .get({ - access: 'internal', - path: FIND_CSP_RULE_TEMPLATE_ROUTE_PATH, - }) - .addVersion( - { - version: '1', - validate: { - request: { - query: findCspRuleTemplateRequest, - }, - }, - }, - async (context, request, response) => { - if (!(await context.fleet).authz.fleet.all) { - return response.forbidden(); - } - - const requestBody: GetCspRuleTemplateRequest = request.query; - const cspContext = await context.csp; - - try { - const cspRulesTemplates: GetCspRuleTemplateResponse = await findCspRuleTemplateHandler( - cspContext.soClient, - requestBody - ); - return response.ok({ body: cspRulesTemplates }); - } catch (err) { - const error = transformError(err); - cspContext.logger.error(`Failed to fetch csp rules templates ${err}`); - return response.customError({ - body: { message: error.message }, - statusCode: error.statusCode, - }); - } - } - ); diff --git a/x-pack/plugins/cloud_security_posture/server/routes/setup_routes.ts b/x-pack/plugins/cloud_security_posture/server/routes/setup_routes.ts index a0e33bce73d3f..88570781ed066 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/setup_routes.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/setup_routes.ts @@ -7,6 +7,7 @@ import type { CoreSetup, Logger } from '@kbn/core/server'; import type { AuthenticatedUser } from '@kbn/security-plugin/common'; +import { INTERNAL_CSP_SETTINGS_SAVED_OBJECT_TYPE } from '../../common/constants'; import type { CspRequestHandlerContext, CspServerPluginStart, @@ -17,8 +18,9 @@ import { defineGetComplianceDashboardRoute } from './compliance_dashboard/compli import { defineGetVulnerabilitiesDashboardRoute } from './vulnerabilities_dashboard/vulnerabilities_dashboard'; import { defineGetBenchmarksRoute } from './benchmarks/benchmarks'; import { defineGetCspStatusRoute } from './status/status'; -import { defineFindCspRuleTemplateRoute } from './csp_rule_template/get_csp_rule_template'; +import { defineFindCspBenchmarkRuleRoute } from './benchmark_rules/find/find'; import { defineGetDetectionEngineAlertsStatus } from './detection_engine/get_detection_engine_alerts_count_by_rule_tags'; +import { defineBulkActionCspBenchmarkRulesRoute } from './benchmark_rules/bulk_action/bulk_action'; /** * 1. Registers routes @@ -38,8 +40,9 @@ export async function setupRoutes({ defineGetVulnerabilitiesDashboardRoute(router); defineGetBenchmarksRoute(router); defineGetCspStatusRoute(router); - defineFindCspRuleTemplateRoute(router); + defineFindCspBenchmarkRuleRoute(router); defineGetDetectionEngineAlertsStatus(router); + defineBulkActionCspBenchmarkRulesRoute(router); core.http.registerRouteHandlerContext( PLUGIN_ID, @@ -61,6 +64,9 @@ export async function setupRoutes({ logger, esClient: coreContext.elasticsearch.client, soClient: coreContext.savedObjects.client, + encryptedSavedObjects: coreContext.savedObjects.getClient({ + includedHiddenTypes: [INTERNAL_CSP_SETTINGS_SAVED_OBJECT_TYPE], + }), agentPolicyService: fleet.agentPolicyService, agentService: fleet.agentService, packagePolicyService: fleet.packagePolicyService, diff --git a/x-pack/plugins/cloud_security_posture/server/routes/status/status.ts b/x-pack/plugins/cloud_security_posture/server/routes/status/status.ts index ed3bcd99746fc..df2fc0d36fc63 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/status/status.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/status/status.ts @@ -44,7 +44,7 @@ import type { CspStatusCode, IndexStatus, PostureTypes, -} from '../../../common/types'; +} from '../../../common/types_old'; import { getAgentStatusesByAgentPolicies, getCspAgentPolicies, diff --git a/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_top_patchable_vulnerabilities.ts b/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_top_patchable_vulnerabilities.ts index 6eec795e2dad6..3f4db5e90a526 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_top_patchable_vulnerabilities.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_top_patchable_vulnerabilities.ts @@ -7,7 +7,7 @@ import { SearchRequest } from '@elastic/elasticsearch/lib/api/types'; import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; -import { AggFieldBucket, PatchableVulnerabilityStat } from '../../../common/types'; +import { AggFieldBucket, PatchableVulnerabilityStat } from '../../../common/types_old'; import { LATEST_VULNERABILITIES_INDEX_DEFAULT_NS } from '../../../common/constants'; interface VulnerabilityBucket { diff --git a/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_top_vulnerabilities.ts b/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_top_vulnerabilities.ts index cd8c5b64f532d..8efb0fd33c61e 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_top_vulnerabilities.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_top_vulnerabilities.ts @@ -7,7 +7,7 @@ import { SearchRequest } from '@elastic/elasticsearch/lib/api/types'; import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; -import { VulnerabilityStat } from '../../../common/types'; +import { VulnerabilityStat } from '../../../common/types_old'; import { LATEST_VULNERABILITIES_INDEX_DEFAULT_NS } from '../../../common/constants'; interface VulnerabilityBucket { diff --git a/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_top_vulnerable_resources.ts b/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_top_vulnerable_resources.ts index 0a41a11aeedea..50f54f69e0a64 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_top_vulnerable_resources.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_top_vulnerable_resources.ts @@ -7,7 +7,7 @@ import { SearchRequest } from '@elastic/elasticsearch/lib/api/types'; import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; -import { AggFieldBucket, VulnerableResourceStat } from '../../../common/types'; +import { AggFieldBucket, VulnerableResourceStat } from '../../../common/types_old'; import { LATEST_VULNERABILITIES_INDEX_DEFAULT_NS } from '../../../common/constants'; interface ResourceBucket { diff --git a/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_vulnerabilities_trend.ts b/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_vulnerabilities_trend.ts index a3e335734e74c..5a619b125deab 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_vulnerabilities_trend.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/get_vulnerabilities_trend.ts @@ -7,7 +7,7 @@ import { ElasticsearchClient } from '@kbn/core/server'; import { BENCHMARK_SCORE_INDEX_DEFAULT_NS } from '../../../common/constants'; -import { VulnStatsTrend } from '../../../common/types'; +import { VulnStatsTrend } from '../../../common/types_old'; interface LastDocBucket { key_as_string: string; diff --git a/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/vulnerabilities_dashboard.ts b/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/vulnerabilities_dashboard.ts index 7f676226559aa..f7de7f1be4b65 100644 --- a/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/vulnerabilities_dashboard.ts +++ b/x-pack/plugins/cloud_security_posture/server/routes/vulnerabilities_dashboard/vulnerabilities_dashboard.ts @@ -7,7 +7,7 @@ import { transformError } from '@kbn/securitysolution-es-utils'; import { getVulnerabilitiesTrends } from './get_vulnerabilities_trend'; -import type { CnvmDashboardData } from '../../../common/types'; +import type { CnvmDashboardData } from '../../../common/types_old'; import { VULNERABILITIES_DASHBOARD_ROUTE_PATH } from '../../../common/constants'; import { CspRouter } from '../../types'; import { getVulnerabilitiesStatistics } from './get_vulnerabilities_statistics'; diff --git a/x-pack/plugins/cloud_security_posture/server/saved_objects/csp_benchmark_rule.ts b/x-pack/plugins/cloud_security_posture/server/saved_objects/csp_benchmark_rule.ts new file mode 100644 index 0000000000000..ebf90aa500097 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/server/saved_objects/csp_benchmark_rule.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SECURITY_SOLUTION_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; +import { SavedObjectsType } from '@kbn/core/server'; +import { rulesV1, rulesV2, rulesV3 } from '../../common/types'; +import { CSP_BENCHMARK_RULE_SAVED_OBJECT_TYPE } from '../../common/constants'; +import { cspBenchmarkRuleMigrations } from './migrations'; +import { cspBenchmarkRuleSavedObjectMapping } from './mappings'; + +export const cspBenchmarkRule: SavedObjectsType = { + name: CSP_BENCHMARK_RULE_SAVED_OBJECT_TYPE, + indexPattern: SECURITY_SOLUTION_SAVED_OBJECT_INDEX, + hidden: false, + namespaceType: 'agnostic', + management: { + importableAndExportable: true, + visibleInManagement: true, + }, + schemas: { + '8.3.0': rulesV1.cspBenchmarkRuleSchema, + '8.4.0': rulesV2.cspBenchmarkRuleSchema, + '8.7.0': rulesV3.cspBenchmarkRuleSchema, + }, + migrations: cspBenchmarkRuleMigrations, + mappings: cspBenchmarkRuleSavedObjectMapping, +}; diff --git a/x-pack/plugins/cloud_security_posture/server/saved_objects/csp_settings.ts b/x-pack/plugins/cloud_security_posture/server/saved_objects/csp_settings.ts new file mode 100644 index 0000000000000..d163ca6e26f05 --- /dev/null +++ b/x-pack/plugins/cloud_security_posture/server/saved_objects/csp_settings.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { SavedObjectsType } from '@kbn/core/server'; +import { SECURITY_SOLUTION_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; +import { cspSettingsSchema } from '../../common/types/rules/v3'; +import { cspSettingsSavedObjectMapping } from './mappings'; +import { INTERNAL_CSP_SETTINGS_SAVED_OBJECT_TYPE } from '../../common/constants'; + +export const cspSettings: SavedObjectsType = { + name: INTERNAL_CSP_SETTINGS_SAVED_OBJECT_TYPE, + indexPattern: SECURITY_SOLUTION_SAVED_OBJECT_INDEX, + hidden: true, + namespaceType: 'agnostic', + schemas: { + '8.12.0': cspSettingsSchema, + }, + mappings: cspSettingsSavedObjectMapping, +}; diff --git a/x-pack/plugins/cloud_security_posture/server/saved_objects/index.ts b/x-pack/plugins/cloud_security_posture/server/saved_objects/index.ts index 7217fd8606ed9..98b08c2fad402 100644 --- a/x-pack/plugins/cloud_security_posture/server/saved_objects/index.ts +++ b/x-pack/plugins/cloud_security_posture/server/saved_objects/index.ts @@ -5,4 +5,5 @@ * 2.0. */ -export { setupSavedObjects } from './saved_objects'; +export { cspBenchmarkRule } from './csp_benchmark_rule'; +export { cspSettings } from './csp_settings'; diff --git a/x-pack/plugins/cloud_security_posture/server/saved_objects/mappings.ts b/x-pack/plugins/cloud_security_posture/server/saved_objects/mappings.ts index f4befec2e4811..e2e880f72a7c9 100644 --- a/x-pack/plugins/cloud_security_posture/server/saved_objects/mappings.ts +++ b/x-pack/plugins/cloud_security_posture/server/saved_objects/mappings.ts @@ -7,7 +7,7 @@ import { SavedObjectsTypeMappingDefinition } from '@kbn/core/server'; -export const cspRuleTemplateSavedObjectMapping: SavedObjectsTypeMappingDefinition = { +export const cspBenchmarkRuleSavedObjectMapping: SavedObjectsTypeMappingDefinition = { dynamic: false, properties: { metadata: { @@ -60,3 +60,8 @@ export const cspRuleTemplateSavedObjectMapping: SavedObjectsTypeMappingDefinitio }, }, }; + +export const cspSettingsSavedObjectMapping: SavedObjectsTypeMappingDefinition = { + dynamic: false, + properties: {}, +}; diff --git a/x-pack/plugins/cloud_security_posture/server/saved_objects/migrations/csp_rule_template.ts b/x-pack/plugins/cloud_security_posture/server/saved_objects/migrations/csp_benchmark_rule.ts similarity index 68% rename from x-pack/plugins/cloud_security_posture/server/saved_objects/migrations/csp_rule_template.ts rename to x-pack/plugins/cloud_security_posture/server/saved_objects/migrations/csp_benchmark_rule.ts index f6f59b21b4a68..e440342d2083d 100644 --- a/x-pack/plugins/cloud_security_posture/server/saved_objects/migrations/csp_rule_template.ts +++ b/x-pack/plugins/cloud_security_posture/server/saved_objects/migrations/csp_benchmark_rule.ts @@ -10,16 +10,12 @@ import { SavedObjectUnsanitizedDoc, SavedObjectMigrationContext, } from '@kbn/core/server'; -import { - CspRuleTemplateV830, - CspRuleTemplateV840, - CspRuleTemplateV870, -} from '../../../common/schemas/csp_rule_template'; +import { rulesV1, rulesV2, rulesV3 } from '../../../common/types'; -function migrateCspRuleTemplatesToV840( - doc: SavedObjectUnsanitizedDoc, +function migrateCspBenchmarkRuleToV840( + doc: SavedObjectUnsanitizedDoc, context: SavedObjectMigrationContext -): SavedObjectUnsanitizedDoc { +): SavedObjectUnsanitizedDoc { const { enabled, muted, benchmark, ...metadata } = doc.attributes; return { ...doc, @@ -37,10 +33,10 @@ function migrateCspRuleTemplatesToV840( }; } -function migrateCspRuleTemplatesToV870( - doc: SavedObjectUnsanitizedDoc, +function migrateCspBenchmarkRuleToV870( + doc: SavedObjectUnsanitizedDoc, context: SavedObjectMigrationContext -): SavedObjectUnsanitizedDoc { +): SavedObjectUnsanitizedDoc { // Keeps only metadata, deprecated state const { muted, enabled, ...attributes } = doc.attributes; @@ -59,7 +55,7 @@ function migrateCspRuleTemplatesToV870( }; } -export const cspRuleTemplateMigrations: SavedObjectMigrationMap = { - '8.4.0': migrateCspRuleTemplatesToV840, - '8.7.0': migrateCspRuleTemplatesToV870, +export const cspBenchmarkRuleMigrations: SavedObjectMigrationMap = { + '8.4.0': migrateCspBenchmarkRuleToV840, + '8.7.0': migrateCspBenchmarkRuleToV870, }; diff --git a/x-pack/plugins/cloud_security_posture/server/saved_objects/migrations/index.ts b/x-pack/plugins/cloud_security_posture/server/saved_objects/migrations/index.ts index 991b7469a9129..595a9566f9b77 100644 --- a/x-pack/plugins/cloud_security_posture/server/saved_objects/migrations/index.ts +++ b/x-pack/plugins/cloud_security_posture/server/saved_objects/migrations/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export * from './csp_rule_template'; +export * from './csp_benchmark_rule'; diff --git a/x-pack/plugins/cloud_security_posture/server/saved_objects/saved_objects.ts b/x-pack/plugins/cloud_security_posture/server/saved_objects/saved_objects.ts deleted file mode 100644 index e218808caa420..0000000000000 --- a/x-pack/plugins/cloud_security_posture/server/saved_objects/saved_objects.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; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { SavedObjectsServiceSetup } from '@kbn/core/server'; -import { SECURITY_SOLUTION_SAVED_OBJECT_INDEX } from '@kbn/core-saved-objects-server'; -import { cspRuleTemplateSavedObjectMapping } from './mappings'; -import { cspRuleTemplateMigrations } from './migrations'; -import { - cspRuleTemplateSchemaV830, - cspRuleTemplateSchemaV840, - cspRuleTemplateSchemaV870, - CspRuleTemplate, -} from '../../common/schemas'; - -import { CSP_RULE_TEMPLATE_SAVED_OBJECT_TYPE } from '../../common/constants'; - -export function setupSavedObjects(savedObjects: SavedObjectsServiceSetup) { - savedObjects.registerType({ - name: CSP_RULE_TEMPLATE_SAVED_OBJECT_TYPE, - indexPattern: SECURITY_SOLUTION_SAVED_OBJECT_INDEX, - hidden: false, - namespaceType: 'agnostic', - management: { - importableAndExportable: true, - visibleInManagement: true, - }, - schemas: { - '8.3.0': cspRuleTemplateSchemaV830, - '8.4.0': cspRuleTemplateSchemaV840, - '8.7.0': cspRuleTemplateSchemaV870, - }, - migrations: cspRuleTemplateMigrations, - mappings: cspRuleTemplateSavedObjectMapping, - }); -} diff --git a/x-pack/plugins/cloud_security_posture/server/tasks/findings_stats_task.ts b/x-pack/plugins/cloud_security_posture/server/tasks/findings_stats_task.ts index c157e8081546a..93886c8f8acca 100644 --- a/x-pack/plugins/cloud_security_posture/server/tasks/findings_stats_task.ts +++ b/x-pack/plugins/cloud_security_posture/server/tasks/findings_stats_task.ts @@ -19,6 +19,7 @@ import { getIdentifierRuntimeMapping } from '../../common/runtime_mappings/get_i import { FindingsStatsTaskResult, ScoreByPolicyTemplateBucket, VulnSeverityAggs } from './types'; import { BENCHMARK_SCORE_INDEX_DEFAULT_NS, + CSPM_FINDINGS_STATS_INTERVAL, LATEST_FINDINGS_INDEX_DEFAULT_NS, LATEST_VULNERABILITIES_INDEX_DEFAULT_NS, VULNERABILITIES_SEVERITY, @@ -36,7 +37,6 @@ import { toBenchmarkMappingFieldKey } from '../lib/mapping_field_util'; const CSPM_FINDINGS_STATS_TASK_ID = 'cloud_security_posture-findings_stats'; const CSPM_FINDINGS_STATS_TASK_TYPE = 'cloud_security_posture-stats_task'; -export const CSPM_FINDINGS_STATS_INTERVAL = 5; export async function scheduleFindingsStatsTask( taskManager: TaskManagerStartContract, diff --git a/x-pack/plugins/cloud_security_posture/server/types.ts b/x-pack/plugins/cloud_security_posture/server/types.ts index 1b3c4fd840347..b6e83939b6ca7 100644 --- a/x-pack/plugins/cloud_security_posture/server/types.ts +++ b/x-pack/plugins/cloud_security_posture/server/types.ts @@ -34,7 +34,7 @@ import type { import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server'; import type { FleetStartContract, FleetRequestHandlerContext } from '@kbn/fleet-plugin/server'; import { SecurityPluginSetup, SecurityPluginStart } from '@kbn/security-plugin/server'; -import { CspStatusCode, IndexDetails } from '../common/types'; +import { CspStatusCode, IndexDetails } from '../common/types_old'; // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface CspServerPluginSetup {} @@ -69,6 +69,7 @@ export interface CspApiRequestHandlerContext { logger: Logger; esClient: IScopedClusterClient; soClient: SavedObjectsClientContract; + encryptedSavedObjects: SavedObjectsClientContract; agentPolicyService: AgentPolicyServiceInterface; agentService: AgentService; packagePolicyService: PackagePolicyClient; diff --git a/x-pack/plugins/cloud_security_posture/tsconfig.json b/x-pack/plugins/cloud_security_posture/tsconfig.json index 48fc1a30594f5..ad1a97748967f 100755 --- a/x-pack/plugins/cloud_security_posture/tsconfig.json +++ b/x-pack/plugins/cloud_security_posture/tsconfig.json @@ -60,7 +60,7 @@ "@kbn/core-http-server-mocks", "@kbn/field-formats-plugin", "@kbn/data-view-field-editor-plugin", - "@kbn/securitysolution-grouping" + "@kbn/securitysolution-grouping", ], "exclude": ["target/**/*"] } diff --git a/x-pack/plugins/custom_branding/public/plugin.ts b/x-pack/plugins/custom_branding/public/plugin.ts index fdf6c48d72275..64ba0213d2934 100644 --- a/x-pack/plugins/custom_branding/public/plugin.ts +++ b/x-pack/plugins/custom_branding/public/plugin.ts @@ -5,17 +5,28 @@ * 2.0. */ -import { CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; -import { CustomBrandingPluginSetup, CustomBrandingPluginStart } from './types'; +import { Plugin } from '@kbn/core/public'; +import type { + CustomBrandingPublicSetup, + CustomBrandingPublicStart, + CustomBrandingPublicSetupDependencies, + CustomBrandingPublicStartDependencies, +} from './types'; export class CustomBrandingPlugin - implements Plugin + implements + Plugin< + CustomBrandingPublicSetup, + CustomBrandingPublicStart, + CustomBrandingPublicSetupDependencies, + CustomBrandingPublicStartDependencies + > { - public setup(core: CoreSetup): CustomBrandingPluginSetup { + public setup(): CustomBrandingPublicSetup { return {}; } - public start(core: CoreStart): CustomBrandingPluginStart { + public start(): CustomBrandingPublicStart { return {}; } diff --git a/x-pack/plugins/custom_branding/public/types.ts b/x-pack/plugins/custom_branding/public/types.ts index 398b585a9b99b..bb4ff4c85f854 100644 --- a/x-pack/plugins/custom_branding/public/types.ts +++ b/x-pack/plugins/custom_branding/public/types.ts @@ -5,6 +5,13 @@ * 2.0. */ // eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface CustomBrandingPluginSetup {} +export interface CustomBrandingPublicSetup {} + // eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface CustomBrandingPluginStart {} +export interface CustomBrandingPublicStart {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface CustomBrandingPublicSetupDependencies {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface CustomBrandingPublicStartDependencies {} diff --git a/x-pack/plugins/custom_branding/server/plugin.ts b/x-pack/plugins/custom_branding/server/plugin.ts index c4f88dff41c64..81bab17d432f5 100755 --- a/x-pack/plugins/custom_branding/server/plugin.ts +++ b/x-pack/plugins/custom_branding/server/plugin.ts @@ -20,7 +20,12 @@ import { License } from '@kbn/license-api-guard-plugin/server'; import { CustomBranding } from '@kbn/core-custom-branding-common'; import { Subscription } from 'rxjs'; import { PLUGIN } from '../common/constants'; -import { Dependencies } from './types'; +import type { + CustomBrandingServerSetup, + CustomBrandingServerStart, + CustomBrandingServerStartDependencies, + CustomBrandingServerSetupDependencies, +} from './types'; import { registerUiSettings } from './ui_settings'; const settingsKeys: Array = [ @@ -31,7 +36,15 @@ const settingsKeys: Array = [ 'pageTitle', ]; -export class CustomBrandingPlugin implements Plugin { +export class CustomBrandingPlugin + implements + Plugin< + CustomBrandingServerSetup, + CustomBrandingServerStart, + CustomBrandingServerSetupDependencies, + CustomBrandingServerStartDependencies + > +{ private readonly license: License; private readonly logger: Logger; private licensingSubscription?: Subscription; @@ -73,7 +86,7 @@ export class CustomBrandingPlugin implements Plugin { return {}; } - public start(core: CoreStart, { licensing }: Dependencies) { + public start(_core: CoreStart, { licensing }: CustomBrandingServerStartDependencies) { this.logger.debug('customBranding: Started'); this.license.start({ pluginId: PLUGIN.ID, diff --git a/x-pack/plugins/custom_branding/server/types.ts b/x-pack/plugins/custom_branding/server/types.ts index c40f49ef1acba..ca9edfc13b23a 100755 --- a/x-pack/plugins/custom_branding/server/types.ts +++ b/x-pack/plugins/custom_branding/server/types.ts @@ -12,7 +12,16 @@ import { import { CustomRequestHandlerContext } from '@kbn/core-http-request-handler-context-server'; import { IRouter } from '@kbn/core/server'; -export interface Dependencies { +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface CustomBrandingServerSetup {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface CustomBrandingServerStart {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface CustomBrandingServerSetupDependencies {} + +export interface CustomBrandingServerStartDependencies { licensing: LicensingPluginStart; } diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/document_count_content/document_count_chart/document_count_chart.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/document_count_content/document_count_chart/document_count_chart.tsx index 60e0c765853da..bc79f7ec908ab 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/document_count_content/document_count_chart/document_count_chart.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/document_count_content/document_count_chart/document_count_chart.tsx @@ -20,7 +20,7 @@ import { XYBrushEvent, } from '@elastic/charts'; import moment from 'moment'; -import { IUiSettingsClient } from '@kbn/core/public'; +import { getTimeZone } from '@kbn/visualization-utils'; import { MULTILAYER_TIME_AXIS_STYLE } from '@kbn/charts-plugin/common'; import type { LogRateHistogramItem } from '@kbn/aiops-utils'; @@ -38,16 +38,6 @@ interface Props { const SPEC_ID = 'document_count'; -function getTimezone(uiSettings: IUiSettingsClient) { - if (uiSettings.isDefault('dateFormat:tz')) { - const detectedTimezone = moment.tz.guess(); - if (detectedTimezone) return detectedTimezone; - else return moment().format('Z'); - } else { - return uiSettings.get('dateFormat:tz', 'Browser'); - } -} - export function LoadingSpinner() { return ( @@ -68,7 +58,6 @@ export const DocumentCountChart: FC = ({ services: { data, uiSettings, fieldFormats, charts }, } = useDataVisualizerKibana(); - const chartTheme = charts.theme.useChartsTheme(); const chartBaseTheme = charts.theme.useChartsBaseTheme(); const xAxisFormatter = fieldFormats.deserialize({ id: 'date' }); @@ -126,7 +115,7 @@ export const DocumentCountChart: FC = ({ timefilterUpdateHandler(range); }; - const timeZone = getTimezone(uiSettings); + const timeZone = getTimeZone(uiSettings); return ( = ({ diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/boolean_content.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/boolean_content.tsx index 32bf2d29a8a6c..51af8c47cc684 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/boolean_content.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/field_data_expanded_row/boolean_content.tsx @@ -7,7 +7,7 @@ import React, { FC, useMemo } from 'react'; import { EuiSpacer } from '@elastic/eui'; -import { Axis, BarSeries, Chart, Settings, ScaleType } from '@elastic/charts'; +import { Axis, BarSeries, Chart, Settings, ScaleType, LEGACY_LIGHT_THEME } from '@elastic/charts'; import { FormattedMessage } from '@kbn/i18n-react'; import { roundToDecimalPlace } from '@kbn/ml-number-utils'; @@ -74,7 +74,13 @@ export const BooleanContent: FC = ({ config, onAddFilter }) = tickFormat={(d: any) => getFormattedValue(d, count)} /> - + = ({ {!isUnsupportedChartData(chartData) && data.length > 0 && ( i)} theme={{ chartMargins: zeroSize, diff --git a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/metric_distribution_chart/metric_distribution_chart.tsx b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/metric_distribution_chart/metric_distribution_chart.tsx index 179968580653b..0ea2bff99ae0f 100644 --- a/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/metric_distribution_chart/metric_distribution_chart.tsx +++ b/x-pack/plugins/data_visualizer/public/application/common/components/stats_table/components/metric_distribution_chart/metric_distribution_chart.tsx @@ -19,6 +19,7 @@ import { Settings, TooltipHeaderFormatter, Tooltip, + LEGACY_LIGHT_THEME, } from '@elastic/charts'; import { MetricDistributionChartTooltipHeader } from './metric_distribution_chart_tooltip_header'; @@ -84,7 +85,12 @@ export const MetricDistributionChart: FC = ({ > - + - + - + - + ; + export const integrationIconRt = rt.intersection([ rt.type({ path: rt.string, @@ -39,20 +41,23 @@ export const integrationRt = rt.intersection([ title: rt.string, version: rt.string, icons: rt.array(integrationIconRt), + datasets: rt.record(rt.string, rt.string), }), ]); -export const malformedDocsRt = rt.type({ +export type Integration = rt.TypeOf; + +export const degradedDocsRt = rt.type({ dataset: rt.string, percentage: rt.number, }); -export type MalformedDocs = rt.TypeOf; +export type DegradedDocs = rt.TypeOf; export const getDataStreamsStatsResponseRt = rt.exact( rt.intersection([ rt.type({ - dataStreamsStats: rt.array(datasetStatRt), + dataStreamsStats: rt.array(dataStreamStatRt), }), rt.type({ integrations: rt.array(integrationRt), @@ -60,8 +65,8 @@ export const getDataStreamsStatsResponseRt = rt.exact( ]) ); -export const getDataStreamsMalformedDocsStatsResponseRt = rt.exact( +export const getDataStreamsDegradedDocsStatsResponseRt = rt.exact( rt.type({ - malformedDocs: rt.array(malformedDocsRt), + degradedDocs: rt.array(degradedDocsRt), }) ); diff --git a/x-pack/plugins/dataset_quality/common/data_streams_stats/data_stream_stat.ts b/x-pack/plugins/dataset_quality/common/data_streams_stats/data_stream_stat.ts index 96035415b3649..6f806be70c25b 100644 --- a/x-pack/plugins/dataset_quality/common/data_streams_stats/data_stream_stat.ts +++ b/x-pack/plugins/dataset_quality/common/data_streams_stats/data_stream_stat.ts @@ -9,33 +9,39 @@ import { Integration } from './integration'; import { DataStreamStatType, IntegrationType } from './types'; export class DataStreamStat { + rawName: string; name: DataStreamStatType['name']; + namespace: string; title: string; size?: DataStreamStatType['size']; - sizeBytes?: DataStreamStatType['size_bytes']; - lastActivity?: DataStreamStatType['last_activity']; + sizeBytes?: DataStreamStatType['sizeBytes']; + lastActivity?: DataStreamStatType['lastActivity']; integration?: IntegrationType; - malformedDocs?: number; + degradedDocs?: number; private constructor(dataStreamStat: DataStreamStat) { + this.rawName = dataStreamStat.rawName; this.name = dataStreamStat.name; this.title = dataStreamStat.title ?? dataStreamStat.name; + this.namespace = dataStreamStat.namespace; this.size = dataStreamStat.size; this.sizeBytes = dataStreamStat.sizeBytes; this.lastActivity = dataStreamStat.lastActivity; this.integration = dataStreamStat.integration; - this.malformedDocs = dataStreamStat.malformedDocs; + this.degradedDocs = dataStreamStat.degradedDocs; } public static create(dataStreamStat: DataStreamStatType) { const [_type, dataset, namespace] = dataStreamStat.name.split('-'); const dataStreamStatProps = { - name: dataStreamStat.name, - title: `${dataset}-${namespace}`, + rawName: dataStreamStat.name, + name: dataset, + title: dataStreamStat.integration?.datasets?.[dataset] ?? dataset, + namespace, size: dataStreamStat.size, - sizeBytes: dataStreamStat.size_bytes, - lastActivity: dataStreamStat.last_activity, + sizeBytes: dataStreamStat.sizeBytes, + lastActivity: dataStreamStat.lastActivity, integration: dataStreamStat.integration ? Integration.create(dataStreamStat.integration) : undefined, diff --git a/x-pack/plugins/dataset_quality/common/data_streams_stats/malformed_docs_stat.ts b/x-pack/plugins/dataset_quality/common/data_streams_stats/malformed_docs_stat.ts index d48e4452b548f..b198f45be7101 100644 --- a/x-pack/plugins/dataset_quality/common/data_streams_stats/malformed_docs_stat.ts +++ b/x-pack/plugins/dataset_quality/common/data_streams_stats/malformed_docs_stat.ts @@ -5,18 +5,18 @@ * 2.0. */ -import { MalformedDocsStatType } from './types'; +import { DegradedDocsStatType } from './types'; -export class MalformedDocsStat { - dataset: MalformedDocsStatType['dataset']; - percentage: MalformedDocsStatType['percentage']; +export class DegradedDocsStat { + dataset: DegradedDocsStatType['dataset']; + percentage: DegradedDocsStatType['percentage']; - private constructor(malformedDocsStat: MalformedDocsStat) { - this.dataset = malformedDocsStat.dataset; - this.percentage = malformedDocsStat.percentage; + private constructor(degradedDocsStat: DegradedDocsStat) { + this.dataset = degradedDocsStat.dataset; + this.percentage = degradedDocsStat.percentage; } - public static create(malformedDocsStat: MalformedDocsStatType) { - return new MalformedDocsStat(malformedDocsStat); + public static create(degradedDocsStat: DegradedDocsStatType) { + return new DegradedDocsStat(degradedDocsStat); } } diff --git a/x-pack/plugins/dataset_quality/common/data_streams_stats/types.ts b/x-pack/plugins/dataset_quality/common/data_streams_stats/types.ts index 586732f0899c9..1db25258a04f8 100644 --- a/x-pack/plugins/dataset_quality/common/data_streams_stats/types.ts +++ b/x-pack/plugins/dataset_quality/common/data_streams_stats/types.ts @@ -19,10 +19,10 @@ export type DataStreamStatType = GetDataStreamsStatsResponse['dataStreamsStats'] integration?: IntegrationType; }; -export type GetDataStreamsMalformedDocsStatsParams = - APIClientRequestParamsOf<`GET /internal/dataset_quality/data_streams/malformed_docs`>['params']; -export type GetDataStreamsMalformedDocsStatsQuery = GetDataStreamsMalformedDocsStatsParams['query']; -export type GetDataStreamsMalformedDocsStatsResponse = - APIReturnType<`GET /internal/dataset_quality/data_streams/malformed_docs`>; -export type DataStreamMalformedDocsStatServiceResponse = MalformedDocsStatType[]; -export type MalformedDocsStatType = GetDataStreamsMalformedDocsStatsResponse['malformedDocs'][0]; +export type GetDataStreamsDegradedDocsStatsParams = + APIClientRequestParamsOf<`GET /internal/dataset_quality/data_streams/degraded_docs`>['params']; +export type GetDataStreamsDegradedDocsStatsQuery = GetDataStreamsDegradedDocsStatsParams['query']; +export type GetDataStreamsDegradedDocsStatsResponse = + APIReturnType<`GET /internal/dataset_quality/data_streams/degraded_docs`>; +export type DataStreamDegradedDocsStatServiceResponse = DegradedDocsStatType[]; +export type DegradedDocsStatType = GetDataStreamsDegradedDocsStatsResponse['degradedDocs'][0]; diff --git a/x-pack/plugins/dataset_quality/common/translations.ts b/x-pack/plugins/dataset_quality/common/translations.ts index b26b7ca5c9029..596d1f38ab70c 100644 --- a/x-pack/plugins/dataset_quality/common/translations.ts +++ b/x-pack/plugins/dataset_quality/common/translations.ts @@ -11,10 +11,6 @@ export const datasetQualityAppTitle = i18n.translate('xpack.datasetQuality.appTi defaultMessage: 'Datasets', }); -export const onboardingLinkTitle = i18n.translate('xpack.datasetQuality.onboardingLinkTitle', { - defaultMessage: 'Add data', -}); - export const noDatasetsDescription = i18n.translate('xpack.datasetQuality.noDatasetsDescription', { defaultMessage: 'Try adjusting your time or filter.', }); diff --git a/x-pack/plugins/dataset_quality/public/components/dataset_quality/columns.tsx b/x-pack/plugins/dataset_quality/public/components/dataset_quality/columns.tsx index cccd4d3b12bb7..cdda4dfa004b1 100644 --- a/x-pack/plugins/dataset_quality/public/components/dataset_quality/columns.tsx +++ b/x-pack/plugins/dataset_quality/public/components/dataset_quality/columns.tsx @@ -5,52 +5,68 @@ * 2.0. */ -import React from 'react'; import { + EuiBadge, EuiBasicTableColumn, EuiCode, EuiFlexGroup, EuiFlexItem, EuiIcon, EuiSkeletonRectangle, - EuiText, EuiToolTip, } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { PackageIcon } from '@kbn/fleet-plugin/public'; -import { ES_FIELD_TYPES, KBN_FIELD_TYPES } from '@kbn/field-types'; import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; +import { ES_FIELD_TYPES, KBN_FIELD_TYPES } from '@kbn/field-types'; +import { PackageIcon } from '@kbn/fleet-plugin/public'; +import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import React from 'react'; import { DEGRADED_QUALITY_MINIMUM_PERCENTAGE, POOR_QUALITY_MINIMUM_PERCENTAGE, } from '../../../common/constants'; import { DataStreamStat } from '../../../common/data_streams_stats/data_stream_stat'; import loggingIcon from '../../icons/logging.svg'; +import { LogExplorerLink } from '../log_explorer_link'; import { QualityIndicator, QualityPercentageIndicator } from '../quality_indicator'; const nameColumnName = i18n.translate('xpack.datasetQuality.nameColumnName', { defaultMessage: 'Dataset Name', }); +const namespaceColumnName = i18n.translate('xpack.datasetQuality.namespaceColumnName', { + defaultMessage: 'Namespace', +}); + const sizeColumnName = i18n.translate('xpack.datasetQuality.sizeColumnName', { defaultMessage: 'Size', }); -const malformedDocsColumnName = i18n.translate('xpack.datasetQuality.malformedDocsColumnName', { - defaultMessage: 'Malformed Docs', +const degradedDocsColumnName = i18n.translate('xpack.datasetQuality.degradedDocsColumnName', { + defaultMessage: 'Degraded Docs', +}); + +const lastActivityColumnName = i18n.translate('xpack.datasetQuality.lastActivityColumnName', { + defaultMessage: 'Last Activity', }); -const malformedDocsDescription = (minimimPercentage: number) => - i18n.translate('xpack.datasetQuality.malformedDocsQualityDescription', { +const actionsColumnName = i18n.translate('xpack.datasetQuality.actionsColumnName', { + defaultMessage: 'Actions', +}); +const openActionName = i18n.translate('xpack.datasetQuality.openActionName', { + defaultMessage: 'Open', +}); + +const degradedDocsDescription = (minimimPercentage: number) => + i18n.translate('xpack.datasetQuality.degradedDocsQualityDescription', { defaultMessage: 'greater than {minimimPercentage}%', values: { minimimPercentage }, }); -const malformedDocsColumnTooltip = ( +const degradedDocsColumnTooltip = ( @@ -60,22 +76,19 @@ const malformedDocsColumnTooltip = ( visualQueue: ( - - - {` ${malformedDocsDescription(POOR_QUALITY_MINIMUM_PERCENTAGE)}`} - + - - - {` ${malformedDocsDescription(DEGRADED_QUALITY_MINIMUM_PERCENTAGE)}`} - + - - - {' 0%'} - + ), @@ -83,16 +96,12 @@ const malformedDocsColumnTooltip = ( /> ); -const lastActivityColumnName = i18n.translate('xpack.datasetQuality.lastActivityColumnName', { - defaultMessage: 'Last Activity', -}); - export const getDatasetQualitTableColumns = ({ fieldFormats, - loadingMalformedStats, + loadingDegradedStats, }: { fieldFormats: FieldFormatsStart; - loadingMalformedStats?: boolean; + loadingDegradedStats?: boolean; }): Array> => { return [ { @@ -122,6 +131,14 @@ export const getDatasetQualitTableColumns = ({ ); }, }, + { + name: namespaceColumnName, + field: 'namespace', + sortable: true, + render: (_, dataStreamStat: DataStreamStat) => ( + {dataStreamStat.namespace} + ), + }, { name: sizeColumnName, field: 'size', @@ -129,28 +146,25 @@ export const getDatasetQualitTableColumns = ({ }, { name: ( - + - {`${malformedDocsColumnName} `} + {`${degradedDocsColumnName} `} ), - field: 'malformedDocs', + field: 'degradedDocs', sortable: true, render: (_, dataStreamStat: DataStreamStat) => ( - - - - {`${dataStreamStat.malformedDocs}%`} + ), @@ -164,5 +178,12 @@ export const getDatasetQualitTableColumns = ({ .convert(timestamp), sortable: true, }, + { + name: actionsColumnName, + render: (dataStreamStat: DataStreamStat) => ( + + ), + width: '100px', + }, ]; }; diff --git a/x-pack/plugins/dataset_quality/public/components/dataset_quality/header.tsx b/x-pack/plugins/dataset_quality/public/components/dataset_quality/header.tsx index 5126a645f7b6f..23753941cbe49 100644 --- a/x-pack/plugins/dataset_quality/public/components/dataset_quality/header.tsx +++ b/x-pack/plugins/dataset_quality/public/components/dataset_quality/header.tsx @@ -5,45 +5,10 @@ * 2.0. */ +import { EuiPageHeader } from '@elastic/eui'; import React from 'react'; -import { EuiPageHeader, EuiButton } from '@elastic/eui'; -import { - ObservabilityOnboardingLocatorParams, - OBSERVABILITY_ONBOARDING_LOCATOR, -} from '@kbn/deeplinks-observability'; -import { datasetQualityAppTitle, onboardingLinkTitle } from '../../../common/translations'; -import { useKibanaContextForPlugin } from '../../utils'; +import { datasetQualityAppTitle } from '../../../common/translations'; export function Header() { - const { - services: { share }, - } = useKibanaContextForPlugin(); - - const OnboardingLink = React.memo(() => { - const locator = share.url.locators.get( - OBSERVABILITY_ONBOARDING_LOCATOR - ); - - const onboardingUrl = locator?.getRedirectUrl({}); - - return ( - - {onboardingLinkTitle} - - ); - }); - - return ( - ]} - /> - ); + return ; } diff --git a/x-pack/plugins/dataset_quality/public/components/log_explorer_link.tsx b/x-pack/plugins/dataset_quality/public/components/log_explorer_link.tsx new file mode 100644 index 0000000000000..11227d0a6bd3a --- /dev/null +++ b/x-pack/plugins/dataset_quality/public/components/log_explorer_link.tsx @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiLink } from '@elastic/eui'; +import React from 'react'; +import { getRouterLinkProps } from '@kbn/router-utils'; +import { + SingleDatasetLocatorParams, + SINGLE_DATASET_LOCATOR_ID, +} from '@kbn/deeplinks-observability'; +import { DataStreamStat } from '../../common/data_streams_stats/data_stream_stat'; +import { useKibanaContextForPlugin } from '../utils'; + +export const LogExplorerLink = React.memo( + ({ dataStreamStat, title }: { dataStreamStat: DataStreamStat; title: string }) => { + const { + services: { share }, + } = useKibanaContextForPlugin(); + const params: SingleDatasetLocatorParams = { + dataset: dataStreamStat.name, + timeRange: { + from: 'now-1d', + to: 'now', + }, + integration: dataStreamStat.integration?.name, + filterControls: { + namespace: { + mode: 'include', + values: [dataStreamStat.namespace], + }, + }, + }; + + const singleDatasetLocator = + share.url.locators.get(SINGLE_DATASET_LOCATOR_ID); + + const urlToLogExplorer = singleDatasetLocator?.getRedirectUrl(params); + + const navigateToLogExplorer = () => { + singleDatasetLocator?.navigate(params) as Promise; + }; + + const logExplorerLinkProps = getRouterLinkProps({ + href: urlToLogExplorer, + onClick: navigateToLogExplorer, + }); + + return {title}; + } +); diff --git a/x-pack/plugins/dataset_quality/public/components/quality_indicator/indicator.tsx b/x-pack/plugins/dataset_quality/public/components/quality_indicator/indicator.tsx index a356ff204425f..8344f046e0210 100644 --- a/x-pack/plugins/dataset_quality/public/components/quality_indicator/indicator.tsx +++ b/x-pack/plugins/dataset_quality/public/components/quality_indicator/indicator.tsx @@ -5,17 +5,21 @@ * 2.0. */ -import { EuiIcon, useEuiTheme } from '@elastic/eui'; -import React from 'react'; - -export function QualityIndicator({ quality }: { quality: 'good' | 'degraded' | 'poor' }) { - const { euiTheme } = useEuiTheme(); +import { EuiHealth } from '@elastic/eui'; +import React, { ReactNode } from 'react'; +export function QualityIndicator({ + quality, + description, +}: { + quality: 'good' | 'degraded' | 'poor'; + description: string | ReactNode; +}) { const qualityColors = { - poor: euiTheme.colors.dangerText, - degraded: euiTheme.colors.warningText, - good: euiTheme.colors.successText, + poor: 'danger', + degraded: 'warning', + good: 'success', }; - return ; + return {description}; } diff --git a/x-pack/plugins/dataset_quality/public/components/quality_indicator/percentage_indicator.tsx b/x-pack/plugins/dataset_quality/public/components/quality_indicator/percentage_indicator.tsx index 5f446a0367c3c..f5034bddab8c2 100644 --- a/x-pack/plugins/dataset_quality/public/components/quality_indicator/percentage_indicator.tsx +++ b/x-pack/plugins/dataset_quality/public/components/quality_indicator/percentage_indicator.tsx @@ -5,7 +5,8 @@ * 2.0. */ -import { isNil } from 'lodash'; +import { EuiText } from '@elastic/eui'; +import { FormattedNumber } from '@kbn/i18n-react'; import React from 'react'; import { DEGRADED_QUALITY_MINIMUM_PERCENTAGE, @@ -13,11 +14,7 @@ import { } from '../../../common/constants'; import { QualityIndicator } from './indicator'; -export function QualityPercentageIndicator({ percentage }: { percentage?: number }) { - if (isNil(percentage)) { - return <>; - } - +export function QualityPercentageIndicator({ percentage = 0 }: { percentage?: number }) { const quality = percentage > POOR_QUALITY_MINIMUM_PERCENTAGE ? 'poor' @@ -25,5 +22,11 @@ export function QualityPercentageIndicator({ percentage }: { percentage?: number ? 'degraded' : 'good'; - return ; + const description = ( + + % + + ); + + return ; } diff --git a/x-pack/plugins/dataset_quality/public/hooks/use_dataset_quality_table.tsx b/x-pack/plugins/dataset_quality/public/hooks/use_dataset_quality_table.tsx index bf7afdc9d95e2..c68fb3a47bd1d 100644 --- a/x-pack/plugins/dataset_quality/public/hooks/use_dataset_quality_table.tsx +++ b/x-pack/plugins/dataset_quality/public/hooks/use_dataset_quality_table.tsx @@ -20,6 +20,7 @@ type DIRECTION = 'asc' | 'desc'; type SORT_FIELD = keyof DataStreamStat; const sortingOverrides: Partial<{ [key in SORT_FIELD]: SORT_FIELD }> = { + ['title']: 'name', ['size']: 'sizeBytes', }; @@ -36,9 +37,9 @@ export const useDatasetQualityTable = () => { const { dataStreamsStatsServiceClient: client } = useDatasetQualityContext(); const { data = [], loading } = useFetcher(async () => client.getDataStreamsStats(), []); - const { data: malformedStats = [], loading: loadingMalformedStats } = useFetcher( + const { data: degradedStats = [], loading: loadingDegradedStats } = useFetcher( async () => - client.getDataStreamsMalformedStats({ + client.getDataStreamsDegradedStats({ start: defaultTimeRange.from, end: defaultTimeRange.to, }), @@ -46,8 +47,8 @@ export const useDatasetQualityTable = () => { ); const columns = useMemo( - () => getDatasetQualitTableColumns({ fieldFormats, loadingMalformedStats }), - [fieldFormats, loadingMalformedStats] + () => getDatasetQualitTableColumns({ fieldFormats, loadingDegradedStats }), + [fieldFormats, loadingDegradedStats] ); const pagination = { @@ -77,18 +78,18 @@ export const useDatasetQualityTable = () => { const renderedItems = useMemo(() => { const overridenSortingField = sortingOverrides[sortField] || sortField; const mergedData = data.map((dataStream) => { - const malformedDocs = find(malformedStats, { dataset: dataStream.name }); + const degradedDocs = find(degradedStats, { dataset: dataStream.rawName }); return { ...dataStream, - malformedDocs: malformedDocs?.percentage, + degradedDocs: degradedDocs?.percentage, }; }); const sortedItems = orderBy(mergedData, overridenSortingField, sortDirection); return sortedItems.slice(pageIndex * pageSize, (pageIndex + 1) * pageSize); - }, [data, malformedStats, sortField, sortDirection, pageIndex, pageSize]); + }, [data, degradedStats, sortField, sortDirection, pageIndex, pageSize]); const resultsCount = useMemo(() => { const startNumberItemsOnPage = pageSize * pageIndex + (renderedItems.length ? 1 : 0); diff --git a/x-pack/plugins/dataset_quality/public/services/data_streams_stats/data_streams_stats_client.ts b/x-pack/plugins/dataset_quality/public/services/data_streams_stats/data_streams_stats_client.ts index 21a4b7b7dc2ff..5fe6f9ab28231 100644 --- a/x-pack/plugins/dataset_quality/public/services/data_streams_stats/data_streams_stats_client.ts +++ b/x-pack/plugins/dataset_quality/public/services/data_streams_stats/data_streams_stats_client.ts @@ -9,13 +9,13 @@ import { HttpStart } from '@kbn/core/public'; import { decodeOrThrow } from '@kbn/io-ts-utils'; import { find, merge } from 'lodash'; import { - getDataStreamsMalformedDocsStatsResponseRt, + getDataStreamsDegradedDocsStatsResponseRt, getDataStreamsStatsResponseRt, } from '../../../common/api_types'; import { DataStreamStatServiceResponse, - GetDataStreamsMalformedDocsStatsQuery, - GetDataStreamsMalformedDocsStatsResponse, + GetDataStreamsDegradedDocsStatsQuery, + GetDataStreamsDegradedDocsStatsResponse, GetDataStreamsStatsError, GetDataStreamsStatsQuery, GetDataStreamsStatsResponse, @@ -52,10 +52,10 @@ export class DataStreamsStatsClient implements IDataStreamsStatsClient { return mergedDataStreamsStats.map(DataStreamStat.create); } - public async getDataStreamsMalformedStats(params: GetDataStreamsMalformedDocsStatsQuery) { + public async getDataStreamsDegradedStats(params: GetDataStreamsDegradedDocsStatsQuery) { const response = await this.http - .get( - '/internal/dataset_quality/data_streams/malformed_docs', + .get( + '/internal/dataset_quality/data_streams/degraded_docs', { query: { ...params, @@ -65,18 +65,18 @@ export class DataStreamsStatsClient implements IDataStreamsStatsClient { ) .catch((error) => { throw new GetDataStreamsStatsError( - `Failed to fetch data streams malformed stats": ${error}` + `Failed to fetch data streams degraded stats": ${error}` ); }); - const { malformedDocs } = decodeOrThrow( - getDataStreamsMalformedDocsStatsResponseRt, + const { degradedDocs } = decodeOrThrow( + getDataStreamsDegradedDocsStatsResponseRt, (message: string) => new GetDataStreamsStatsError( - `Failed to decode data streams malformed docs stats response: ${message}"` + `Failed to decode data streams degraded docs stats response: ${message}"` ) )(response); - return malformedDocs; + return degradedDocs; } } diff --git a/x-pack/plugins/dataset_quality/public/services/data_streams_stats/types.ts b/x-pack/plugins/dataset_quality/public/services/data_streams_stats/types.ts index 4f79753bc99cd..46c39082ce1d4 100644 --- a/x-pack/plugins/dataset_quality/public/services/data_streams_stats/types.ts +++ b/x-pack/plugins/dataset_quality/public/services/data_streams_stats/types.ts @@ -7,9 +7,9 @@ import { HttpStart } from '@kbn/core/public'; import { - DataStreamMalformedDocsStatServiceResponse, + DataStreamDegradedDocsStatServiceResponse, DataStreamStatServiceResponse, - GetDataStreamsMalformedDocsStatsQuery, + GetDataStreamsDegradedDocsStatsQuery, GetDataStreamsStatsQuery, } from '../../../common/data_streams_stats'; @@ -25,7 +25,7 @@ export interface DataStreamsStatsServiceStartDeps { export interface IDataStreamsStatsClient { getDataStreamsStats(params?: GetDataStreamsStatsQuery): Promise; - getDataStreamsMalformedStats( - params?: GetDataStreamsMalformedDocsStatsQuery - ): Promise; + getDataStreamsDegradedStats( + params?: GetDataStreamsDegradedDocsStatsQuery + ): Promise; } diff --git a/x-pack/plugins/dataset_quality/server/routes/data_streams/get_data_streams/index.ts b/x-pack/plugins/dataset_quality/server/routes/data_streams/get_data_streams/index.ts index 6154e3bc11a24..b79b4eeec0116 100644 --- a/x-pack/plugins/dataset_quality/server/routes/data_streams/get_data_streams/index.ts +++ b/x-pack/plugins/dataset_quality/server/routes/data_streams/get_data_streams/index.ts @@ -6,8 +6,8 @@ */ import type { ElasticsearchClient } from '@kbn/core/server'; +import { DataStreamTypes } from '../../../types/default_api_types'; import { dataStreamService } from '../../../services'; -import { DataStreamTypes } from '../../../types/data_stream'; export async function getDataStreams(options: { esClient: ElasticsearchClient; diff --git a/x-pack/plugins/dataset_quality/server/routes/data_streams/get_data_streams_stats/get_data_streams_stats.test.ts b/x-pack/plugins/dataset_quality/server/routes/data_streams/get_data_streams_stats/get_data_streams_stats.test.ts index 830c3b162573f..a0104afc1d5fe 100644 --- a/x-pack/plugins/dataset_quality/server/routes/data_streams/get_data_streams_stats/get_data_streams_stats.test.ts +++ b/x-pack/plugins/dataset_quality/server/routes/data_streams/get_data_streams_stats/get_data_streams_stats.test.ts @@ -79,32 +79,32 @@ describe('getDataStreams', () => { { name: 'logs-elastic_agent-default', size: '1gb', - size_bytes: 1170805528, - last_activity: 1698916071000, + sizeBytes: 1170805528, + lastActivity: 1698916071000, }, { name: 'logs-elastic_agent.filebeat-default', size: '1.3mb', - size_bytes: 1459100, - last_activity: 1698902209996, + sizeBytes: 1459100, + lastActivity: 1698902209996, }, { name: 'logs-elastic_agent.fleet_server-default', size: '2.9mb', - size_bytes: 3052148, - last_activity: 1698914110010, + sizeBytes: 3052148, + lastActivity: 1698914110010, }, { name: 'logs-elastic_agent.metricbeat-default', size: '1.6mb', - size_bytes: 1704807, - last_activity: 1698672046707, + sizeBytes: 1704807, + lastActivity: 1698672046707, }, { name: 'logs-test.test-default', size: '6.2mb', - size_bytes: 6570447, - last_activity: 1698913802000, + sizeBytes: 6570447, + lastActivity: 1698913802000, }, ]); }); diff --git a/x-pack/plugins/dataset_quality/server/routes/data_streams/get_data_streams_stats/index.ts b/x-pack/plugins/dataset_quality/server/routes/data_streams/get_data_streams_stats/index.ts index 9ec252d096357..a78f2fec53e29 100644 --- a/x-pack/plugins/dataset_quality/server/routes/data_streams/get_data_streams_stats/index.ts +++ b/x-pack/plugins/dataset_quality/server/routes/data_streams/get_data_streams_stats/index.ts @@ -6,8 +6,8 @@ */ import type { ElasticsearchClient } from '@kbn/core/server'; +import { DataStreamTypes } from '../../../types/default_api_types'; import { dataStreamService } from '../../../services'; -import { DataStreamTypes } from '../../../types/data_stream'; export async function getDataStreamsStats(options: { esClient: ElasticsearchClient; @@ -24,9 +24,9 @@ export async function getDataStreamsStats(options: { const mappedDataStreams = matchingDataStreamsStats.map((dataStream) => { return { name: dataStream.data_stream, - size: dataStream.store_size, - size_bytes: dataStream.store_size_bytes, - last_activity: dataStream.maximum_timestamp, + size: dataStream.store_size?.toString(), + sizeBytes: dataStream.store_size_bytes, + lastActivity: dataStream.maximum_timestamp, }; }); diff --git a/x-pack/plugins/dataset_quality/server/routes/data_streams/get_malformed_docs.ts b/x-pack/plugins/dataset_quality/server/routes/data_streams/get_degraded_docs.ts similarity index 81% rename from x-pack/plugins/dataset_quality/server/routes/data_streams/get_malformed_docs.ts rename to x-pack/plugins/dataset_quality/server/routes/data_streams/get_degraded_docs.ts index 155fe981428c7..1af5a603f3638 100644 --- a/x-pack/plugins/dataset_quality/server/routes/data_streams/get_malformed_docs.ts +++ b/x-pack/plugins/dataset_quality/server/routes/data_streams/get_degraded_docs.ts @@ -7,17 +7,17 @@ import type { ElasticsearchClient } from '@kbn/core/server'; import { rangeQuery, termQuery } from '@kbn/observability-plugin/server'; -import { MalformedDocs } from '../../../common/api_types'; +import { DegradedDocs } from '../../../common/api_types'; import { DATA_STREAM_DATASET, DATA_STREAM_NAMESPACE, DATA_STREAM_TYPE, _IGNORED, } from '../../../common/es_fields'; -import { DataStreamTypes } from '../../types/data_stream'; +import { DataStreamTypes } from '../../types/default_api_types'; import { createDatasetQualityESClient, wildcardQuery } from '../../utils'; -export async function getMalformedDocsPaginated(options: { +export async function getDegradedDocsPaginated(options: { esClient: ElasticsearchClient; type?: DataStreamTypes; start: number; @@ -27,8 +27,8 @@ export async function getMalformedDocsPaginated(options: { dataset: string; namespace: string; }; - prevResults?: MalformedDocs[]; -}): Promise { + prevResults?: DegradedDocs[]; +}): Promise { const { esClient, type = 'logs', datasetQuery, start, end, after, prevResults = [] } = options; const datasetQualityESClient = createDatasetQualityESClient(esClient); @@ -61,7 +61,7 @@ export async function getMalformedDocsPaginated(options: { ], }, aggs: { - malformed: { + degraded: { filter: { exists: { field: _IGNORED, @@ -73,16 +73,16 @@ export async function getMalformedDocsPaginated(options: { }, }); - const currMalformedDocs = + const currDegradedDocs = response.aggregations?.datasets.buckets.map((bucket) => ({ dataset: `${type}-${bucket.key.dataset}-${bucket.key.namespace}`, - percentage: (bucket.malformed.doc_count * 100) / bucket.doc_count, + percentage: (bucket.degraded.doc_count * 100) / bucket.doc_count, })) ?? []; - const malformedDocs = [...prevResults, ...currMalformedDocs]; + const degradedDocs = [...prevResults, ...currDegradedDocs]; if (response.aggregations?.datasets.after_key) { - return getMalformedDocsPaginated({ + return getDegradedDocsPaginated({ esClient, type, start, @@ -92,9 +92,9 @@ export async function getMalformedDocsPaginated(options: { dataset: response.aggregations?.datasets.after_key.dataset as string, namespace: response.aggregations?.datasets.after_key.namespace as string, }, - prevResults: malformedDocs, + prevResults: degradedDocs, }); } - return malformedDocs; + return degradedDocs; } diff --git a/x-pack/plugins/dataset_quality/server/routes/data_streams/get_integrations.ts b/x-pack/plugins/dataset_quality/server/routes/data_streams/get_integrations.ts new file mode 100644 index 0000000000000..7871571b607b6 --- /dev/null +++ b/x-pack/plugins/dataset_quality/server/routes/data_streams/get_integrations.ts @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { PackageClient } from '@kbn/fleet-plugin/server'; +import { DataStreamStat, Integration } from '../../../common/api_types'; + +export async function getIntegrations(options: { + packageClient: PackageClient; + dataStreams: DataStreamStat[]; +}): Promise { + const { packageClient, dataStreams } = options; + + const packages = await packageClient.getPackages(); + const installedPackages = dataStreams.map((item) => item.integration); + + return Promise.all( + packages + .filter((pkg) => installedPackages.includes(pkg.name)) + .map(async (p) => ({ + name: p.name, + title: p.title, + version: p.version, + icons: p.icons, + datasets: await getDatasets({ + packageClient, + name: p.name, + version: p.version, + }), + })) + ); +} + +const getDatasets = async (options: { + packageClient: PackageClient; + name: string; + version: string; +}) => { + const { packageClient, name, version } = options; + + const pkg = await packageClient.getPackage(name, version); + + return pkg.packageInfo.data_streams?.reduce( + (acc, curr) => ({ + ...acc, + [curr.dataset]: curr.title, + }), + {} + ); +}; diff --git a/x-pack/plugins/dataset_quality/server/routes/data_streams/routes.ts b/x-pack/plugins/dataset_quality/server/routes/data_streams/routes.ts index a70bcc133e100..4f95331d97651 100644 --- a/x-pack/plugins/dataset_quality/server/routes/data_streams/routes.ts +++ b/x-pack/plugins/dataset_quality/server/routes/data_streams/routes.ts @@ -7,20 +7,19 @@ import * as t from 'io-ts'; import { keyBy, merge, values } from 'lodash'; -import { DataStreamStat } from '../../types/data_stream'; -import { dataStreamTypesRt, rangeRt } from '../../types/default_api_types'; -import { Integration } from '../../types/integration'; +import { typeRt, rangeRt } from '../../types/default_api_types'; import { createDatasetQualityServerRoute } from '../create_datasets_quality_server_route'; import { getDataStreams } from './get_data_streams'; import { getDataStreamsStats } from './get_data_streams_stats'; -import { getMalformedDocsPaginated } from './get_malformed_docs'; -import { MalformedDocs } from '../../../common/api_types'; +import { getDegradedDocsPaginated } from './get_degraded_docs'; +import { DegradedDocs, DataStreamStat, Integration } from '../../../common/api_types'; +import { getIntegrations } from './get_integrations'; const statsRoute = createDatasetQualityServerRoute({ endpoint: 'GET /internal/dataset_quality/data_streams/stats', params: t.type({ query: t.intersection([ - dataStreamTypesRt, + typeRt, t.partial({ datasetQuery: t.string, }), @@ -41,7 +40,6 @@ const statsRoute = createDatasetQualityServerRoute({ const fleetPluginStart = await plugins.fleet.start(); const packageClient = fleetPluginStart.packageService.asInternalUser; - const packages = await packageClient.getPackages(); const [dataStreams, dataStreamsStats] = await Promise.all([ getDataStreams({ @@ -52,32 +50,21 @@ const statsRoute = createDatasetQualityServerRoute({ getDataStreamsStats({ esClient, ...params.query }), ]); - const installedPackages = dataStreams.items.map((item) => item.integration); - - const integrations = packages - .filter((pkg) => installedPackages.includes(pkg.name)) - .map((p) => ({ - name: p.name, - title: p.title, - version: p.version, - icons: p.icons, - })); - return { dataStreamsStats: values( merge(keyBy(dataStreams.items, 'name'), keyBy(dataStreamsStats.items, 'name')) ), - integrations, + integrations: await getIntegrations({ packageClient, dataStreams: dataStreams.items }), }; }, }); -const malformedDocsRoute = createDatasetQualityServerRoute({ - endpoint: 'GET /internal/dataset_quality/data_streams/malformed_docs', +const degradedDocsRoute = createDatasetQualityServerRoute({ + endpoint: 'GET /internal/dataset_quality/data_streams/degraded_docs', params: t.type({ query: t.intersection([ rangeRt, - dataStreamTypesRt, + typeRt, t.partial({ datasetQuery: t.string, }), @@ -87,25 +74,25 @@ const malformedDocsRoute = createDatasetQualityServerRoute({ tags: [], }, async handler(resources): Promise<{ - malformedDocs: MalformedDocs[]; + degradedDocs: DegradedDocs[]; }> { const { context, params } = resources; const coreContext = await context.core; const esClient = coreContext.elasticsearch.client.asCurrentUser; - const malformedDocs = await getMalformedDocsPaginated({ + const degradedDocs = await getDegradedDocsPaginated({ esClient, ...params.query, }); return { - malformedDocs, + degradedDocs, }; }, }); export const dataStreamsRouteRepository = { ...statsRoute, - ...malformedDocsRoute, + ...degradedDocsRoute, }; diff --git a/x-pack/plugins/dataset_quality/server/types/data_stream.ts b/x-pack/plugins/dataset_quality/server/types/data_stream.ts deleted file mode 100644 index 1ccac8199a8b6..0000000000000 --- a/x-pack/plugins/dataset_quality/server/types/data_stream.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ByteSize } from '@elastic/elasticsearch/lib/api/types'; -import { Integration } from './integration'; -export interface DataStreamStat { - name: string; - size?: ByteSize; - size_bytes?: number; - last_activity?: number; - integration?: Integration; -} - -export type DataStreamTypes = 'logs' | 'metrics' | 'traces' | 'synthetics' | 'profiling'; diff --git a/x-pack/plugins/dataset_quality/server/types/default_api_types.ts b/x-pack/plugins/dataset_quality/server/types/default_api_types.ts index e36bb1e58f65a..6148832dad140 100644 --- a/x-pack/plugins/dataset_quality/server/types/default_api_types.ts +++ b/x-pack/plugins/dataset_quality/server/types/default_api_types.ts @@ -8,16 +8,21 @@ import * as t from 'io-ts'; import { isoToEpochRt } from '@kbn/io-ts-utils'; -export const dataStreamTypesRt = t.partial({ - type: t.union([ - t.literal('logs'), - t.literal('metrics'), - t.literal('traces'), - t.literal('synthetics'), - t.literal('profiling'), - ]), +// https://github.com/gcanti/io-ts/blob/master/index.md#union-of-string-literals +export const dataStreamTypesRt = t.keyof({ + logs: null, + metrics: null, + traces: null, + synthetics: null, + profiling: null, }); +export const typeRt = t.partial({ + type: dataStreamTypesRt, +}); + +export type DataStreamTypes = t.TypeOf; + export const rangeRt = t.type({ start: isoToEpochRt, end: isoToEpochRt, diff --git a/x-pack/plugins/dataset_quality/tsconfig.json b/x-pack/plugins/dataset_quality/tsconfig.json index 3bfb546ed49ec..65f77bac100aa 100644 --- a/x-pack/plugins/dataset_quality/tsconfig.json +++ b/x-pack/plugins/dataset_quality/tsconfig.json @@ -13,7 +13,6 @@ "@kbn/core", "@kbn/core-plugins-server", "@kbn/core-elasticsearch-server-mocks", - "@kbn/deeplinks-observability", "@kbn/fleet-plugin", "@kbn/observability-shared-plugin", "@kbn/server-route-repository", @@ -26,7 +25,9 @@ "@kbn/field-types", "@kbn/io-ts-utils", "@kbn/observability-plugin", - "@kbn/es-types" + "@kbn/es-types", + "@kbn/deeplinks-observability", + "@kbn/router-utils", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/drilldowns/url_drilldown/kibana.jsonc b/x-pack/plugins/drilldowns/url_drilldown/kibana.jsonc index f201cea6502e9..a7d0217c4e2ee 100644 --- a/x-pack/plugins/drilldowns/url_drilldown/kibana.jsonc +++ b/x-pack/plugins/drilldowns/url_drilldown/kibana.jsonc @@ -9,13 +9,13 @@ "browser": true, "requiredPlugins": [ "embeddable", - "uiActions", "uiActionsEnhanced" ], "requiredBundles": [ "kibanaUtils", "kibanaReact", - "imageEmbeddable" + "imageEmbeddable", + "uiActions" ] } } diff --git a/x-pack/plugins/elastic_assistant/server/__mocks__/request_context.ts b/x-pack/plugins/elastic_assistant/server/__mocks__/request_context.ts index a186b3a19051c..6f68d8f20de32 100644 --- a/x-pack/plugins/elastic_assistant/server/__mocks__/request_context.ts +++ b/x-pack/plugins/elastic_assistant/server/__mocks__/request_context.ts @@ -24,6 +24,7 @@ export const createMockClients = () => { clusterClient: core.elasticsearch.client, elasticAssistant: { actions: actionsClientMock.create(), + getRegisteredTools: jest.fn(), logger: loggingSystemMock.createLogger(), }, savedObjectsClient: core.savedObjects.client, @@ -72,6 +73,7 @@ const createElasticAssistantRequestContextMock = ( ): jest.Mocked => { return { actions: clients.elasticAssistant.actions as unknown as ActionsPluginStart, + getRegisteredTools: jest.fn(), logger: clients.elasticAssistant.logger, }; }; diff --git a/x-pack/plugins/elastic_assistant/server/index.ts b/x-pack/plugins/elastic_assistant/server/index.ts index 0cccc1ba27a7a..1775fc60528e8 100755 --- a/x-pack/plugins/elastic_assistant/server/index.ts +++ b/x-pack/plugins/elastic_assistant/server/index.ts @@ -13,6 +13,10 @@ export async function plugin(initializerContext: PluginInitializerContext) { } export type { - ElasticAssistantPluginSetup as EcsDataQualityDashboardPluginSetup, - ElasticAssistantPluginStart as EcsDataQualityDashboardPluginStart, + ElasticAssistantPluginSetup, + ElasticAssistantPluginStart, + ElasticAssistantPluginSetupDependencies, + ElasticAssistantPluginStartDependencies, + AssistantTool, + AssistantToolParams, } from './types'; diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/execute_custom_llm_chain/index.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/execute_custom_llm_chain/index.ts index 52a066e54de1e..d6868925cc667 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/langchain/execute_custom_llm_chain/index.ts +++ b/x-pack/plugins/elastic_assistant/server/lib/langchain/execute_custom_llm_chain/index.ts @@ -16,7 +16,7 @@ import { KNOWLEDGE_BASE_INDEX_PATTERN } from '../../../routes/knowledge_base/con import type { AgentExecutorParams, AgentExecutorResponse } from '../executors/types'; import { withAssistantSpan } from '../tracers/with_assistant_span'; import { APMTracer } from '../tracers/apm_tracer'; -import { getApplicableTools } from '../tools'; +import { AssistantToolParams } from '../../../types'; export const DEFAULT_AGENT_EXECUTOR_ID = 'Elastic AI Assistant Agent Executor'; @@ -31,6 +31,7 @@ export const callAgentExecutor = async ({ allow, allowReplacement, assistantLangChain, + assistantTools = [], connectorId, elserId, esClient, @@ -71,7 +72,8 @@ export const callAgentExecutor = async ({ // Create a chain that uses the ELSER backed ElasticsearchStore, override k=10 for esql query generation for now const chain = RetrievalQAChain.fromLLM(llm, esStore.asRetriever(10)); - const tools: Tool[] = getApplicableTools({ + // Fetch any applicable tools that the source plugin may have registered + const assistantToolParams: AssistantToolParams = { allow, allowReplacement, alertsIndexPattern, @@ -83,7 +85,8 @@ export const callAgentExecutor = async ({ replacements, request, size, - }); + }; + const tools: Tool[] = assistantTools.flatMap((tool) => tool.getTool(assistantToolParams) ?? []); logger.debug(`applicable tools: ${JSON.stringify(tools.map((t) => t.name).join(', '), null, 2)}`); diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/executors/types.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/executors/types.ts index bd6a38da5cdc4..e7824e2822f8a 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/langchain/executors/types.ts +++ b/x-pack/plugins/elastic_assistant/server/lib/langchain/executors/types.ts @@ -12,6 +12,7 @@ import { Logger } from '@kbn/logging'; import { KibanaRequest } from '@kbn/core-http-server'; import type { LangChainTracer } from 'langchain/callbacks'; import { RequestBody, ResponseBody } from '../types'; +import type { AssistantTool } from '../../../types'; export interface AgentExecutorParams { alertsIndexPattern?: string; @@ -19,6 +20,7 @@ export interface AgentExecutorParams { allow?: string[]; allowReplacement?: string[]; assistantLangChain: boolean; + assistantTools?: AssistantTool[]; connectorId: string; esClient: ElasticsearchClient; kbResource: string | undefined; diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/tools/alert_counts/get_alert_counts_tool.test.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/tools/alert_counts/get_alert_counts_tool.test.ts deleted file mode 100644 index cffe31cbcfd39..0000000000000 --- a/x-pack/plugins/elastic_assistant/server/lib/langchain/tools/alert_counts/get_alert_counts_tool.test.ts +++ /dev/null @@ -1,105 +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 { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; -import type { KibanaRequest } from '@kbn/core-http-server'; -import { DynamicTool } from 'langchain/tools'; -import { omit } from 'lodash/fp'; - -import { getAlertCountsTool } from './get_alert_counts_tool'; -import type { RequestBody } from '../../types'; - -describe('getAlertCountsTool', () => { - const alertsIndexPattern = 'alerts-index'; - const esClient = { - search: jest.fn().mockResolvedValue({}), - } as unknown as ElasticsearchClient; - const replacements = { key: 'value' }; - const request = { - body: { - assistantLangChain: false, - alertsIndexPattern: '.alerts-security.alerts-default', - allow: ['@timestamp', 'cloud.availability_zone', 'user.name'], - allowReplacement: ['user.name'], - replacements, - size: 20, - }, - } as unknown as KibanaRequest; - - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('returns a `DynamicTool` with a `func` that calls `esClient.search()` with the expected query', async () => { - const tool: DynamicTool = getAlertCountsTool({ - alertsIndexPattern, - esClient, - replacements, - request, - }) as DynamicTool; - - await tool.func(''); - - expect(esClient.search).toHaveBeenCalledWith({ - aggs: { statusBySeverity: { terms: { field: 'kibana.alert.severity' } } }, - index: ['alerts-index'], - query: { - bool: { - filter: [ - { - bool: { - filter: [{ match_phrase: { 'kibana.alert.workflow_status': 'open' } }], - must_not: [{ exists: { field: 'kibana.alert.building_block_type' } }], - }, - }, - { range: { '@timestamp': { gte: 'now/d', lte: 'now/d' } } }, - ], - }, - }, - size: 0, - }); - }); - - it('returns null when the request is missing required anonymization parameters', () => { - const requestWithMissingParams = omit('body.allow', request) as unknown as KibanaRequest< - unknown, - unknown, - RequestBody - >; - - const tool = getAlertCountsTool({ - alertsIndexPattern, - esClient, - replacements, - request: requestWithMissingParams, - }); - - expect(tool).toBeNull(); - }); - - it('returns null when the alertsIndexPattern is undefined', () => { - const tool = getAlertCountsTool({ - // alertsIndexPattern is undefined - esClient, - replacements, - request, - }); - - expect(tool).toBeNull(); - }); - - it('returns a tool instance with the expected tags', () => { - const tool = getAlertCountsTool({ - alertsIndexPattern, - esClient, - replacements, - request, - }) as DynamicTool; - - expect(tool.tags).toEqual(['alerts', 'alerts-count']); - }); -}); diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/tools/alert_counts/get_alert_counts_tool.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/tools/alert_counts/get_alert_counts_tool.ts deleted file mode 100644 index 9c5ec2555d2e5..0000000000000 --- a/x-pack/plugins/elastic_assistant/server/lib/langchain/tools/alert_counts/get_alert_counts_tool.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { SearchResponse } from '@elastic/elasticsearch/lib/api/types'; -import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; -import { KibanaRequest } from '@kbn/core-http-server'; -import { DynamicTool, Tool } from 'langchain/tools'; - -import { getAlertsCountQuery } from './get_alert_counts_query'; -import { requestHasRequiredAnonymizationParams } from '../../helpers'; -import type { RequestBody } from '../../types'; - -export const ALERT_COUNTS_TOOL_DESCRIPTION = - 'Call this for the counts of last 24 hours of open alerts in the environment, grouped by their severity'; - -export const getAlertCountsTool = ({ - alertsIndexPattern, - esClient, - replacements, - request, -}: { - alertsIndexPattern?: string; - esClient: ElasticsearchClient; - replacements?: Record; - request: KibanaRequest; -}): Tool | null => { - if (!requestHasRequiredAnonymizationParams(request) || alertsIndexPattern == null) { - return null; - } - - return new DynamicTool({ - name: 'alert-counts', - description: ALERT_COUNTS_TOOL_DESCRIPTION, - func: async () => { - const query = getAlertsCountQuery(alertsIndexPattern); - - const result = await esClient.search(query); - - return JSON.stringify(result); - }, - tags: ['alerts', 'alerts-count'], - }); -}; diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/tools/esql_language_knowledge_base/get_esql_language_knowledge_base_tool.test.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/tools/esql_language_knowledge_base/get_esql_language_knowledge_base_tool.test.ts deleted file mode 100644 index ccd97b7deb088..0000000000000 --- a/x-pack/plugins/elastic_assistant/server/lib/langchain/tools/esql_language_knowledge_base/get_esql_language_knowledge_base_tool.test.ts +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { RetrievalQAChain } from 'langchain/chains'; -import { DynamicTool } from 'langchain/tools'; - -import { getEsqlLanguageKnowledgeBaseTool } from './get_esql_language_knowledge_base_tool'; - -const chain = {} as RetrievalQAChain; - -describe('getEsqlLanguageKnowledgeBaseTool', () => { - it('returns null if assistantLangChain is false', () => { - const tool = getEsqlLanguageKnowledgeBaseTool({ - assistantLangChain: false, - chain, - modelExists: true, - }); - - expect(tool).toBeNull(); - }); - - it('returns null if modelExists is false (the ELSER model is not installed)', () => { - const tool = getEsqlLanguageKnowledgeBaseTool({ - assistantLangChain: true, - chain, - modelExists: false, // <-- ELSER model is not installed - }); - - expect(tool).toBeNull(); - }); - - it('should return a Tool instance if assistantLangChain and modelExists are true', () => { - const tool = getEsqlLanguageKnowledgeBaseTool({ - assistantLangChain: true, - modelExists: true, - chain, - }); - - expect(tool?.name).toEqual('ESQLKnowledgeBaseTool'); - }); - - it('should return a tool with the expected tags', () => { - const tool = getEsqlLanguageKnowledgeBaseTool({ - assistantLangChain: true, - chain, - modelExists: true, - }) as DynamicTool; - - expect(tool.tags).toEqual(['esql', 'query-generation', 'knowledge-base']); - }); -}); diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/tools/esql_language_knowledge_base/get_esql_language_knowledge_base_tool.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/tools/esql_language_knowledge_base/get_esql_language_knowledge_base_tool.ts deleted file mode 100644 index f49551261a79d..0000000000000 --- a/x-pack/plugins/elastic_assistant/server/lib/langchain/tools/esql_language_knowledge_base/get_esql_language_knowledge_base_tool.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { RetrievalQAChain } from 'langchain/chains'; -import { ChainTool, Tool } from 'langchain/tools'; - -export const getEsqlLanguageKnowledgeBaseTool = ({ - assistantLangChain, - modelExists, - chain, -}: { - assistantLangChain: boolean; - chain: RetrievalQAChain; - /** true when the ELSER model is installed */ - modelExists: boolean; -}): Tool | null => - assistantLangChain && modelExists - ? new ChainTool({ - name: 'ESQLKnowledgeBaseTool', - description: - 'Call this for knowledge on how to build an ESQL query, or answer questions about the ES|QL query language.', - chain, - tags: ['esql', 'query-generation', 'knowledge-base'], - }) - : null; diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/tools/index.test.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/tools/index.test.ts deleted file mode 100644 index cb0e79c0558d4..0000000000000 --- a/x-pack/plugins/elastic_assistant/server/lib/langchain/tools/index.test.ts +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; -import { KibanaRequest } from '@kbn/core-http-server'; -import { RetrievalQAChain } from 'langchain/chains'; - -import { RequestBody } from '../types'; -import { getApplicableTools } from '.'; - -describe('getApplicableTools', () => { - const alertsIndexPattern = 'alerts-index'; - const esClient = { - search: jest.fn().mockResolvedValue({}), - } as unknown as ElasticsearchClient; - const modelExists = true; // the ELSER model is installed - const onNewReplacements = jest.fn(); - const replacements = { key: 'value' }; - const request = { - body: { - assistantLangChain: true, - alertsIndexPattern: '.alerts-security.alerts-default', - allow: ['@timestamp', 'cloud.availability_zone', 'user.name'], - allowReplacement: ['user.name'], - replacements, - size: 20, - }, - } as unknown as KibanaRequest; - const chain = {} as unknown as RetrievalQAChain; - - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('should return an array of applicable tools', () => { - const tools = getApplicableTools({ - alertsIndexPattern, - allow: request.body.allow, - allowReplacement: request.body.allowReplacement, - assistantLangChain: request.body.assistantLangChain, - chain, - esClient, - modelExists, - onNewReplacements, - replacements, - request, - size: request.body.size, - }); - - const minExpectedTools = 3; // 3 tools are currently implemented - - expect(tools.length).toBeGreaterThanOrEqual(minExpectedTools); - }); -}); diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/tools/index.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/tools/index.ts deleted file mode 100644 index edc9c264b636a..0000000000000 --- a/x-pack/plugins/elastic_assistant/server/lib/langchain/tools/index.ts +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; -import { KibanaRequest } from '@kbn/core-http-server'; -import { RetrievalQAChain } from 'langchain/chains'; -import { Tool } from 'langchain/tools'; - -import { getAlertCountsTool } from './alert_counts/get_alert_counts_tool'; -import { getEsqlLanguageKnowledgeBaseTool } from './esql_language_knowledge_base/get_esql_language_knowledge_base_tool'; -import { getOpenAlertsTool } from './open_alerts/get_open_alerts_tool'; -import type { RequestBody } from '../types'; - -export interface GetApplicableTools { - alertsIndexPattern?: string; - allow?: string[]; - allowReplacement?: string[]; - assistantLangChain: boolean; - chain: RetrievalQAChain; - esClient: ElasticsearchClient; - modelExists: boolean; - onNewReplacements?: (newReplacements: Record) => void; - replacements?: Record; - request: KibanaRequest; - size?: number; -} - -export const getApplicableTools = ({ - alertsIndexPattern, - allow, - allowReplacement, - assistantLangChain, - chain, - esClient, - modelExists, - onNewReplacements, - replacements, - request, - size, -}: GetApplicableTools): Tool[] => - [ - getEsqlLanguageKnowledgeBaseTool({ assistantLangChain, chain, modelExists }) ?? [], - getAlertCountsTool({ - alertsIndexPattern, - esClient, - replacements, - request, - }) ?? [], - getOpenAlertsTool({ - alertsIndexPattern, - allow, - allowReplacement, - esClient, - onNewReplacements, - replacements, - request, - size, - }) ?? [], - ].flat(); diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/tools/open_alerts/get_open_alerts_tool.test.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/tools/open_alerts/get_open_alerts_tool.test.ts deleted file mode 100644 index 8c996db2d63b4..0000000000000 --- a/x-pack/plugins/elastic_assistant/server/lib/langchain/tools/open_alerts/get_open_alerts_tool.test.ts +++ /dev/null @@ -1,206 +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 { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; -import type { KibanaRequest } from '@kbn/core-http-server'; -import { DynamicTool } from 'langchain/tools'; -import { omit } from 'lodash/fp'; - -import { getOpenAlertsTool } from './get_open_alerts_tool'; -import { mockAlertsFieldsApi } from '../../../../__mocks__/alerts'; -import type { RequestBody } from '../../types'; -import { MAX_SIZE } from './helpers'; - -describe('getOpenAlertsTool', () => { - const alertsIndexPattern = 'alerts-index'; - const esClient = { - search: jest.fn().mockResolvedValue(mockAlertsFieldsApi), - } as unknown as ElasticsearchClient; - const replacements = { key: 'value' }; - const request = { - body: { - assistantLangChain: false, - alertsIndexPattern: '.alerts-security.alerts-default', - allow: ['@timestamp', 'cloud.availability_zone', 'user.name'], - allowReplacement: ['user.name'], - replacements, - size: 20, - }, - } as unknown as KibanaRequest; - - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('returns a `DynamicTool` with a `func` that calls `esClient.search()` with the expected query', async () => { - const tool: DynamicTool = getOpenAlertsTool({ - alertsIndexPattern, - allow: request.body.allow, - allowReplacement: request.body.allowReplacement, - esClient, - onNewReplacements: jest.fn(), - replacements, - request, - size: request.body.size, - }) as DynamicTool; - - await tool.func(''); - - expect(esClient.search).toHaveBeenCalledWith({ - allow_no_indices: true, - body: { - _source: false, - fields: [ - { - field: '@timestamp', - include_unmapped: true, - }, - { - field: 'cloud.availability_zone', - include_unmapped: true, - }, - { - field: 'user.name', - include_unmapped: true, - }, - ], - query: { - bool: { - filter: [ - { - bool: { - filter: [ - { - match_phrase: { - 'kibana.alert.workflow_status': 'open', - }, - }, - { - range: { - '@timestamp': { - format: 'strict_date_optional_time', - gte: 'now-1d/d', - lte: 'now/d', - }, - }, - }, - ], - must: [], - must_not: [ - { - exists: { - field: 'kibana.alert.building_block_type', - }, - }, - ], - should: [], - }, - }, - ], - }, - }, - runtime_mappings: {}, - size: 20, - sort: [ - { - 'kibana.alert.risk_score': { - order: 'desc', - }, - }, - { - '@timestamp': { - order: 'desc', - }, - }, - ], - }, - ignore_unavailable: true, - index: ['alerts-index'], - }); - }); - - it('returns null when the request is missing required anonymization parameters', () => { - const requestWithMissingParams = omit('body.allow', request) as unknown as KibanaRequest< - unknown, - unknown, - RequestBody - >; - - const tool = getOpenAlertsTool({ - alertsIndexPattern, - allow: requestWithMissingParams.body.allow, - allowReplacement: requestWithMissingParams.body.allowReplacement, - esClient, - onNewReplacements: jest.fn(), - replacements, - request: requestWithMissingParams, - size: requestWithMissingParams.body.size, - }); - - expect(tool).toBeNull(); - }); - - it('returns null when alertsIndexPattern is undefined', () => { - const tool = getOpenAlertsTool({ - // alertsIndexPattern is undefined - allow: request.body.allow, - allowReplacement: request.body.allowReplacement, - esClient, - onNewReplacements: jest.fn(), - replacements, - request, - size: request.body.size, - }); - - expect(tool).toBeNull(); - }); - - it('returns null when size is undefined', () => { - const tool = getOpenAlertsTool({ - alertsIndexPattern, - allow: request.body.allow, - allowReplacement: request.body.allowReplacement, - esClient, - onNewReplacements: jest.fn(), - replacements, - request, - // size is undefined - }); - - expect(tool).toBeNull(); - }); - - it('returns null when size out of range', () => { - const tool = getOpenAlertsTool({ - alertsIndexPattern, - allow: request.body.allow, - allowReplacement: request.body.allowReplacement, - esClient, - onNewReplacements: jest.fn(), - replacements, - request, - size: MAX_SIZE + 1, // <-- size is out of range - }); - - expect(tool).toBeNull(); - }); - - it('returns a tool instance with the expected tags', () => { - const tool = getOpenAlertsTool({ - alertsIndexPattern, - allow: request.body.allow, - allowReplacement: request.body.allowReplacement, - esClient, - onNewReplacements: jest.fn(), - replacements, - request, - size: request.body.size, - }) as DynamicTool; - - expect(tool.tags).toEqual(['alerts', 'open-alerts']); - }); -}); diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/tools/open_alerts/get_open_alerts_tool.ts b/x-pack/plugins/elastic_assistant/server/lib/langchain/tools/open_alerts/get_open_alerts_tool.ts deleted file mode 100644 index 755bfa7f9dc3a..0000000000000 --- a/x-pack/plugins/elastic_assistant/server/lib/langchain/tools/open_alerts/get_open_alerts_tool.ts +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import type { SearchResponse } from '@elastic/elasticsearch/lib/api/types'; -import { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; -import { KibanaRequest } from '@kbn/core-http-server'; -import { getAnonymizedValue, transformRawData } from '@kbn/elastic-assistant-common'; -import { DynamicTool, Tool } from 'langchain/tools'; -import { requestHasRequiredAnonymizationParams } from '../../helpers'; -import { RequestBody } from '../../types'; - -import { getOpenAlertsQuery } from './get_open_alerts_query'; -import { getRawDataOrDefault, sizeIsOutOfRange } from './helpers'; - -export const OPEN_ALERTS_TOOL_DESCRIPTION = - 'Call this for knowledge about the latest n open alerts (sorted by `kibana.alert.risk_score`) in the environment, or when answering questions about open alerts'; - -/** - * Returns a tool for querying open alerts, or null if the request - * doesn't have all the required parameters. - */ -export const getOpenAlertsTool = ({ - alertsIndexPattern, - allow, - allowReplacement, - esClient, - onNewReplacements, - replacements, - request, - size, -}: { - alertsIndexPattern?: string; - allow?: string[]; - allowReplacement?: string[]; - esClient: ElasticsearchClient; - onNewReplacements?: (newReplacements: Record) => void; - replacements?: Record; - request: KibanaRequest; - size?: number; -}): Tool | null => { - if ( - !requestHasRequiredAnonymizationParams(request) || - alertsIndexPattern == null || - size == null || - sizeIsOutOfRange(size) - ) { - return null; - } - - return new DynamicTool({ - name: 'open-alerts', - description: OPEN_ALERTS_TOOL_DESCRIPTION, - func: async () => { - const query = getOpenAlertsQuery({ - alertsIndexPattern, - allow: allow ?? [], - size, - }); - - const result = await esClient.search(query); - - // Accumulate replacements locally so we can, for example use the same - // replacement for a hostname when we see it in multiple alerts: - let localReplacements = { ...replacements }; - const localOnNewReplacements = (newReplacements: Record) => { - localReplacements = { ...localReplacements, ...newReplacements }; // update the local state - - onNewReplacements?.(localReplacements); // invoke the callback with the latest replacements - }; - - return JSON.stringify( - result.hits?.hits?.map((x) => - transformRawData({ - allow: allow ?? [], - allowReplacement: allowReplacement ?? [], - currentReplacements: localReplacements, // <-- the latest local replacements - getAnonymizedValue, - onNewReplacements: localOnNewReplacements, // <-- the local callback - rawData: getRawDataOrDefault(x.fields), - }) - ) - ); - }, - tags: ['alerts', 'open-alerts'], - }); -}; diff --git a/x-pack/plugins/elastic_assistant/server/plugin.ts b/x-pack/plugins/elastic_assistant/server/plugin.ts index a0df339695885..bd06165e57284 100755 --- a/x-pack/plugins/elastic_assistant/server/plugin.ts +++ b/x-pack/plugins/elastic_assistant/server/plugin.ts @@ -18,12 +18,14 @@ import { import { once } from 'lodash'; import { + AssistantTool, ElasticAssistantPluginSetup, ElasticAssistantPluginSetupDependencies, ElasticAssistantPluginStart, ElasticAssistantPluginStartDependencies, ElasticAssistantRequestHandlerContext, GetElser, + PLUGIN_ID, } from './types'; import { deleteKnowledgeBaseRoute, @@ -32,6 +34,13 @@ import { postEvaluateRoute, postKnowledgeBaseRoute, } from './routes'; +import { appContextService, GetRegisteredTools } from './services/app_context'; + +interface CreateRouteHandlerContextParams { + core: CoreSetup; + logger: Logger; + getRegisteredTools: GetRegisteredTools; +} export class ElasticAssistantPlugin implements @@ -48,15 +57,20 @@ export class ElasticAssistantPlugin this.logger = initializerContext.logger.get(); } - private createRouteHandlerContext = ( - core: CoreSetup, - logger: Logger - ): IContextProvider => { + private createRouteHandlerContext = ({ + core, + logger, + getRegisteredTools, + }: CreateRouteHandlerContextParams): IContextProvider< + ElasticAssistantRequestHandlerContext, + typeof PLUGIN_ID + > => { return async function elasticAssistantRouteHandlerContext(context, request) { const [_, pluginsStart] = await core.getStartServices(); return { actions: pluginsStart.actions, + getRegisteredTools, logger, }; }; @@ -65,16 +79,15 @@ export class ElasticAssistantPlugin public setup(core: CoreSetup, plugins: ElasticAssistantPluginSetupDependencies) { this.logger.debug('elasticAssistant: Setup'); const router = core.http.createRouter(); - - core.http.registerRouteHandlerContext< - ElasticAssistantRequestHandlerContext, - 'elasticAssistant' - >( - 'elasticAssistant', - this.createRouteHandlerContext( - core as CoreSetup, - this.logger - ) + core.http.registerRouteHandlerContext( + PLUGIN_ID, + this.createRouteHandlerContext({ + core: core as CoreSetup, + logger: this.logger, + getRegisteredTools: (pluginName: string) => { + return appContextService.getRegisteredTools(pluginName); + }, + }) ); const getElserId: GetElser = once( @@ -94,16 +107,45 @@ export class ElasticAssistantPlugin postEvaluateRoute(router, getElserId); return { actions: plugins.actions, + getRegisteredTools: (pluginName: string) => { + return appContextService.getRegisteredTools(pluginName); + }, }; } public start(core: CoreStart, plugins: ElasticAssistantPluginStartDependencies) { this.logger.debug('elasticAssistant: Started'); + appContextService.start({ logger: this.logger }); return { + /** + * Actions plugin start contract + */ actions: plugins.actions, + + /** + * Get the registered tools for a given plugin name. + * @param pluginName + */ + getRegisteredTools: (pluginName: string) => { + return appContextService.getRegisteredTools(pluginName); + }, + + /** + * Register tools to be used by the Elastic Assistant for a given plugin. Use the plugin name that + * corresponds to your application as defined in the `x-kbn-context` header of requests made from your + * application. + * + * @param pluginName + * @param tools + */ + registerTools: (pluginName: string, tools: AssistantTool[]) => { + return appContextService.registerTools(pluginName, tools); + }, }; } - public stop() {} + public stop() { + appContextService.stop(); + } } diff --git a/x-pack/plugins/elastic_assistant/server/routes/evaluate/post_evaluate.ts b/x-pack/plugins/elastic_assistant/server/routes/evaluate/post_evaluate.ts index 39e3233f63744..f5ec4c4555a76 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/evaluate/post_evaluate.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/evaluate/post_evaluate.ts @@ -100,6 +100,11 @@ export const postEvaluateRoute = ( throwIfSystemAction: false, }); + // Fetch any tools registered by the request's originating plugin + const assistantTools = (await context.elasticAssistant).getRegisteredTools( + 'securitySolution' + ); + // Get a scoped esClient for passing to the agents for retrieval, and // writing results to the output index const esClient = (await context.core).elasticsearch.client.asCurrentUser; @@ -142,6 +147,7 @@ export const postEvaluateRoute = ( AGENT_EXECUTOR_MAP[agentName]({ actions, assistantLangChain: true, + assistantTools, connectorId, esClient, elserId, diff --git a/x-pack/plugins/elastic_assistant/server/routes/helpers.test.ts b/x-pack/plugins/elastic_assistant/server/routes/helpers.test.ts new file mode 100644 index 0000000000000..384e1f8865736 --- /dev/null +++ b/x-pack/plugins/elastic_assistant/server/routes/helpers.test.ts @@ -0,0 +1,67 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { KibanaRequest } from '@kbn/core-http-server'; +import type { RequestBody } from '../lib/langchain/types'; + +import { DEFAULT_PLUGIN_NAME, getPluginNameFromRequest } from './helpers'; + +describe('getPluginNameFromRequest', () => { + const contextRequestHeaderEncoded = encodeURIComponent( + JSON.stringify({ + type: 'application', + name: 'superSolution', + url: '/kbn/app/super/rules/id/163fa5a4-d72a-45fa-8142-8edc298ecd17/alerts', + page: 'app', + id: 'new', + }) + ); + + const request = { + headers: { + 'x-kbn-context': contextRequestHeaderEncoded, + }, + } as unknown as KibanaRequest; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('extracts plugin name from "x-kbn-context" request header', async () => { + const pluginName = getPluginNameFromRequest({ + request, + defaultPluginName: DEFAULT_PLUGIN_NAME, + }); + expect(pluginName).toEqual('superSolution'); + }); + + it('fails to extract plugin name from undefined "x-kbn-context" request header, falls back to default provided', async () => { + const invalidRequest = { + headers: { + 'x-kbn-context': undefined, + }, + } as unknown as KibanaRequest; + const pluginName = getPluginNameFromRequest({ + request: invalidRequest, + defaultPluginName: DEFAULT_PLUGIN_NAME, + }); + expect(pluginName).toEqual(DEFAULT_PLUGIN_NAME); + }); + + it('fails to extract plugin name from malformed "x-kbn-context" invalidRequest header, falls back to default provided', async () => { + const invalidRequest = { + headers: { + 'x-kbn-context': 'asdfku', + }, + } as unknown as KibanaRequest; + const pluginName = getPluginNameFromRequest({ + request: invalidRequest, + defaultPluginName: DEFAULT_PLUGIN_NAME, + }); + expect(pluginName).toEqual(DEFAULT_PLUGIN_NAME); + }); +}); diff --git a/x-pack/plugins/elastic_assistant/server/routes/helpers.ts b/x-pack/plugins/elastic_assistant/server/routes/helpers.ts new file mode 100644 index 0000000000000..99d4493c16cca --- /dev/null +++ b/x-pack/plugins/elastic_assistant/server/routes/helpers.ts @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { KibanaRequest } from '@kbn/core-http-server'; +import { Logger } from '@kbn/core/server'; +import { RequestBody } from '../lib/langchain/types'; + +interface GetPluginNameFromRequestParams { + request: KibanaRequest; + defaultPluginName: string; + logger?: Logger; +} + +export const DEFAULT_PLUGIN_NAME = 'securitySolutionUI'; + +/** + * Attempts to extract the plugin name the request originated from using the request headers. + * + * Note from Kibana Core: This is not a 100% fit solution, though, because plugins can run in the background, + * or even use other plugins’ helpers (ie, APM can use the infra helpers to call a third plugin) + * + * Should suffice for our purposes here with where the Elastic Assistant is currently used, but if needing a + * dedicated solution, the core folks said to reach out. + * + * @param logger optional logger to log any errors + * @param defaultPluginName default plugin name to use if unable to determine from request + * @param request Kibana Request + * + * @returns plugin name + */ +export const getPluginNameFromRequest = ({ + logger, + defaultPluginName, + request, +}: GetPluginNameFromRequestParams): string => { + try { + const contextHeader = request.headers['x-kbn-context']; + if (contextHeader != null) { + return JSON.parse( + decodeURIComponent(Array.isArray(contextHeader) ? contextHeader[0] : contextHeader) + )?.name; + } + } catch (err) { + logger?.error( + `Error determining source plugin for selecting tools, using ${defaultPluginName}.` + ); + } + return defaultPluginName; +}; diff --git a/x-pack/plugins/elastic_assistant/server/routes/post_actions_connector_execute.test.ts b/x-pack/plugins/elastic_assistant/server/routes/post_actions_connector_execute.test.ts index 537ead452c8ea..0f7d964eea480 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/post_actions_connector_execute.test.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/post_actions_connector_execute.test.ts @@ -56,6 +56,7 @@ jest.mock('../lib/langchain/execute_custom_llm_chain', () => ({ const mockContext = { elasticAssistant: { actions: jest.fn(), + getRegisteredTools: jest.fn(() => []), logger: loggingSystemMock.createLogger(), }, core: { diff --git a/x-pack/plugins/elastic_assistant/server/routes/post_actions_connector_execute.ts b/x-pack/plugins/elastic_assistant/server/routes/post_actions_connector_execute.ts index ed68f3526a112..9c1d8601da532 100644 --- a/x-pack/plugins/elastic_assistant/server/routes/post_actions_connector_execute.ts +++ b/x-pack/plugins/elastic_assistant/server/routes/post_actions_connector_execute.ts @@ -23,6 +23,7 @@ import { import { ElasticAssistantRequestHandlerContext, GetElser } from '../types'; import { ESQL_RESOURCE } from './knowledge_base/constants'; import { callAgentExecutor } from '../lib/langchain/execute_custom_llm_chain'; +import { DEFAULT_PLUGIN_NAME, getPluginNameFromRequest } from './helpers'; export const postActionsConnectorExecuteRoute = ( router: IRouter, @@ -58,6 +59,14 @@ export const postActionsConnectorExecuteRoute = ( // TODO: Add `traceId` to actions request when calling via langchain logger.debug('Executing via langchain, assistantLangChain: true'); + // Fetch any tools registered by the request's originating plugin + const pluginName = getPluginNameFromRequest({ + request, + defaultPluginName: DEFAULT_PLUGIN_NAME, + logger, + }); + const assistantTools = (await context.elasticAssistant).getRegisteredTools(pluginName); + // get a scoped esClient for assistant memory const esClient = (await context.core).elasticsearch.client.asCurrentUser; @@ -79,6 +88,7 @@ export const postActionsConnectorExecuteRoute = ( allowReplacement: request.body.allowReplacement, actions, assistantLangChain: request.body.assistantLangChain, + assistantTools, connectorId, elserId, esClient, diff --git a/x-pack/plugins/elastic_assistant/server/services/app_context.test.ts b/x-pack/plugins/elastic_assistant/server/services/app_context.test.ts new file mode 100644 index 0000000000000..621995d3452be --- /dev/null +++ b/x-pack/plugins/elastic_assistant/server/services/app_context.test.ts @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { appContextService, ElasticAssistantAppContext } from './app_context'; +import { loggerMock } from '@kbn/logging-mocks'; +import { AssistantTool } from '../types'; + +// Mock Logger +const mockLogger = loggerMock.create(); + +// Mock ElasticAssistantAppContext +const mockAppContext: ElasticAssistantAppContext = { + logger: mockLogger, +}; + +describe('AppContextService', () => { + const toolOne: AssistantTool = { + id: 'tool-one', + name: 'ToolOne', + description: 'Description 1', + sourceRegister: 'Source1', + isSupported: jest.fn(), + getTool: jest.fn(), + }; + const toolTwo: AssistantTool = { + id: 'tool-two', + name: 'ToolTwo', + description: 'Description 2', + sourceRegister: 'Source2', + isSupported: jest.fn(), + getTool: jest.fn(), + }; + + beforeEach(() => { + appContextService.stop(); + jest.clearAllMocks(); + }); + + describe('starting and stopping', () => { + it('should clear registered tools when stopped ', () => { + appContextService.start(mockAppContext); + appContextService.registerTools('super', [toolOne]); + appContextService.stop(); + + expect(appContextService.getRegisteredTools('super').length).toBe(0); + }); + }); + + describe('registering tools', () => { + it('should register and get tools for a single plugin', () => { + const pluginName = 'pluginName'; + + appContextService.start(mockAppContext); + appContextService.registerTools(pluginName, [toolOne, toolTwo]); + + // Check if getRegisteredTools returns the correct tools + const retrievedTools = appContextService.getRegisteredTools(pluginName); + expect(retrievedTools).toEqual([toolOne, toolTwo]); + }); + + it('should register and get tools for multiple plugins', () => { + const pluginOne = 'plugin1'; + const pluginTwo = 'plugin2'; + + appContextService.start(mockAppContext); + appContextService.registerTools(pluginOne, [toolOne]); + appContextService.registerTools(pluginTwo, [toolTwo]); + + expect(appContextService.getRegisteredTools(pluginOne)).toEqual([toolOne]); + expect(appContextService.getRegisteredTools(pluginTwo)).toEqual([toolTwo]); + }); + + it('should not add the same tool twice', () => { + const pluginName = 'pluginName'; + + appContextService.start(mockAppContext); + appContextService.registerTools(pluginName, [toolOne]); + appContextService.registerTools(pluginName, [toolOne]); + + expect(appContextService.getRegisteredTools(pluginName).length).toEqual(1); + }); + }); +}); diff --git a/x-pack/plugins/elastic_assistant/server/services/app_context.ts b/x-pack/plugins/elastic_assistant/server/services/app_context.ts new file mode 100644 index 0000000000000..bd7a7c0cc3203 --- /dev/null +++ b/x-pack/plugins/elastic_assistant/server/services/app_context.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 type { Logger } from '@kbn/core/server'; +import type { AssistantTool } from '../types'; + +export type PluginName = string; +export type RegisteredToolsStorage = Map>; +export type GetRegisteredTools = (pluginName: string) => AssistantTool[]; +export interface ElasticAssistantAppContext { + logger: Logger; +} + +/** + * Service for managing context specific to the Elastic Assistant + * + * Inspired by `AppContextService` impl from fleet plugin: x-pack/plugins/fleet/server/services/app_context.ts + */ +class AppContextService { + private logger: Logger | undefined; + private registeredTools: RegisteredToolsStorage = new Map>(); + + public start(appContext: ElasticAssistantAppContext) { + this.logger = appContext.logger; + } + + public stop() { + this.registeredTools.clear(); + } + + /** + * Register tools to be used by the Elastic Assistant + * + * @param pluginName + * @param tools + */ + public registerTools(pluginName: string, tools: AssistantTool[]) { + this.logger?.debug('AppContextService:registerTools'); + this.logger?.debug(`pluginName: ${pluginName}`); + this.logger?.debug(`tools: ${tools.map((tool) => tool.name).join(', ')}`); + + if (!this.registeredTools.has(pluginName)) { + this.logger?.debug('plugin has no tools, making new set'); + this.registeredTools.set(pluginName, new Set()); + } + tools.forEach((tool) => this.registeredTools.get(pluginName)?.add(tool)); + } + + /** + * Get the registered tools + * + * @param pluginName + */ + public getRegisteredTools(pluginName: string): AssistantTool[] { + const tools = Array.from(this.registeredTools?.get(pluginName) ?? new Set()); + + this.logger?.debug('AppContextService:getRegisteredTools'); + this.logger?.debug(`pluginName: ${pluginName}`); + this.logger?.debug(`tools: ${tools.map((tool) => tool.name).join(', ')}`); + + return tools; + } +} + +export const appContextService = new AppContextService(); diff --git a/x-pack/plugins/elastic_assistant/server/types.ts b/x-pack/plugins/elastic_assistant/server/types.ts index ed9081c084420..5be8a35a275b9 100755 --- a/x-pack/plugins/elastic_assistant/server/types.ts +++ b/x-pack/plugins/elastic_assistant/server/types.ts @@ -16,6 +16,13 @@ import type { SavedObjectsClientContract, } from '@kbn/core/server'; import { type MlPluginSetup } from '@kbn/ml-plugin/server'; +import { Tool } from 'langchain/dist/tools/base'; +import { RetrievalQAChain } from 'langchain/chains'; +import { ElasticsearchClient } from '@kbn/core/server'; +import { RequestBody } from './lib/langchain/types'; +import type { GetRegisteredTools } from './services/app_context'; + +export const PLUGIN_ID = 'elasticAssistant' as const; /** The plugin setup interface */ export interface ElasticAssistantPluginSetup { @@ -25,6 +32,17 @@ export interface ElasticAssistantPluginSetup { /** The plugin start interface */ export interface ElasticAssistantPluginStart { actions: ActionsPluginStart; + /** + * Register tools to be used by the elastic assistant + * @param pluginName Name of the plugin the tool should be registered to + * @param tools AssistantTools to be registered with for the given plugin + */ + registerTools: (pluginName: string, tools: AssistantTool[]) => void; + /** + * Get the registered tools + * @param pluginName Name of the plugin to get the tools for + */ + getRegisteredTools: GetRegisteredTools; } export interface ElasticAssistantPluginSetupDependencies { @@ -37,6 +55,7 @@ export interface ElasticAssistantPluginStartDependencies { export interface ElasticAssistantApiRequestHandlerContext { actions: ActionsPluginStart; + getRegisteredTools: GetRegisteredTools; logger: Logger; } @@ -51,3 +70,30 @@ export type GetElser = ( request: KibanaRequest, savedObjectsClient: SavedObjectsClientContract ) => Promise | never; + +/** + * Interfaces for registering tools to be used by the elastic assistant + */ + +export interface AssistantTool { + id: string; + name: string; + description: string; + sourceRegister: string; + isSupported: (params: AssistantToolParams) => boolean; + getTool: (params: AssistantToolParams) => Tool | null; +} + +export interface AssistantToolParams { + alertsIndexPattern?: string; + allow?: string[]; + allowReplacement?: string[]; + assistantLangChain: boolean; + chain: RetrievalQAChain; + esClient: ElasticsearchClient; + modelExists: boolean; + onNewReplacements?: (newReplacements: Record) => void; + replacements?: Record; + request: KibanaRequest; + size?: number; +} diff --git a/x-pack/plugins/elastic_assistant/tsconfig.json b/x-pack/plugins/elastic_assistant/tsconfig.json index 5cad4d4b52141..53616fc2dc2b0 100644 --- a/x-pack/plugins/elastic_assistant/tsconfig.json +++ b/x-pack/plugins/elastic_assistant/tsconfig.json @@ -21,7 +21,6 @@ "@kbn/securitysolution-io-ts-utils", "@kbn/actions-plugin", "@kbn/elastic-assistant", - "@kbn/elastic-assistant-common", "@kbn/logging-mocks", "@kbn/core-elasticsearch-server-mocks", "@kbn/core-logging-server-mocks", diff --git a/x-pack/plugins/encrypted_saved_objects/server/create_migration.ts b/x-pack/plugins/encrypted_saved_objects/server/create_migration.ts index 6ef6b82d20e0a..c811bf9674716 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/create_migration.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/create_migration.ts @@ -16,6 +16,7 @@ import type { import { EncryptionError } from './crypto'; import type { EncryptedSavedObjectsService, EncryptedSavedObjectTypeRegistration } from './crypto'; import { normalizeNamespace } from './saved_objects'; +import { mapAttributes } from './saved_objects/map_attributes'; type SavedObjectOptionalMigrationFn = ( doc: SavedObjectUnsanitizedDoc | SavedObjectUnsanitizedDoc, @@ -157,9 +158,3 @@ export const getCreateMigration = }); }; }; - -function mapAttributes(obj: SavedObjectUnsanitizedDoc, mapper: (attributes: T) => T) { - return Object.assign(obj, { - attributes: mapper(obj.attributes), - }); -} diff --git a/x-pack/plugins/encrypted_saved_objects/server/create_model_version.test.ts b/x-pack/plugins/encrypted_saved_objects/server/create_model_version.test.ts new file mode 100644 index 0000000000000..89d9cb3e617b5 --- /dev/null +++ b/x-pack/plugins/encrypted_saved_objects/server/create_model_version.test.ts @@ -0,0 +1,756 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { logger } from 'elastic-apm-node'; + +import type { + SavedObjectModelTransformationContext, + SavedObjectsModelUnsafeTransformChange, +} from '@kbn/core-saved-objects-server'; + +import { getCreateEsoModelVersion } from './create_model_version'; +import type { EncryptedSavedObjectTypeRegistration } from './crypto'; +import { EncryptionError, EncryptionErrorOperation } from './crypto'; +import { encryptedSavedObjectsServiceMock } from './crypto/index.mock'; + +describe('create ESO model version', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + const inputType: EncryptedSavedObjectTypeRegistration = { + type: 'known-type-1', + attributesToEncrypt: new Set(['firstAttr']), + }; + const outputType: EncryptedSavedObjectTypeRegistration = { + type: 'known-type-1', + attributesToEncrypt: new Set(['firstAttr', 'secondAttr']), + }; + const context: SavedObjectModelTransformationContext = { + log: logger, + modelVersion: 1, + namespaceType: 'single', + }; + const encryptionSavedObjectService = encryptedSavedObjectsServiceMock.create(); + + it('throws if the types are not compatible', () => { + const mvCreator = getCreateEsoModelVersion(encryptionSavedObjectService, () => + encryptedSavedObjectsServiceMock.create() + ); + expect(() => + mvCreator({ + modelVersion: { + changes: [ + { + type: 'unsafe_transform', + transformFn: (document) => { + return { document }; + }, + }, + ], + }, + inputType: { + type: 'known-type-1', + attributesToEncrypt: new Set(), + }, + outputType: { + type: 'known-type-2', + attributesToEncrypt: new Set(), + }, + }) + ).toThrowErrorMatchingInlineSnapshot( + `"An invalid Encrypted Saved Objects Model Version transformation is trying to transform across types (\\"known-type-1\\" => \\"known-type-2\\"), which isn't permitted"` + ); + }); + + it('throws if there are no changes defined', () => { + const mvCreator = getCreateEsoModelVersion(encryptionSavedObjectService, () => + encryptedSavedObjectsServiceMock.create() + ); + expect(() => + mvCreator({ + modelVersion: { + changes: [], + }, + inputType, + outputType, + }) + ).toThrowErrorMatchingInlineSnapshot( + `"No Model Version changes defined. At least one change is required to create an Encrypted Saved Objects Model Version."` + ); + }); + + it('merges all applicable transforms', () => { + const instantiateServiceWithLegacyType = jest.fn(() => encryptionSavedObjectService); + + const mvCreator = getCreateEsoModelVersion( + encryptionSavedObjectService, + instantiateServiceWithLegacyType + ); + + const esoModelVersion = mvCreator({ + modelVersion: { + // changes include at least one of each supported transform type in an interleaved order + // (we're not concerned with mapping changes here) + changes: [ + { + type: 'unsafe_transform', + transformFn: (document) => { + document.attributes.three = '3'; + return { document }; + }, + }, + { + type: 'data_removal', + removedAttributePaths: ['firstAttr'], + }, + { + type: 'unsafe_transform', + transformFn: (document) => { + document.attributes.two = '2'; + return { document: { ...document, new_prop_1: 'new prop 1' } }; + }, + }, + { + type: 'data_backfill', + backfillFn: () => { + return { attributes: { one: '1' } }; + }, + }, + { + type: 'unsafe_transform', + transformFn: (document) => { + document.attributes.four = '4'; + return { document: { ...document, new_prop_2: 'new prop 2' } }; + }, + }, + ], + }, + inputType, + outputType, + }); + + const initialAttributes = { + firstAttr: 'first_attr', + }; + + const expectedAttributes = { + one: '1', + two: '2', + three: '3', + four: '4', + }; + + encryptionSavedObjectService.decryptAttributesSync.mockReturnValueOnce(initialAttributes); + encryptionSavedObjectService.encryptAttributesSync.mockReturnValueOnce(expectedAttributes); + + // There should be only one change now + expect(esoModelVersion.changes.length === 1); + + // It should be a single unsafe transform + const unsafeTransforms = esoModelVersion.changes.filter( + (change) => change.type === 'unsafe_transform' + ) as SavedObjectsModelUnsafeTransformChange[]; + expect(unsafeTransforms.length === 1); + + const result = unsafeTransforms[0].transformFn( + { + id: '123', + type: 'known-type-1', + namespace: 'namespace', + attributes: initialAttributes, + }, + context + ); + + // This is the major part of the test. Did the encrypt function get called with + // the attributes updated by all of the transform functions. + expect(encryptionSavedObjectService.encryptAttributesSync).toBeCalledTimes(1); + expect(encryptionSavedObjectService.encryptAttributesSync).toHaveBeenCalledWith( + { + id: '123', + type: 'known-type-1', + namespace: 'namespace', + }, + expectedAttributes + ); + + expect(result).toEqual({ + document: { + id: '123', + type: 'known-type-1', + namespace: 'namespace', + new_prop_1: 'new prop 1', // added by unsafe transform + new_prop_2: 'new prop 2', // added by unsafe transform + attributes: expectedAttributes, + }, + }); + }); + + it('throws error on decryption failure if shouldTransformIfDecryptionFails is false', () => { + const instantiateServiceWithLegacyType = jest.fn(() => encryptionSavedObjectService); + + const mvCreator = getCreateEsoModelVersion( + encryptionSavedObjectService, + instantiateServiceWithLegacyType + ); + + const esoModelVersion = mvCreator({ + modelVersion: { + changes: [ + { + type: 'unsafe_transform', + transformFn: (document) => { + return { document }; + }, + }, + ], + }, + inputType, + outputType, + shouldTransformIfDecryptionFails: false, + }); + + const attributes = { + firstAttr: 'first_attr', + }; + + encryptionSavedObjectService.decryptAttributesSync.mockImplementationOnce(() => { + throw new Error('decryption failed!'); + }); + + const unsafeTransforms = esoModelVersion.changes.filter( + (change) => change.type === 'unsafe_transform' + ) as SavedObjectsModelUnsafeTransformChange[]; + expect(unsafeTransforms.length === 1); + expect(() => { + unsafeTransforms[0].transformFn( + { + id: '123', + type: 'known-type-1', + namespace: 'namespace', + attributes, + }, + context + ); + }).toThrowError(`decryption failed!`); + + expect(encryptionSavedObjectService.decryptAttributesSync).toHaveBeenCalledWith( + { + id: '123', + type: 'known-type-1', + namespace: 'namespace', + }, + attributes, + { isTypeBeingConverted: false } + ); + + expect(encryptionSavedObjectService.encryptAttributesSync).not.toHaveBeenCalled(); + }); + + it('throws error on decryption failure if shouldTransformIfDecryptionFails is true but error is not encryption error', () => { + const instantiateServiceWithLegacyType = jest.fn(() => encryptionSavedObjectService); + + const mvCreator = getCreateEsoModelVersion( + encryptionSavedObjectService, + instantiateServiceWithLegacyType + ); + + const esoModelVersion = mvCreator({ + modelVersion: { + changes: [ + { + type: 'unsafe_transform', + transformFn: (document) => { + return { document }; + }, + }, + ], + }, + inputType, + outputType, + shouldTransformIfDecryptionFails: true, + }); + + const attributes = { + firstAttr: 'first_attr', + }; + + encryptionSavedObjectService.decryptAttributesSync.mockImplementationOnce(() => { + throw new Error('decryption failed!'); + }); + + const unsafeTransforms = esoModelVersion.changes.filter( + (change) => change.type === 'unsafe_transform' + ) as SavedObjectsModelUnsafeTransformChange[]; + expect(unsafeTransforms.length === 1); + expect(() => { + unsafeTransforms[0].transformFn( + { + id: '123', + type: 'known-type-1', + namespace: 'namespace', + attributes, + }, + context + ); + }).toThrowError(`decryption failed!`); + + expect(encryptionSavedObjectService.decryptAttributesSync).toHaveBeenCalledWith( + { + id: '123', + type: 'known-type-1', + namespace: 'namespace', + }, + attributes, + { isTypeBeingConverted: false } + ); + + expect(encryptionSavedObjectService.stripOrDecryptAttributesSync).not.toHaveBeenCalled(); + expect(encryptionSavedObjectService.encryptAttributesSync).not.toHaveBeenCalled(); + }); + + it('executes transformation on decryption failure if shouldTransformIfDecryptionFails is true and error is encryption error', () => { + const instantiateServiceWithLegacyType = jest.fn(() => encryptionSavedObjectService); + + const mvCreator = getCreateEsoModelVersion( + encryptionSavedObjectService, + instantiateServiceWithLegacyType + ); + + const esoModelVersion = mvCreator({ + modelVersion: { + changes: [ + { + type: 'unsafe_transform', + transformFn: (document) => { + return { document }; + }, + }, + ], + }, + inputType, + outputType, + shouldTransformIfDecryptionFails: true, + }); + + const attributes = { + firstAttr: 'first_attr', + attrToStrip: 'secret', + }; + const strippedAttributes = { + firstAttr: 'first_attr', + }; + + encryptionSavedObjectService.decryptAttributesSync.mockImplementationOnce(() => { + throw new EncryptionError( + `Unable to decrypt attribute "'attribute'"`, + 'attribute', + EncryptionErrorOperation.Decryption, + new Error('decryption failed') + ); + }); + + encryptionSavedObjectService.stripOrDecryptAttributesSync.mockReturnValueOnce({ + attributes: strippedAttributes, + }); + + const unsafeTransforms = esoModelVersion.changes.filter( + (change) => change.type === 'unsafe_transform' + ) as SavedObjectsModelUnsafeTransformChange[]; + expect(unsafeTransforms.length === 1); + unsafeTransforms[0].transformFn( + { + id: '123', + type: 'known-type-1', + namespace: 'namespace', + attributes, + }, + context + ); + + expect(encryptionSavedObjectService.stripOrDecryptAttributesSync).toHaveBeenCalledWith( + { + id: '123', + type: 'known-type-1', + namespace: 'namespace', + }, + attributes, + { isTypeBeingConverted: false } + ); + + expect(encryptionSavedObjectService.encryptAttributesSync).toHaveBeenCalledWith( + { + id: '123', + type: 'known-type-1', + namespace: 'namespace', + }, + strippedAttributes + ); + }); + + it('throws error on transform failure', () => { + const instantiateServiceWithLegacyType = jest.fn(() => encryptionSavedObjectService); + + const mvCreator = getCreateEsoModelVersion( + encryptionSavedObjectService, + instantiateServiceWithLegacyType + ); + + const esoModelVersion = mvCreator({ + modelVersion: { + changes: [ + { + type: 'unsafe_transform', + transformFn: (document) => { + throw new Error('transform failed!'); + }, + }, + ], + }, + inputType, + outputType, + }); + + const attributes = { + firstAttr: 'first_attr', + }; + + encryptionSavedObjectService.decryptAttributesSync.mockReturnValueOnce(attributes); + + const unsafeTransforms = esoModelVersion.changes.filter( + (change) => change.type === 'unsafe_transform' + ) as SavedObjectsModelUnsafeTransformChange[]; + expect(unsafeTransforms.length === 1); + expect(() => { + unsafeTransforms[0].transformFn( + { + id: '123', + type: 'known-type-1', + namespace: 'namespace', + attributes, + }, + context + ); + }).toThrowError(`transform failed!`); + + expect(encryptionSavedObjectService.decryptAttributesSync).toHaveBeenCalledWith( + { + id: '123', + type: 'known-type-1', + namespace: 'namespace', + }, + attributes, + { isTypeBeingConverted: false } + ); + + expect(encryptionSavedObjectService.encryptAttributesSync).not.toHaveBeenCalled(); + }); + + it('throws error on transform failure even if shouldMigrateIfDecryptionFails is true', () => { + const instantiateServiceWithLegacyType = jest.fn(() => encryptionSavedObjectService); + + const mvCreator = getCreateEsoModelVersion( + encryptionSavedObjectService, + instantiateServiceWithLegacyType + ); + + const esoModelVersion = mvCreator({ + modelVersion: { + changes: [ + { + type: 'unsafe_transform', + transformFn: (document) => { + throw new Error('transform failed!'); + }, + }, + ], + }, + inputType, + outputType, + shouldTransformIfDecryptionFails: true, + }); + + const attributes = { + firstAttr: 'first_attr', + }; + + encryptionSavedObjectService.decryptAttributesSync.mockReturnValueOnce(attributes); + + const unsafeTransforms = esoModelVersion.changes.filter( + (change) => change.type === 'unsafe_transform' + ) as SavedObjectsModelUnsafeTransformChange[]; + expect(unsafeTransforms.length === 1); + expect(() => { + unsafeTransforms[0].transformFn( + { + id: '123', + type: 'known-type-1', + namespace: 'namespace', + attributes, + }, + context + ); + }).toThrowError(`transform failed!`); + + expect(encryptionSavedObjectService.decryptAttributesSync).toHaveBeenCalledWith( + { + id: '123', + type: 'known-type-1', + namespace: 'namespace', + }, + attributes, + { isTypeBeingConverted: false } + ); + + expect(encryptionSavedObjectService.encryptAttributesSync).not.toHaveBeenCalled(); + }); + + it('throws error on encryption failure', () => { + const instantiateServiceWithLegacyType = jest.fn(() => encryptionSavedObjectService); + + const mvCreator = getCreateEsoModelVersion( + encryptionSavedObjectService, + instantiateServiceWithLegacyType + ); + + const esoModelVersion = mvCreator({ + modelVersion: { + changes: [ + { + type: 'unsafe_transform', + transformFn: (document) => { + return { document }; + }, + }, + ], + }, + inputType, + outputType, + }); + + const attributes = { + firstAttr: 'first_attr', + }; + + encryptionSavedObjectService.decryptAttributesSync.mockReturnValueOnce(attributes); + encryptionSavedObjectService.encryptAttributesSync.mockImplementationOnce(() => { + throw new Error('encryption failed!'); + }); + + const unsafeTransforms = esoModelVersion.changes.filter( + (change) => change.type === 'unsafe_transform' + ) as SavedObjectsModelUnsafeTransformChange[]; + expect(unsafeTransforms.length === 1); + expect(() => { + unsafeTransforms[0].transformFn( + { + id: '123', + type: 'known-type-1', + namespace: 'namespace', + attributes, + }, + context + ); + }).toThrowError(`encryption failed!`); + + expect(encryptionSavedObjectService.decryptAttributesSync).toHaveBeenCalledWith( + { + id: '123', + type: 'known-type-1', + namespace: 'namespace', + }, + attributes, + { isTypeBeingConverted: false } + ); + + expect(encryptionSavedObjectService.encryptAttributesSync).toHaveBeenCalledWith( + { + id: '123', + type: 'known-type-1', + namespace: 'namespace', + }, + attributes + ); + }); + + it('throws error on encryption failure even if shouldMigrateIfDecryptionFails is true', () => { + const instantiateServiceWithLegacyType = jest.fn(() => encryptionSavedObjectService); + + const mvCreator = getCreateEsoModelVersion( + encryptionSavedObjectService, + instantiateServiceWithLegacyType + ); + + const esoModelVersion = mvCreator({ + modelVersion: { + changes: [ + { + type: 'unsafe_transform', + transformFn: (document) => { + return { document }; + }, + }, + ], + }, + inputType, + outputType, + shouldTransformIfDecryptionFails: true, + }); + + const attributes = { + firstAttr: 'first_attr', + }; + + encryptionSavedObjectService.decryptAttributesSync.mockReturnValueOnce(attributes); + encryptionSavedObjectService.encryptAttributesSync.mockImplementationOnce(() => { + throw new Error('encryption failed!'); + }); + + const unsafeTransforms = esoModelVersion.changes.filter( + (change) => change.type === 'unsafe_transform' + ) as SavedObjectsModelUnsafeTransformChange[]; + expect(unsafeTransforms.length === 1); + expect(() => { + unsafeTransforms[0].transformFn( + { + id: '123', + type: 'known-type-1', + namespace: 'namespace', + attributes, + }, + context + ); + }).toThrowError(`encryption failed!`); + + expect(encryptionSavedObjectService.decryptAttributesSync).toHaveBeenCalledWith( + { + id: '123', + type: 'known-type-1', + namespace: 'namespace', + }, + attributes, + { isTypeBeingConverted: false } + ); + + expect(encryptionSavedObjectService.encryptAttributesSync).toHaveBeenCalledWith( + { + id: '123', + type: 'known-type-1', + namespace: 'namespace', + }, + attributes + ); + }); + + it('decrypts with input type, and encrypts with output type', () => { + const serviceWithInputLegacyType = encryptedSavedObjectsServiceMock.create(); + const serviceWithOutputLegacyType = encryptedSavedObjectsServiceMock.create(); + const instantiateServiceWithLegacyType = jest.fn(); + + function createEsoMv() { + instantiateServiceWithLegacyType + .mockImplementationOnce(() => serviceWithInputLegacyType) + .mockImplementationOnce(() => serviceWithOutputLegacyType); + + const mvCreator = getCreateEsoModelVersion( + encryptionSavedObjectService, + instantiateServiceWithLegacyType + ); + + return mvCreator({ + modelVersion: { + changes: [ + { + type: 'unsafe_transform', + transformFn: (document) => { + // modify an encrypted field + document.attributes.firstAttr = `~~${document.attributes.firstAttr}~~`; + // encrypt a non encrypted field if it's there + if (document.attributes.nonEncryptedAttr) { + document.attributes.encryptedAttr = document.attributes.nonEncryptedAttr; + delete document.attributes.nonEncryptedAttr; + } + return { document }; + }, + }, + ], + }, + inputType, + outputType, + }); + } + const esoMv = createEsoMv(); + + expect(instantiateServiceWithLegacyType).toHaveBeenCalledWith(inputType); + expect(instantiateServiceWithLegacyType).toHaveBeenCalledWith(outputType); + + serviceWithInputLegacyType.decryptAttributesSync.mockReturnValueOnce({ + firstAttr: 'first_attr', + nonEncryptedAttr: 'non encrypted', + }); + + serviceWithOutputLegacyType.encryptAttributesSync.mockReturnValueOnce({ + firstAttr: `#####`, + encryptedAttr: `#####`, + }); + + const unsafeTransforms = esoMv.changes.filter( + (change) => change.type === 'unsafe_transform' + ) as SavedObjectsModelUnsafeTransformChange[]; + expect(unsafeTransforms.length === 1); + const result = unsafeTransforms[0].transformFn( + { + id: '123', + type: 'known-type-1', + namespace: 'namespace', + attributes: { + firstAttr: '#####', + nonEncryptedAttr: 'non encrypted', + }, + }, + context + ); + + expect(result).toMatchObject({ + document: { + id: '123', + type: 'known-type-1', + namespace: 'namespace', + attributes: { + firstAttr: '#####', + encryptedAttr: `#####`, + }, + }, + }); + + expect(serviceWithInputLegacyType.decryptAttributesSync).toHaveBeenCalledWith( + { + id: '123', + type: 'known-type-1', + namespace: 'namespace', + }, + { + firstAttr: '#####', + nonEncryptedAttr: 'non encrypted', + }, + { isTypeBeingConverted: false } + ); + + expect(serviceWithOutputLegacyType.encryptAttributesSync).toHaveBeenCalledWith( + { + id: '123', + type: 'known-type-1', + namespace: 'namespace', + }, + { + firstAttr: `~~first_attr~~`, + encryptedAttr: 'non encrypted', + } + ); + }); +}); diff --git a/x-pack/plugins/encrypted_saved_objects/server/create_model_version.ts b/x-pack/plugins/encrypted_saved_objects/server/create_model_version.ts new file mode 100644 index 0000000000000..952818e2b25d7 --- /dev/null +++ b/x-pack/plugins/encrypted_saved_objects/server/create_model_version.ts @@ -0,0 +1,135 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { buildModelVersionTransformFn } from '@kbn/core-saved-objects-base-server-internal'; +import type { + SavedObjectModelTransformationFn, + SavedObjectsModelChange, + SavedObjectsModelVersion, +} from '@kbn/core-saved-objects-server'; + +import { EncryptionError } from './crypto'; +import type { EncryptedSavedObjectsService, EncryptedSavedObjectTypeRegistration } from './crypto'; +import { mapAttributes } from './saved_objects/map_attributes'; + +export interface CreateEsoModelVersionFnOpts { + modelVersion: SavedObjectsModelVersion; + shouldTransformIfDecryptionFails?: boolean; + inputType: EncryptedSavedObjectTypeRegistration; + outputType: EncryptedSavedObjectTypeRegistration; +} + +// This function is designed to wrap a Model Version implementation of an Encrypted Saved Object (a Saved Object +// who's type is registered with the Encrypted Saved Object Plugin). The purpose of this wrapper is to ensure that +// version changes to the ESO what would require re-encryption (e.g.changes to encrypted fields or fields excluded +// from AAD) are performed correctly. Prior to Model Versions, the CreateEncryptedSavedObjectsMigrationFn handled +// wrapping migration functions for the same purpose. +// +// For Model Versions, 'data_backfill', 'data_removal', and 'unsafe_transform' changes are leveraged to implement +// any changes to the object as usual. This function returns a Model Version where the changes are merged into a +// single 'unsafe_transform' transform where the document being transformed is first decrypted via the inputType +// EncryptedSavedObjectTypeRegistration, then transformed based on the changes defined in the input Model Version, +// and finally encrypted via the outputType EncryptedSavedObjectTypeRegistration.The implementation for this can +// be found in getCreateEsoModelVersion below. +export type CreateEsoModelVersionFn = ( + opts: CreateEsoModelVersionFnOpts +) => SavedObjectsModelVersion; + +export const getCreateEsoModelVersion = + ( + encryptedSavedObjectsService: Readonly, + instantiateServiceWithLegacyType: ( + typeRegistration: EncryptedSavedObjectTypeRegistration + ) => EncryptedSavedObjectsService + ): CreateEsoModelVersionFn => + ({ modelVersion, shouldTransformIfDecryptionFails, inputType, outputType }) => { + // If there are no changes, then there is no reason to create an Encrypted Saved Objects Model Version + // Throw an error to notify the developer + const incomingChanges = modelVersion.changes; + if (incomingChanges.length === 0) { + throw new Error( + `No Model Version changes defined. At least one change is required to create an Encrypted Saved Objects Model Version.` + ); + } + + if (inputType.type !== outputType.type) { + throw new Error( + `An invalid Encrypted Saved Objects Model Version transformation is trying to transform across types ("${inputType.type}" => "${outputType.type}"), which isn't permitted` + ); + } + + const inputService = instantiateServiceWithLegacyType(inputType); + const outputService = + inputType !== outputType ? instantiateServiceWithLegacyType(outputType) : inputService; + + const transformFn = createMergedTransformFn( + inputService, + outputService, + shouldTransformIfDecryptionFails, + incomingChanges + ); + + return { ...modelVersion, changes: [{ type: 'unsafe_transform', transformFn }] }; + }; + +function createMergedTransformFn( + inputService: Readonly, + outputService: Readonly, + shouldTransformIfDecryptionFails: boolean | undefined, + modelChanges: SavedObjectsModelChange[] +): SavedObjectModelTransformationFn { + // This merges the functions from all 'data_backfill', 'data_removal', and 'unsafe_transform' changes + const mergedTransformFn = buildModelVersionTransformFn(modelChanges); + + return (document, context) => { + const { type, id, originId } = document; + + const descriptorNamespace = context.namespaceType === 'single' ? document.namespace : undefined; + const encryptionDescriptor = { id, type, namespace: descriptorNamespace }; + const decryptionParams = { + // Note about isTypeBeingConverted: false + // "Converting to multi-namespace clashes with the ZDT requirement for serverless" + // See deprecation in packages/core/saved-objects/core-saved-objects-server/src/migration.ts SavedObjectMigrationContext + isTypeBeingConverted: false, + originId, + }; + + const documentToTransform = mapAttributes(document, (inputAttributes) => { + try { + return inputService.decryptAttributesSync( + encryptionDescriptor, + inputAttributes, + decryptionParams + ); + } catch (err) { + if (!shouldTransformIfDecryptionFails || !(err instanceof EncryptionError)) { + throw err; + } + + context.log.warn( + `Decryption failed for encrypted Saved Object "${document.id}" of type "${document.type}" with error: ${err.message}. Encrypted attributes have been stripped from the original document and model version transformation will be applied but this may cause errors later on.` + ); + return inputService.stripOrDecryptAttributesSync( + encryptionDescriptor, + inputAttributes, + decryptionParams + ).attributes; + } + }); + + // call merged transforms + const result = mergedTransformFn(documentToTransform, context); + + // encrypt + const transformedDoc = mapAttributes(result.document, (transformedAttributes) => { + return outputService.encryptAttributesSync(encryptionDescriptor, transformedAttributes); + }); + + // return encrypted doc + return { ...result, document: transformedDoc }; + }; +} diff --git a/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.test.ts b/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.test.ts index 57238786dd499..640f27da6cd3a 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.test.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.test.ts @@ -202,7 +202,9 @@ describe('#stripOrDecryptAttributes', () => { ); expect(decryptedAttributes).toEqual({ attrZero: 'zero', attrTwo: 'two', attrFour: 'four' }); - expect(error).toMatchInlineSnapshot(`[Error: Unable to decrypt attribute "attrThree"]`); + expect(error).toMatchInlineSnapshot( + `[Error: Unable to decrypt attribute "attrThree" of saved object "known-type-1,object-id"]` + ); }); }); @@ -257,7 +259,9 @@ describe('#stripOrDecryptAttributes', () => { const encryptionError = error as EncryptionError; expect(encryptionError.attributeName).toBe('attrThree'); - expect(encryptionError.message).toBe('Unable to decrypt attribute "attrThree"'); + expect(encryptionError.message).toBe( + 'Unable to decrypt attribute "attrThree" of saved object "known-type-1,object-id"' + ); expect(encryptionError.cause).toEqual( new Error('Decryption is disabled because of missing decryption keys.') ); @@ -381,7 +385,9 @@ describe('#stripOrDecryptAttributesSync', () => { ); expect(decryptedAttributes).toEqual({ attrZero: 'zero', attrTwo: 'two', attrFour: 'four' }); - expect(error).toMatchInlineSnapshot(`[Error: Unable to decrypt attribute "attrThree"]`); + expect(error).toMatchInlineSnapshot( + `[Error: Unable to decrypt attribute "attrThree" of saved object "known-type-1,object-id"]` + ); }); }); @@ -436,7 +442,9 @@ describe('#stripOrDecryptAttributesSync', () => { const encryptionError = error as EncryptionError; expect(encryptionError.attributeName).toBe('attrThree'); - expect(encryptionError.message).toBe('Unable to decrypt attribute "attrThree"'); + expect(encryptionError.message).toBe( + 'Unable to decrypt attribute "attrThree" of saved object "known-type-1,object-id"' + ); expect(encryptionError.cause).toEqual( new Error('Decryption is disabled because of missing decryption keys.') ); diff --git a/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.ts b/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.ts index 7bde0d6190547..9a273bbef9fd1 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/crypto/encrypted_saved_objects_service.ts @@ -286,11 +286,15 @@ export class EncryptedSavedObjectsService { encryptedAttributes[attributeName] = (yield [attributeValue, encryptionAAD])!; } catch (err) { this.options.logger.error( - `Failed to encrypt "${attributeName}" attribute: ${err.message || err}` + `Failed to encrypt "${attributeName}" attribute of saved object "${descriptorToArray( + descriptor + )}": ${err.message || err}` ); throw new EncryptionError( - `Unable to encrypt attribute "${attributeName}"`, + `Unable to encrypt attribute "${attributeName}" of saved object "${descriptorToArray( + descriptor + )}"`, attributeName, EncryptionErrorOperation.Encryption, err @@ -544,11 +548,15 @@ export class EncryptedSavedObjectsService { decryptedAttributes[attributeName] = (yield [attributeValue, encryptionAADs])!; } catch (err) { this.options.logger.error( - `Failed to decrypt "${attributeName}" attribute: ${err.message || err}` + `Failed to decrypt attribute "${attributeName}" of saved object "${descriptorToArray( + descriptor + )}": ${err.message || err}` ); throw new EncryptionError( - `Unable to decrypt attribute "${attributeName}"`, + `Unable to decrypt attribute "${attributeName}" of saved object "${descriptorToArray( + descriptor + )}"`, attributeName, EncryptionErrorOperation.Decryption, err diff --git a/x-pack/plugins/encrypted_saved_objects/server/mocks.ts b/x-pack/plugins/encrypted_saved_objects/server/mocks.ts index aa72fb372e878..1bc30c41b6b22 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/mocks.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/mocks.ts @@ -19,6 +19,7 @@ function createEncryptedSavedObjectsSetupMock( __legacyCompat: { registerLegacyAPI: jest.fn() }, canEncrypt, createMigration: jest.fn(), + createModelVersion: jest.fn(), } as jest.Mocked; } diff --git a/x-pack/plugins/encrypted_saved_objects/server/plugin.test.ts b/x-pack/plugins/encrypted_saved_objects/server/plugin.test.ts index 527e4bcd82535..6ae68f89151e0 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/plugin.test.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/plugin.test.ts @@ -22,6 +22,7 @@ describe('EncryptedSavedObjects Plugin', () => { Object { "canEncrypt": false, "createMigration": [Function], + "createModelVersion": [Function], "registerType": [Function], } `); @@ -39,6 +40,7 @@ describe('EncryptedSavedObjects Plugin', () => { Object { "canEncrypt": true, "createMigration": [Function], + "createModelVersion": [Function], "registerType": [Function], } `); diff --git a/x-pack/plugins/encrypted_saved_objects/server/plugin.ts b/x-pack/plugins/encrypted_saved_objects/server/plugin.ts index 7b7fed43f5181..ba69b00ecb4e1 100644 --- a/x-pack/plugins/encrypted_saved_objects/server/plugin.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/plugin.ts @@ -12,8 +12,11 @@ import type { CoreSetup, Logger, Plugin, PluginInitializerContext } from '@kbn/c import type { SecurityPluginSetup } from '@kbn/security-plugin/server'; import type { ConfigType } from './config'; -import type { CreateEncryptedSavedObjectsMigrationFn } from './create_migration'; -import { getCreateMigration } from './create_migration'; +import { + type CreateEncryptedSavedObjectsMigrationFn, + getCreateMigration, +} from './create_migration'; +import { type CreateEsoModelVersionFn, getCreateEsoModelVersion } from './create_model_version'; import type { EncryptedSavedObjectTypeRegistration } from './crypto'; import { EncryptedSavedObjectsService, @@ -35,6 +38,7 @@ export interface EncryptedSavedObjectsPluginSetup { canEncrypt: boolean; registerType: (typeRegistration: EncryptedSavedObjectTypeRegistration) => void; createMigration: CreateEncryptedSavedObjectsMigrationFn; + createModelVersion: CreateEsoModelVersionFn; } export interface EncryptedSavedObjectsPluginStart { @@ -129,6 +133,18 @@ export class EncryptedSavedObjectsPlugin return serviceForMigration; } ), + createModelVersion: getCreateEsoModelVersion( + service, + (typeRegistration: EncryptedSavedObjectTypeRegistration) => { + const serviceForMigration = new EncryptedSavedObjectsService({ + primaryCrypto, + decryptionOnlyCryptos, + logger: this.logger, + }); + serviceForMigration.registerType(typeRegistration); + return serviceForMigration; + } + ), }; } diff --git a/x-pack/plugins/dataset_quality/server/types/integration.ts b/x-pack/plugins/encrypted_saved_objects/server/saved_objects/map_attributes.ts similarity index 51% rename from x-pack/plugins/dataset_quality/server/types/integration.ts rename to x-pack/plugins/encrypted_saved_objects/server/saved_objects/map_attributes.ts index 2595a120c8b70..f76203ab2341d 100644 --- a/x-pack/plugins/dataset_quality/server/types/integration.ts +++ b/x-pack/plugins/encrypted_saved_objects/server/saved_objects/map_attributes.ts @@ -5,17 +5,10 @@ * 2.0. */ -export interface Integration { - name: string; - title?: string; - version?: string; - icons?: IntegrationIcon[]; -} +import type { SavedObjectUnsanitizedDoc } from '@kbn/core/server'; -export interface IntegrationIcon { - path: string; - src: string; - title?: string; - size?: string; - type?: string; +export function mapAttributes(obj: SavedObjectUnsanitizedDoc, mapper: (attributes: T) => T) { + return Object.assign(obj, { + attributes: mapper(obj.attributes), + }); } diff --git a/x-pack/plugins/encrypted_saved_objects/tsconfig.json b/x-pack/plugins/encrypted_saved_objects/tsconfig.json index 7d60b8171f1d9..7b9087064c109 100644 --- a/x-pack/plugins/encrypted_saved_objects/tsconfig.json +++ b/x-pack/plugins/encrypted_saved_objects/tsconfig.json @@ -10,6 +10,7 @@ "@kbn/core", "@kbn/utility-types", "@kbn/core-saved-objects-server", + "@kbn/core-saved-objects-base-server-internal", ], "exclude": [ "target/**/*", diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_overview/analytics_collection_chart.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_overview/analytics_collection_chart.tsx index 3cda9a274cd69..231af9e9afd96 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_overview/analytics_collection_chart.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_collection_view/analytics_collection_overview/analytics_collection_chart.tsx @@ -62,7 +62,6 @@ export const AnalyticsCollectionChart: React.FC< const { uiSettings, charts: chartSettings } = useValues(KibanaLogic); const fromDateParsed = DateMath.parse(timeRange.from); const toDataParsed = DateMath.parse(timeRange.to); - const chartTheme = chartSettings.theme.useChartsTheme(); const baseChartTheme = chartSettings.theme.useChartsBaseTheme(); const charts = useMemo( () => [ @@ -127,7 +126,6 @@ export const AnalyticsCollectionChart: React.FC< ) : ( { diff --git a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_overview/analytics_collection_card/analytics_collection_card.tsx b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_overview/analytics_collection_card/analytics_collection_card.tsx index f67cccd4ddcdd..587024e87581e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_overview/analytics_collection_card/analytics_collection_card.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/analytics/components/analytics_overview/analytics_collection_card/analytics_collection_card.tsx @@ -10,7 +10,15 @@ import React, { MouseEvent } from 'react'; import { parsePath } from 'history'; import { useValues } from 'kea'; -import { AreaSeries, Chart, CurveType, ScaleType, Settings, Tooltip } from '@elastic/charts'; +import { + AreaSeries, + Chart, + CurveType, + ScaleType, + Settings, + Tooltip, + LEGACY_LIGHT_THEME, +} from '@elastic/charts'; import { EuiBadge, EuiCard, @@ -175,6 +183,8 @@ export const AnalyticsCollectionCard: React.FC< {!isLoading && data?.some(([, y]) => y && y !== 0) && ( { expect(wrapper.find(Chart).prop('size')).toEqual({ height: 300 }); expect(wrapper.find(Axis)).toHaveLength(2); - expect(mockKibanaValues.charts.theme.useChartsTheme).toHaveBeenCalled(); expect(mockKibanaValues.charts.theme.useChartsBaseTheme).toHaveBeenCalled(); expect(wrapper.find(LineSeries)).toHaveLength(1); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_chart.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_chart.tsx index c53d22fbcee05..3267e88e07af7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_chart.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/analytics/components/analytics_chart.tsx @@ -39,11 +39,7 @@ export const AnalyticsChart: React.FC = ({ height = 300, lines }) => { return ( moment(tooltip.value).format(TOOLTIP_DATE_FORMAT)} /> - + {lines.map(({ id, data, isDashed }) => ( { }, [curationId]); if (dataLoading) { - return ; + return ; } return isAutomated ? : ; }; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/layout/kibana_header_actions.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/layout/kibana_header_actions.tsx index e23c8ff8f0f0c..e2756bdda05f2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/layout/kibana_header_actions.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/layout/kibana_header_actions.tsx @@ -9,8 +9,7 @@ import React from 'react'; import { useValues } from 'kea'; -import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; - +import { EndpointsHeaderAction } from '../../../shared/layout/endpoints_header_action'; import { EngineLogic } from '../engine'; import { QueryTesterButton } from '../query_tester'; @@ -18,12 +17,6 @@ export const KibanaHeaderActions: React.FC = () => { const { engineName } = useValues(EngineLogic); return ( - - {engineName && ( - - - - )} - + {Boolean(engineName) && } ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/index.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/index.tsx index efef9bf1bb398..1c661f1f6d03b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/index.tsx @@ -16,6 +16,7 @@ import { isVersionMismatch } from '../../../common/is_version_mismatch'; import { InitialAppData } from '../../../common/types'; import { HttpLogic } from '../shared/http'; import { KibanaLogic } from '../shared/kibana'; +import { EndpointsHeaderAction } from '../shared/layout/endpoints_header_action'; import { VersionMismatchPage } from '../shared/version_mismatch'; import { AppLogic } from './app_logic'; @@ -77,13 +78,18 @@ export const AppSearch: React.FC = (props) => { ); }; -export const AppSearchUnconfigured: React.FC = () => ( - - - - - -); +export const AppSearchUnconfigured: React.FC = () => { + const { renderHeaderActions } = useValues(KibanaLogic); + renderHeaderActions(EndpointsHeaderAction); + + return ( + + + + + + ); +}; export const AppSearchConfigured: React.FC> = (props) => { const { diff --git a/x-pack/plugins/enterprise_search/public/applications/applications/components/layout/page_template.tsx b/x-pack/plugins/enterprise_search/public/applications/applications/components/layout/page_template.tsx index 54b3450161014..550acf7fa9359 100644 --- a/x-pack/plugins/enterprise_search/public/applications/applications/components/layout/page_template.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/applications/components/layout/page_template.tsx @@ -5,13 +5,17 @@ * 2.0. */ -import React from 'react'; +import React, { useLayoutEffect } from 'react'; + +import { useValues } from 'kea'; import { ENTERPRISE_SEARCH_CONTENT_PLUGIN } from '../../../../../common/constants'; +import { KibanaLogic } from '../../../shared/kibana'; import { SetEnterpriseSearchApplicationsChrome } from '../../../shared/kibana_chrome'; import { EnterpriseSearchPageTemplateWrapper, PageTemplateProps } from '../../../shared/layout'; import { useEnterpriseSearchApplicationNav } from '../../../shared/layout'; import { SendEnterpriseSearchTelemetry } from '../../../shared/telemetry'; +import { SearchApplicationHeaderDocsAction } from '../search_application/header_docs_action'; export type EnterpriseSearchApplicationsPageTemplateProps = Omit< PageTemplateProps, @@ -36,6 +40,14 @@ export const EnterpriseSearchApplicationsPageTemplate: React.FC< pageTemplateProps.isEmptyState, hasSchemaConflicts ); + const { renderHeaderActions } = useValues(KibanaLogic); + useLayoutEffect(() => { + renderHeaderActions(SearchApplicationHeaderDocsAction); + + return () => { + renderHeaderActions(); + }; + }, []); return ( ( - - - - {i18n.translate( - 'xpack.enterpriseSearch.searchApplications.searchApplication.header.searchApplicationsDoc', - { - defaultMessage: 'Search Applications Doc', - } - )} - - - + + + {i18n.translate( + 'xpack.enterpriseSearch.searchApplications.searchApplication.header.searchApplicationsDoc', + { + defaultMessage: 'Search Applications Doc', + } + )} + + ); diff --git a/x-pack/plugins/enterprise_search/public/applications/applications/components/search_application/search_application_view.tsx b/x-pack/plugins/enterprise_search/public/applications/applications/components/search_application/search_application_view.tsx index 92a99b45be6d5..fef6f95c3b435 100644 --- a/x-pack/plugins/enterprise_search/public/applications/applications/components/search_application/search_application_view.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/applications/components/search_application/search_application_view.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useEffect, useLayoutEffect } from 'react'; +import React, { useEffect } from 'react'; import { useParams, Redirect } from 'react-router-dom'; import { useValues, useActions } from 'kea'; @@ -13,8 +13,6 @@ import { useValues, useActions } from 'kea'; import { Routes, Route } from '@kbn/shared-ux-router'; import { Status } from '../../../../../common/types/api'; - -import { KibanaLogic } from '../../../shared/kibana'; import { SEARCH_APPLICATION_PATH, SEARCH_APPLICATION_CONTENT_PATH, @@ -29,7 +27,6 @@ import { DeleteSearchApplicationModal } from '../search_applications/delete_sear import { SearchApplicationConnect } from './connect/search_application_connect'; import { SearchApplicationDocsExplorer } from './docs_explorer/docs_explorer'; -import { SearchApplicationHeaderDocsAction } from './header_docs_action'; import { SearchApplicationContent } from './search_application_content'; import { SearchApplicationError } from './search_application_error'; import { SearchApplicationViewLogic } from './search_application_view_logic'; @@ -48,15 +45,6 @@ export const SearchApplicationView: React.FC = () => { const { tabId = SearchApplicationViewTabs.DOCS_EXPLORER } = useParams<{ tabId?: string; }>(); - const { renderHeaderActions } = useValues(KibanaLogic); - - useLayoutEffect(() => { - renderHeaderActions(SearchApplicationHeaderDocsAction); - - return () => { - renderHeaderActions(); - }; - }, []); useEffect(() => { fetchSearchApplication({ name: searchApplicationName }); diff --git a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/elasticsearch_client_instructions.test.tsx b/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/elasticsearch_client_instructions.test.tsx deleted file mode 100644 index b7fe9c459ee6d..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/elasticsearch_client_instructions.test.tsx +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { setMockValues } from '../../../__mocks__/kea_logic'; - -import React from 'react'; - -import { shallow, ShallowWrapper } from 'enzyme'; - -import * as Languages from './languages'; - -import { ElasticsearchClientInstructions } from '.'; - -describe('Elasticsearch Client Instructions', () => { - let wrapper: ShallowWrapper; - - afterEach(() => { - jest.clearAllMocks(); - }); - - const setup = (language: string) => { - setMockValues({ - cloud: { - cloudId: 'example-cloud-id', - }, - }); - wrapper = shallow(); - }; - - describe('Displaying the right language options', () => { - it.concurrent.each([ - ['dotnet', Languages.ElasticsearchDotnet, false], - ['go', Languages.ElasticsearchGo, true], - ['java', Languages.ElasticsearchJava, false], - ['javascript', Languages.ElasticsearchJavascript, true], - ['php', Languages.ElasticsearchPhp, true], - ['python', Languages.ElasticsearchPython, true], - ['ruby', Languages.ElasticsearchRuby, true], - ['rust', Languages.ElasticsearchRust, false], - ])('%s', (language, Component, hasCloudIdProp) => { - setup(language); - expect(wrapper.find(Component)).toHaveLength(1); - expect(wrapper.find(Component).prop('cloudId')).toEqual( - hasCloudIdProp ? 'example-cloud-id' : undefined - ); - }); - }); - - it('does not display language for unrecognised language', () => { - setup('coffeescript'); - expect(wrapper.isEmptyRender()).toEqual(true); - }); -}); diff --git a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/elasticsearch_client_instructions.tsx b/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/elasticsearch_client_instructions.tsx deleted file mode 100644 index 92c28e8f23336..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/elasticsearch_client_instructions.tsx +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; - -import { useValues } from 'kea'; - -import { KibanaLogic } from '../../../shared/kibana'; - -import { - ElasticsearchDotnet, - ElasticsearchGo, - ElasticsearchJava, - ElasticsearchJavascript, - ElasticsearchPhp, - ElasticsearchPython, - ElasticsearchRuby, - ElasticsearchRust, -} from './languages'; - -const useCloudId = (): string | undefined => { - const { cloud } = useValues(KibanaLogic); - return cloud?.cloudId; -}; - -export const ElasticsearchClientInstructions: React.FC<{ language: string }> = ({ language }) => { - const cloudId = useCloudId(); - - switch (language) { - case 'dotnet': - return ; - case 'go': - return ; - case 'java': - return ; - case 'javascript': - return ; - case 'php': - return ; - case 'python': - return ; - case 'ruby': - return ; - case 'rust': - return ; - default: - return null; - } -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/languages/elasticsearch_dotnet.tsx b/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/languages/elasticsearch_dotnet.tsx deleted file mode 100644 index ee739a459c774..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/languages/elasticsearch_dotnet.tsx +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; - -import dedent from 'dedent'; - -import { EuiCodeBlock, EuiLink, EuiText, EuiSpacer } from '@elastic/eui'; - -import { docLinks } from '../../../../shared/doc_links'; - -export const ElasticsearchDotnet: React.FC<{ cloudId?: string }> = ({ cloudId }) => { - return ( - <> - -

- The official .Net client for Elasticsearch includes all the features you need to add - search to a .Net application: -

-
    -
  • One-to-one mapping with REST API.
  • -
  • Strongly typed requests and responses for Elasticsearch APIs.
  • -
  • Fluent API for building requests.
  • -
  • Helpers for common tasks such as bulk indexing of documents.
  • -
  • Pluggable serialization of requests and responses based on System.Text.Json.
  • -
  • Diagnostics, auditing, and .NET activity integration.
  • -
- -

- The .NET Elasticsearch client is built upon the Elastic Transport library which provides: -

-
    -
  • Connection management and load balancing across all available nodes.
  • -
  • Request retries and dead connections handling.
  • -
- - - Learn more about the official .NET clients for Elasticsearch - - - - The official Elasticsearch .NET clients on Github - -
- - - - -

Installation

-

- For SDK style projects, you can install the Elasticsearch client by running the following - .NET CLI command in your terminal: -

-
- - - {dedent` - dotnet add package Elastic.Clients.Elasticsearch - `} - - - -

- This command adds a package reference to your project (csproj) file for the latest stable - version of the client. -

-

- If you prefer, you may also manually add a package reference inside your project file: -

-
- - - {dedent` - - `} - - - - - -

- For Visual Studio users, the .NET client can also be installed from the Package Manager - Console inside Visual Studio using the following command: -

-
- - - {dedent` - Install-Package Elastic.Clients.Elasticsearch - `} - - - - - -

- Alternatively, search for Elastic.Clients.Elasticsearch in the NuGet Package Manager UI. -

-
- - - - {cloudId ? ( - <> - -

Connecting to Elastic Cloud

-

- Connecting to an Elasticsearch Service deployment is achieved by providing the unique - Cloud ID for your deployment when configuring the ElasticsearchClient instance. You - can retrieve the Cloud ID from the homepage of the deployment in Elasticsearch - Service. You also require suitable credentials that your application uses to - authenticate with your deployment. -

-

- As a security best practice, it is recommended to create a dedicated API key per - application, with permissions limited to only those required for any API calls the - application is authorized to make. -

-

- The following snippet shows you how to create a client instance that connects to an - Elasticsearch deployment in the cloud. -

-
- - - {dedent` - using Elastic.Clients.Elasticsearch; - using Elastic.Transport; - - var client = new ElasticsearchClient("${cloudId}", new ApiKey("")); - - `} - - - ) : ( - <> - -

Connecting to Elasticsearch

-

- The .Net client for Elasticsearch supports connecting to single nodes as well as - multiple nodes utilizing a node pool.{' '} - - Visit the documentation to learn more about connecting to Elasticsearch. - -

-
- - )} - - ); -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/languages/elasticsearch_go.tsx b/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/languages/elasticsearch_go.tsx deleted file mode 100644 index cca6fe484fb3c..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/languages/elasticsearch_go.tsx +++ /dev/null @@ -1,139 +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 dedent from 'dedent'; - -import { EuiCodeBlock, EuiLink, EuiText, EuiSpacer } from '@elastic/eui'; - -import { docLinks } from '../../../../shared/doc_links'; - -export const ElasticsearchGo: React.FC<{ cloudId?: string }> = ({ cloudId }) => { - return ( - <> - -

- The official Go client for Elasticsearch includes all the features you need to add search - to a Go application: -

-
    -
  • One-to-one mapping with the Elasticsearch REST API
  • -
  • Generalized, pluggable architecture
  • -
  • Helpers for convenience
  • -
  • A rich set of examples in the documentation
  • -
- - Learn more about the Go client for Elasticsearch - - - - The Go client for Elasticsearch on Github - - - - View the documentation on GoDoc - -
- - - - -

Installation

-

Add the package to your go.mod file:

-
- - - {dedent` - require github.com/elastic/go-elasticsearch/v8 main - `} - - - - - -

Getting started

-

- The elasticsearch package ties together two separate packages for calling the - Elasticsearch APIs and transferring data over HTTP: esapi and{' '} - elastictransport. -

-

- Use the elasticsearch.NewDefaultClient() function to create the client with - the default settings. -

-
- - - {dedent` - es, err := elasticsearch.NewDefaultClient() - if err != nil { - log.Fatalf("Error creating the client: %s", err) - } - - res, err := es.Info() - if err != nil { - log.Fatalf("Error getting response: %s", err) - } - - defer res.Body.Close() - log.Println(res) - `} - - - - - {cloudId ? ( - <> - -

Connecting to Elastic Cloud

-

- If you are using Elastic Cloud, the client offers an easy way to connect to it. You - must pass your Cloud ID to the client, which is found in the Cloud console, as well as - a corresponding API key. -

-
- - - {dedent` - cfg := elasticsearch.Config{ - CloudID: "${cloudId}", - APIKey: "API_KEY" - } - es, err := elasticsearch.NewClient(cfg) - `} - - - ) : ( - <> - -

Connecting to Elasticsearch

-

- To set the cluster endpoint(s) programmatically, pass a configuration object to the{' '} - elasticsearch.NewClient() function. To set the username and password, - include them in the endpoint URL, or use the corresponding configuration options. -

-
- - - {dedent` - cfg := elasticsearch.Config{ - Addresses: []string{ - "http://localhost:9200", - "http://localhost:9201", - }, - Username: "", - Password: "", - } - es, err := elasticsearch.NewClient(cfg) - `} - - - )} - - ); -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/languages/elasticsearch_java.tsx b/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/languages/elasticsearch_java.tsx deleted file mode 100644 index 573d447c25e03..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/languages/elasticsearch_java.tsx +++ /dev/null @@ -1,107 +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 dedent from 'dedent'; - -import { EuiCodeBlock, EuiLink, EuiText, EuiSpacer } from '@elastic/eui'; - -import { docLinks } from '../../../../shared/doc_links'; - -export const ElasticsearchJava: React.FC = () => { - return ( - <> - -

- The Elasticsearch Java API Client includes all the features you need to add search to a - Java application: -

-
    -
  • Strongly typed requests and responses for all Elasticsearch APIs.
  • -
  • Blocking and asynchronous versions of all APIs.
  • -
  • - Use of fluent builders and functional patterns to allow writing concise yet readable - code when creating complex nested structures. -
  • -
  • - Seamless integration of application classes by using an object mapper such as Jackson or - any JSON-B implementation. -
  • -
- - Learn more about the Elasticsearch JAVA API client - - - - The Elasticsearch JAVA API client on Github - -
- - - - -

Installation

-

- There are several ways to install the Java API client.{' '} - - Visit the client documentation to learn more - - . -

-

Connecting to Elasticsearch

-

The client is structured around three main components:

-
    -
  • - API client classes. These provide strongly typed data structures and - methods for Elasticsearch APIs. Since the Elasticsearch API is large, it is structured - in feature groups (also called “namespaces”), each having its own client class. - Elasticsearch core features are implemented in the ElasticsearchClient class. -
  • -
  • - A JSON object mapper. This maps your application classes to JSON and - seamlessly integrates them with the API client. -
  • -
  • - A transport layer implementation. This is where all HTTP request - handling takes place. -
  • -
-

The code snippet below creates and wires these three components together:

-
- - - {dedent` - // Create the low-level client - RestClient restClient = RestClient.builder( - new HttpHost("localhost", 9200)).build(); - - // Create the transport with a Jackson mapper - ElasticsearchTransport transport = new RestClientTransport( - restClient, new JacksonJsonpMapper()); - - // And create the API client - ElasticsearchClient client = new ElasticsearchClient(transport); - `} - - - -

- Authentication is managed by the{' '} - - Java Low Level REST Client - - . For further details on configuring authentication, refer to{' '} - - its documentation - - . -

-
- - ); -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/languages/elasticsearch_javascript.tsx b/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/languages/elasticsearch_javascript.tsx deleted file mode 100644 index 587ecb09e1198..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/languages/elasticsearch_javascript.tsx +++ /dev/null @@ -1,97 +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 dedent from 'dedent'; - -import { EuiCodeBlock, EuiLink, EuiText, EuiSpacer } from '@elastic/eui'; - -import { docLinks } from '../../../../shared/doc_links'; - -export const ElasticsearchJavascript: React.FC<{ cloudId?: string }> = ({ cloudId }) => { - return ( - <> - -

- This is the official Node.js client for Elasticsearch includes all the features you need - to add search to any Node.js application: -

-
    -
  • One-to-one mapping with REST API.
  • -
  • Generalized, pluggable architecture.
  • -
  • Configurable, automatic discovery of cluster nodes.
  • -
  • Persistent, Keep-Alive connections.
  • -
  • Load balancing across all available nodes.
  • -
  • Child client support.
  • -
  • TypeScript support out of the box.
  • -
- - Learn more about the official Node.js client for Elasticsearch - - - - The official Node.js client for Elasticsearch on Github - -
- - - - -

Installation

-

To install the latest version of the client, run the following command:

-
- - - npm install @elastic/elasticsearch - - - - - {cloudId ? ( - <> - -

Connecting to Elastic Cloud

-

- If you are using Elastic Cloud, the client offers an easy way to connect to it via the - cloud option. You must pass the Cloud ID that you can find in the cloud console, then - your username and password inside the auth option. -

-
- - - {dedent` - const { Client } = require('@elastic/elasticsearch') - const client = new Client({ - cloud: { - id: '${cloudId}', - }, - auth: { - username: '', - password: '' - } - })`} - - - ) : ( - <> - -

Connecting to Elasticsearch

-

- There are several ways to connect and authenticate to Elasticsearch running outside of - Cloud, including API keys, bearer tokens, and basic authentication.{' '} - - Visit the client’s documentation to learn more - - . -

-
- - )} - - ); -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/languages/elasticsearch_php.tsx b/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/languages/elasticsearch_php.tsx deleted file mode 100644 index 229d36d487469..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/languages/elasticsearch_php.tsx +++ /dev/null @@ -1,113 +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 dedent from 'dedent'; - -import { EuiCodeBlock, EuiLink, EuiText, EuiSpacer } from '@elastic/eui'; - -import { docLinks } from '../../../../shared/doc_links'; - -export const ElasticsearchPhp: React.FC<{ cloudId?: string }> = ({ cloudId }) => { - return ( - <> - -

- This official PHP client for Elasticsearch is designed to be a low-level client that does - not stray from the Elasticsearch REST API. -

- - Learn more about the official PHP client for Elasticsearch - - - - The official PHP client for Elasticsearch on Github - -
- - - - -

Installation

-

To install the latest version of the client, run the following command:

- -

Elasticsearch-php only has four requirements that you need to pay attention:

-
    -
  • PHP 7.1.0 or higher
  • -
  • - - Composer - -
  • -
  • - - ext-curl - - : the Libcurl extension for PHP -
  • -
  • Native JSON Extensions (ext-json) 1.3.7 or higher
  • -
-

- The rest of the dependencies are automatically downloaded and installed by Composer. - Composer is a package and dependency manager for PHP and makes it easy to install - Elasticsearch-php. -

- - Visit the documentation for more information. - -
- - - - {cloudId ? ( - <> - -

Connecting to Elastic Cloud

-

- You can connect to Elastic Cloud using Basic authentication or an{' '} - API key. Where {''} is reported in the Deployment UI. For - basic authentication, {''} and {''} are generated when you deploy - a new cloud instance. You’ll need to store the {''} and {''} since - they will not be available via UI. -

-
- - - {dedent` - // Connect via basic authentication - $client = ClientBuilder::create() - ->setElasticCloudId('${cloudId}') - ->setBasicAuthentication('', '') - ->build(); - - // Connect with an API key - $client = ClientBuilder::create() - ->setElasticCloudId('${cloudId}') - ->setApiKey('', '') - ->build(); - `} - - - ) : ( - <> - -

Connecting to Elasticsearch

-

- There are several ways to connect and authenticate to Elasticsearch running outside of - Cloud, including API keys, bearer tokens, and basic authentication.{' '} - - Visit the client’s documentation to learn more - - . -

-
- - )} - - ); -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/languages/elasticsearch_python.tsx b/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/languages/elasticsearch_python.tsx deleted file mode 100644 index 54c7aca9b610a..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/languages/elasticsearch_python.tsx +++ /dev/null @@ -1,163 +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 dedent from 'dedent'; - -import { EuiCodeBlock, EuiLink, EuiText, EuiSpacer } from '@elastic/eui'; - -import { docLinks } from '../../../../shared/doc_links'; - -export const ElasticsearchPython: React.FC<{ cloudId?: string }> = ({ cloudId }) => { - return ( - <> - -

- elasticsearch-py, the official Python client for Elasticsearch, is a low-level client for - interacting with Elasticsearch’s REST API. It’s designed to be unopinionated and - extendable. -

- - Learn more about the Python client for Elasticsearch - - - - The Python client for Elasticsearch on Read the Docs - - - - elasticsearch-py on Github - -
- - - - -

Installation

-

- Install the elasticsearch package with{' '} - - pip - - : -

-
- - - {dedent` - $ python -m pip install elasticsearch - `} - - - - - -

- If your application uses async/await in Python you can install the client with the async - extra: -

-
- - - {dedent` - $ python -m pip install elasticsearch[async] - `} - - - -

- Learn more about{' '} - - using asyncio with this project - - . -

-
- - - - {cloudId ? ( - <> - -

Connecting to Elastic Cloud

-

- Cloud ID is an easy way to configure your client to work with your Elastic Cloud - deployment. Combine the cloud_id with either basic_auth or api_key to authenticate - with your Elastic Cloud deployment. -

-

- Using cloud_id enables TLS verification and HTTP compression by default and sets the - port to 443 unless otherwise overwritten via the port parameter or the port value - encoded within cloud_id. Using Cloud ID also disables sniffing as a proxy is in use. -

-
- - - {dedent` - from elasticsearch import Elasticsearch - - es = Elasticsearch( - cloud_id="${cloudId}" - ) - `} - - - ) : ( - <> - -

Connecting to Elasticsearch

-

- A single node can be specified via a scheme, host,{' '} - port, and optional path_prefix. These values can either be - specified manually via a URL in a string, dictionary, - NodeConfig, or a list of these values. You must specify at least{' '} - scheme, host and port - for each node. All of the following are valid configurations: -

-
- - - {dedent` - from elasticsearch import Elasticsearch - - # Single node via URL - es = Elasticsearch("http://localhost:9200") - - # Multiple nodes via URL - es = Elasticsearch([ - "http://localhost:9200", - "http://localhost:9201", - "http://localhost:9202" - ]) - - # Single node via dictionary - es = Elasticsearch({"scheme": "http", "host": "localhost", "port": 9200}) - - # Multiple nodes via dictionary - es = Elasticsearch([ - {"scheme": "http", "host": "localhost", "port": 9200}, - {"scheme": "http", "host": "localhost", "port": 9201}, - ]) - `} - - - -

- There are several ways to authenticate to Elasticsearch running outside of Cloud, - including API keys, bearer tokens, and basic authentication.{' '} - - Visit the client’s documentation to learn more - - . -

-
- - )} - - ); -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/languages/elasticsearch_ruby.tsx b/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/languages/elasticsearch_ruby.tsx deleted file mode 100644 index 0592037311900..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/languages/elasticsearch_ruby.tsx +++ /dev/null @@ -1,161 +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 dedent from 'dedent'; - -import { EuiCodeBlock, EuiLink, EuiText, EuiSpacer } from '@elastic/eui'; - -import { docLinks } from '../../../../shared/doc_links'; - -export const ElasticsearchRuby: React.FC<{ cloudId?: string }> = ({ cloudId }) => { - return ( - <> - -

- The elasticsearch{' '} - - Rubygem - {' '} - provides a low-level client for communicating with an Elasticsearch cluster, fully - compatible with other official clients. -

- - Learn more about the Ruby client for Elasticsearch - - - - The Elasticsearch Ruby client on Github - - - - The Elasticsearch Ruby client on RubyDoc - - - - -

Check out these other official Ruby libraries for working with Elasticsearch:

- -
- - - - -

Installation

-

- Install the elasticsearch gem from Rubygems: -

-
- - - {dedent` - $ gem install elasticsearch - `} - - - -

Or add it to your project’s Gemfile:

-
- - - {dedent` - gem 'elasticsearch', '' - `} - - - - - {cloudId ? ( - <> - -

Connecting to Elastic Cloud

-

- If you are using Elastic Cloud, the client offers an easy way to connect to it. You - must pass the Cloud ID that you can find in the cloud console. -

-

- You can connect to Elastic Cloud using Basic authentication or an{' '} - API key. Where {''} is reported in the Deployment UI. For - basic authentication, {''} and {''} are generated when you deploy - a new cloud instance. You’ll need to store the {''} and {''} since - they will not be available via UI. -

-
- - - {dedent` - require 'elasticsearch' - - // Connect via basic authentication - client = Elasticsearch::Client.new( - cloud_id: '${cloudId}' - user: '', - password: '', - ) - - // Connect via API key - client = Elasticsearch::Client.new( - cloud_id: '${cloudId}', - api_key: {id: '', api_key: ''} - ) - `} - - - ) : ( - <> - -

Connecting to Elasticsearch

-

- There are several ways to authenticate to Elasticsearch running outside of Cloud, - including API keys, bearer tokens, and basic authentication.{' '} - - Visit the client’s documentation to learn more - - . -

-
- - )} - - ); -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/languages/elasticsearch_rust.tsx b/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/languages/elasticsearch_rust.tsx deleted file mode 100644 index 208fffca35a52..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/languages/elasticsearch_rust.tsx +++ /dev/null @@ -1,89 +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 dedent from 'dedent'; - -import { EuiCodeBlock, EuiLink, EuiText, EuiSpacer } from '@elastic/eui'; - -import { docLinks } from '../../../../shared/doc_links'; - -export const ElasticsearchRust: React.FC = () => { - return ( - <> - -

- The official Rust client for Elasticsearch includes all the features you need to add - search to a Rust application: -

-
    -
  • Fluent builders for all Elasticsearch REST API endpoints
  • -
  • Persistent keep-alive connections
  • -
  • TLS support with system or custom certificates
  • -
  • Proxy support with authentication
  • -
  • Async support with Tokio
  • -
- - Learn more about the Rust client for Elasticsearch - - - - The official Rust client for Elasticsearch on Github - - - - View the documentation on docs.rs - -
- - - - -

Installation

-

- Add elasticsearch crate and version to Cargo.toml. -

-
- - - {dedent` - [dependencies] - elasticsearch = "" - `} - - - -

- The following optional dependencies may also be useful to create requests and read - responses -

-
- - - {dedent` - serde = "~1" - serde_json = "~1" - `} - - - -

- The client also includes{' '} - - async support with tokio - - . -

-
- - ); -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/languages/index.ts b/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/languages/index.ts deleted file mode 100644 index 8a2accf80142f..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/languages/index.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ElasticsearchDotnet } from './elasticsearch_dotnet'; -import { ElasticsearchGo } from './elasticsearch_go'; -import { ElasticsearchJava } from './elasticsearch_java'; -import { ElasticsearchJavascript } from './elasticsearch_javascript'; -import { ElasticsearchPhp } from './elasticsearch_php'; -import { ElasticsearchPython } from './elasticsearch_python'; -import { ElasticsearchRuby } from './elasticsearch_ruby'; -import { ElasticsearchRust } from './elasticsearch_rust'; - -export { - ElasticsearchDotnet, - ElasticsearchGo, - ElasticsearchJava, - ElasticsearchJavascript, - ElasticsearchPhp, - ElasticsearchPython, - ElasticsearchRuby, - ElasticsearchRust, -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_cloud_id/elasticsearch_cloud_id.test.tsx b/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_cloud_id/elasticsearch_cloud_id.test.tsx deleted file mode 100644 index a6070724bb878..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_cloud_id/elasticsearch_cloud_id.test.tsx +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { setMockValues, mockTelemetryActions } from '../../../__mocks__/kea_logic'; - -import React from 'react'; - -import { shallow, ShallowWrapper } from 'enzyme'; - -import { EuiButtonIcon, EuiCopy, EuiFieldText } from '@elastic/eui'; - -import { ElasticsearchCloudId } from '.'; - -const execCommandMock = (global.document.execCommand = jest.fn()); -const warn = jest.spyOn(console, 'warn').mockImplementation(() => {}); - -describe('Elasticsearch Cloud Id', () => { - let wrapper: ShallowWrapper; - - beforeEach(() => { - setMockValues({ - cloud: { - cloudId: 'example-cloud-id', - deploymentUrl: 'https://cloud.elastic.co/deployments/fake-deployment-id', - }, - }); - wrapper = shallow(); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - describe('Visibility conditions', () => { - it('renders panel when cloud id is provided', () => { - expect(wrapper.find('[data-test-subj="CloudIdPanel"]')).toHaveLength(1); - }); - - it('is hidden when cloud id isnt available', () => { - setMockValues({ cloud: { cloudId: null } }); - wrapper = shallow(); - expect(wrapper.find('[data-test-subj="CloudIdPanel"]')).toHaveLength(0); - }); - }); - - describe('Cloud Id Interactions', () => { - it('should be able copy cloud id', () => { - const field = wrapper.find(EuiFieldText).dive(); - - expect(field.props()).toEqual( - expect.objectContaining({ - readOnly: true, - }) - ); - expect(field.find('input').props()).toEqual( - expect.objectContaining({ - value: 'example-cloud-id', - }) - ); - - const euiCopyHOC = field.dive().find(EuiCopy); - expect(euiCopyHOC.props().textToCopy).toEqual('example-cloud-id'); - const copyButton = euiCopyHOC.dive().find(EuiButtonIcon); - expect(copyButton).toHaveLength(1); - execCommandMock.mockImplementationOnce(() => true); - - copyButton.simulate('click'); - expect(execCommandMock).toHaveBeenCalledWith('copy'); - expect(mockTelemetryActions.sendEnterpriseSearchTelemetry).toHaveBeenCalledWith({ - action: 'clicked', - metric: 'cloud_id', - }); - }); - - it('should fail gracefully if not allowed to copy', () => { - const field = wrapper.find(EuiFieldText).dive(); - const euiCopyHOC = field.dive().find(EuiCopy); - const copyButton = euiCopyHOC.dive().find(EuiButtonIcon); - execCommandMock.mockImplementationOnce(() => false); - - copyButton.simulate('click'); - expect(execCommandMock).toHaveBeenCalledWith('copy'); - expect(warn).toHaveBeenCalledWith('Unable to copy to clipboard.'); - expect(mockTelemetryActions.sendEnterpriseSearchTelemetry).toHaveBeenCalled(); - }); - - it('should present a manage link to deployment screen', () => { - const manageLink = wrapper.find('[data-test-subj="cloudManageLink"]'); - expect(manageLink).toHaveLength(1); - expect(manageLink.props().href).toEqual( - 'https://cloud.elastic.co/deployments/fake-deployment-id' - ); - }); - }); -}); diff --git a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_cloud_id/elasticsearch_cloud_id.tsx b/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_cloud_id/elasticsearch_cloud_id.tsx deleted file mode 100644 index 9e3a59aee83df..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_cloud_id/elasticsearch_cloud_id.tsx +++ /dev/null @@ -1,136 +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 { useActions, useValues } from 'kea'; - -import { - EuiButton, - EuiButtonIcon, - EuiCopy, - EuiFieldText, - EuiFlexGroup, - EuiFlexItem, - EuiForm, - EuiFormRow, - EuiLink, - EuiPanel, - EuiTitle, -} from '@elastic/eui'; - -import { i18n } from '@kbn/i18n'; - -import { useCloudDetails } from '../../../shared/cloud_details/cloud_details'; -import { HttpLogic } from '../../../shared/http'; -import { TelemetryLogic } from '../../../shared/telemetry'; -import { SendTelemetryHelper } from '../../../shared/telemetry/telemetry_logic'; - -const onFocusHandler = (e: React.FocusEvent): void => { - e.target.select(); -}; - -const copyCloudIdHandler = ( - copy: () => void, - sendTelemetry: ({ action, metric }: SendTelemetryHelper) => void -) => { - return () => { - copy(); - sendTelemetry({ - action: 'clicked', - metric: 'cloud_id', - }); - }; -}; - -export const ElasticsearchCloudId: React.FC = () => { - const cloud = useCloudDetails(); - const { sendEnterpriseSearchTelemetry } = useActions(TelemetryLogic); - const { http } = useValues(HttpLogic); - - // hide the panel when no cloud context is available - if (!cloud.cloudId) { - return null; - } - - return ( - - - - - - -

- {i18n.translate('xpack.enterpriseSearch.overview.elasticsearchCloudId.heading', { - defaultMessage: 'My Deployment', - })} -

-
-
-
-
- - - {i18n.translate('xpack.enterpriseSearch.overview.elasticsearchCloudId.manageLink', { - defaultMessage: 'Manage', - })} - - -
- - - - - - {(copy) => ( - - )} - - } - /> - - - - - - - {i18n.translate( - 'xpack.enterpriseSearch.overview.elasticsearchCloudId.manageApiKeysLink', - { - defaultMessage: 'Manage API keys', - } - )} - - - -
- ); -}; diff --git a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_guide/elasticsearch_guide.test.tsx b/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_guide/elasticsearch_guide.test.tsx deleted file mode 100644 index 07849ce476268..0000000000000 --- a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_guide/elasticsearch_guide.test.tsx +++ /dev/null @@ -1,73 +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 { shallow, ShallowWrapper } from 'enzyme'; - -import { EuiSteps, EuiSelect } from '@elastic/eui'; - -import { ElasticsearchClientInstructions } from '../elasticsearch_client_instructions'; - -import { ElasticsearchGuide } from '.'; - -describe('Elasticsearch Guide Component', () => { - let wrapper: ShallowWrapper; - - const setup = (param: string) => { - Object.defineProperty(window, 'location', { - value: { search: param }, - writable: true, - }); - wrapper = shallow(); - }; - - describe('Rendering Language option ', () => { - it('should use default language (java)', () => { - setup(''); - const clientInstructionsElement = wrapper - .find(EuiSteps) - .dive() - .find(ElasticsearchClientInstructions); - expect(clientInstructionsElement.prop('language')).toBe('java'); - }); - - it('should use language from param (ruby)', () => { - setup('?client=ruby'); - const clientInstructionsElement = wrapper - .find(EuiSteps) - .dive() - .find(ElasticsearchClientInstructions); - expect(clientInstructionsElement.prop('language')).toBe('ruby'); - }); - - it('should fallback to java if client not recognised', () => { - setup('?client=coffeescript'); - const clientInstructionsElement = wrapper - .find(EuiSteps) - .dive() - .find(ElasticsearchClientInstructions); - expect(clientInstructionsElement.prop('language')).toBe('java'); - }); - }); - - describe('Changing Language option', () => { - it('should change the client instructions language prop when choosing another option', () => { - setup(''); - const languageSelectElement = wrapper.find(EuiSteps).dive().find(EuiSelect); - languageSelectElement.simulate('change', { target: { value: 'ruby' } }); - - wrapper.update(); - - const clientInstructionsElement = wrapper - .find(EuiSteps) - .dive() - .find(ElasticsearchClientInstructions); - expect(clientInstructionsElement.prop('language')).toBe('ruby'); - }); - }); -}); diff --git a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_guide/elasticsearch_guide.tsx b/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_guide/elasticsearch_guide.tsx index 7da4392ef1112..dd82686a31405 100644 --- a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_guide/elasticsearch_guide.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_guide/elasticsearch_guide.tsx @@ -7,191 +7,66 @@ import React, { useEffect, useState } from 'react'; -import queryString from 'query-string'; +import { useActions, useValues } from 'kea'; -import { - EuiText, - EuiFlexGroup, - EuiFlexItem, - EuiSpacer, - EuiSteps, - EuiSelect, - EuiLink, - useGeneratedHtmlId, -} from '@elastic/eui'; +import { EuiHorizontalRule, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { LanguageDefinitionSnippetArguments } from '@kbn/search-api-panels'; +import { ELASTICSEARCH_URL_PLACEHOLDER } from '@kbn/search-api-panels/constants'; -import { docLinks } from '../../../shared/doc_links'; -import { ElasticsearchResources } from '../../../shared/elasticsearch_resources'; -import { SetElasticsearchChrome as SetPageChrome } from '../../../shared/kibana_chrome'; -import { ElasticsearchClientInstructions } from '../elasticsearch_client_instructions'; -import { ElasticsearchCloudId } from '../elasticsearch_cloud_id'; +import { FetchApiKeysAPILogic } from '../../../enterprise_search_overview/api/fetch_api_keys_logic'; +import { CreateApiKeyFlyout } from '../../../shared/api_key/create_api_key_flyout'; +import { useCloudDetails } from '../../../shared/cloud_details/cloud_details'; +import { GettingStarted } from '../../../shared/getting_started/getting_started'; import { EnterpriseSearchElasticsearchPageTemplate } from '../layout'; -// Replace FormattedMessage with i18n strings +export const ElasticsearchGuide = () => { + const cloudContext = useCloudDetails(); + const [isFlyoutOpen, setIsFlyoutOpen] = useState(false); -export const ElasticsearchGuide: React.FC = () => { - const languages = [ - { value: 'dotnet', text: '.Net' }, - { value: 'go', text: 'Go' }, - { value: 'java', text: 'Java' }, - { value: 'javascript', text: 'JavaScript' }, - { value: 'php', text: 'PHP' }, - { value: 'python', text: 'Python' }, - { value: 'ruby', text: 'Ruby' }, - { value: 'rust', text: 'Rust' }, - ]; - - const client = queryString.parse(window.location.search).client as string; - const languageExists = languages.some((language) => language.value === client); - const [selectedLanguage, setSelectedLanguage] = useState(languageExists ? client : 'java'); - - const basicSelectId = useGeneratedHtmlId({ prefix: 'languageSelect' }); - - const onChange = (e: React.ChangeEvent) => { - setSelectedLanguage(e.target.value); + const codeArgs: LanguageDefinitionSnippetArguments = { + apiKey: '', + cloudId: cloudContext.cloudId, + url: cloudContext.elasticsearchUrl || ELASTICSEARCH_URL_PLACEHOLDER, }; + const { makeRequest } = useActions(FetchApiKeysAPILogic); + const { data } = useValues(FetchApiKeysAPILogic); + const apiKeys = data?.api_keys || []; - // TODO: The page keeps the scroll position if being opened from Enterpise Search Overview, - // This is a temporary solution for demoing - useEffect(() => { - window.scrollTo(0, 0); - }, []); + useEffect(() => makeRequest({}), []); return ( - - - {/* maxWidth is needed to prevent code blocks with long unbreakable strings (Kibana PR Cloud ID) from stretching the column */} - - -

- {i18n.translate( - 'xpack.enterpriseSearch.overview.elasticsearchGuide.elasticsearchTitle', - { - defaultMessage: 'Getting started with Elasticsearch', - } - )} -

-

- {i18n.translate( - 'xpack.enterpriseSearch.overview.elasticsearchGuide.elasticsearchDescription', - { - defaultMessage: - "Elasticsearch provides the low-level tools you need to build fast, relevant search for your website or application. Because it's powerful and flexible, Elasticsearch can handle search use cases of all shapes and sizes.", - } - )} -

-
- - - - - -

- {i18n.translate( - 'xpack.enterpriseSearch.overview.elasticsearchGuide.connectToElasticsearchDescription', - { - defaultMessage: - 'Elastic builds and maintains clients in several popular languages and our community has contributed many more.', - } - )} -

- - {i18n.translate( - 'xpack.enterpriseSearch.overview.elasticsearchGuide.elasticsearchClientsLink', - { defaultMessage: 'Learn more about Elasticsearch clients' } - )} - -
- - - - onChange(e)} - aria-label={i18n.translate( - 'xpack.enterpriseSearch.overview.elasticsearchGuide.elasticsearchClientsSelectAriaLabel', - { defaultMessage: 'Language client' } - )} - /> - - - - ), - }, - { - title: i18n.translate( - 'xpack.enterpriseSearch.overview.elasticsearchGuide.elasticsearchSearchExperienceTitle', - { defaultMessage: 'Build a search experience with Elasticsearch' } - ), - children: ( - <> - -

- {i18n.translate( - 'xpack.enterpriseSearch.overview.elasticsearchGuide.elasticsearchSearchExperienceDescription', - { - defaultMessage: - 'Ready to add an engaging, modern search experience to your application or website? Search UI, Elastic’s JavaScript search framework for building world-class search experiences, was made for the task.', - } - )} -

-
- - - - - - {i18n.translate( - 'xpack.enterpriseSearch.overview.elasticsearchGuide.elasticsearchSearchUIMarketingLink', - { defaultMessage: 'Learn more about Search UI' } - )} - - - - - - - {i18n.translate( - 'xpack.enterpriseSearch.overview.elasticsearchGuide.elasticsearchSearchUIGitHubLink', - { defaultMessage: 'Search UI on GitHub' } - )} - - - - - - ), - }, - ]} - /> -
- - - - - -
+ {isFlyoutOpen && setIsFlyoutOpen(false)} />} + +

+ {i18n.translate('xpack.enterpriseSearch.content.overview.gettingStarted.pageTitle', { + defaultMessage: 'Elasticsearch language clients', + })} +

+
+ + +

+ {i18n.translate( + 'xpack.enterpriseSearch.content.overview.gettingStarted.pageDescription', + { + defaultMessage: + "Set up your programming language client, ingest some data, and you'll be ready to start searching within minutes.", + } + )} +

+
+ + + setIsFlyoutOpen(true)} + codeArgs={codeArgs} + isPanelLeft + showPipelinesPanel + />
); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/layout/page_template.tsx b/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/layout/page_template.tsx index f854246a9f9a7..81ba64913879e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/layout/page_template.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/layout/page_template.tsx @@ -22,6 +22,7 @@ export const EnterpriseSearchElasticsearchPageTemplate: React.FC = ({ isCrawler }) => { > {i18n.translate( 'xpack.enterpriseSearch.connectors.newConnectorsClientButtonLabel', - { defaultMessage: 'New Connectors Client' } + { defaultMessage: 'New Connector Client' } )} , ] diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/select_connector/connector_checkable.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/select_connector/connector_checkable.tsx index d2f5fd7e13fb3..d225f8175721c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/select_connector/connector_checkable.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/select_connector/connector_checkable.tsx @@ -12,16 +12,16 @@ import { css } from '@emotion/react'; import { EuiBadge, EuiButtonIcon, - EuiCard, EuiContextMenuItem, EuiContextMenuPanel, EuiFlexGroup, EuiFlexItem, EuiIcon, EuiLink, + EuiPanel, EuiPopover, - EuiSpacer, EuiText, + EuiThemeComputed, EuiTitle, useEuiTheme, } from '@elastic/eui'; @@ -30,7 +30,6 @@ import { i18n } from '@kbn/i18n'; import { BETA_LABEL, NATIVE_LABEL, CONNECTOR_CLIENT_LABEL } from '../../../../shared/constants'; -import './connector_checkable.scss'; import { PlatinumLicensePopover } from '../../shared/platinum_license_popover/platinum_license_popover'; export interface ConnectorCheckableProps { @@ -45,6 +44,22 @@ export interface ConnectorCheckableProps { showNativeBadge: boolean; } +const getCss = ( + euiTheme: EuiThemeComputed, + isDisabled: ConnectorCheckableProps['isDisabled'], + showNativeBadge: ConnectorCheckableProps['showNativeBadge'] +) => { + return css` + ${showNativeBadge && + `box-shadow: 8px 9px 0px -1px ${euiTheme.colors.lightestShade}, + 8px 9px 0px 0px ${euiTheme.colors.lightShade};`} + ${isDisabled && + `background-color: ${euiTheme.colors.lightestShade}; + color: ${euiTheme.colors.disabledText}; + `} + `; +}; + export const ConnectorCheckable: React.FC = ({ isDisabled, documentationUrl, @@ -60,175 +75,176 @@ export const ConnectorCheckable: React.FC = ({ const [isLicensePopoverOpen, setIsLicensePopoverOpen] = useState(false); const [isNativePopoverOpen, setIsNativePopoverOpen] = useState(false); return ( - { - if (isDisabled) return; - onConnectorSelect(showNativeBadge); - }, - } - : {})} - hasBorder + { + if (isDisabled && showNativeBadge) return; + onConnectorSelect(showNativeBadge); + }} id={`checkableCard-${serviceType}`} - css={ - showNativeBadge - ? css` - box-shadow: 8px 9px 0px -1px ${euiTheme.colors.lightestShade}, - 8px 9px 0px 0px ${euiTheme.colors.lightShade}; - ` - : undefined - } - layout="horizontal" + css={getCss(euiTheme, isDisabled, showNativeBadge)} + hasBorder data-telemetry-id={`entSearchContent-connector-selectConnector-${serviceType}-select`} - icon={iconType ? : undefined} - title={ - - - - - {isDisabled ? ( - -

{name}

-
- ) : ( - -

{name}

-
- )} -
- {isDisabled && ( - - + + + {iconType ? : null} + + + + + + + + + {isDisabled ? ( + +

{name}

+
+ ) : ( + +

{name}

+
+ )} +
+ {isDisabled && ( + + { + event.preventDefault(); + event.stopPropagation(); + setIsLicensePopoverOpen(!isLicensePopoverOpen); + }} + /> } - )} - iconType="questionInCircle" - onClick={() => setIsLicensePopoverOpen(!isLicensePopoverOpen)} - /> - } - closePopover={() => setIsLicensePopoverOpen(false)} - isPopoverOpen={isLicensePopoverOpen} - /> + closePopover={() => setIsLicensePopoverOpen(false)} + isPopoverOpen={isLicensePopoverOpen} + /> + + )} +
- )} -
-
- {showNativeBadge && ( - - { - e.stopPropagation(); - e.preventDefault(); - setIsNativePopoverOpen(true); - }} - /> - } - isOpen={isNativePopoverOpen} - closePopover={() => { - setIsNativePopoverOpen(false); - }} - > - { - e.stopPropagation(); - onConnectorSelect(true); - }} - > - {i18n.translate( - 'xpack.enterpriseSearch.connectorCheckable.setupANativeConnectorContextMenuItemLabel', - { defaultMessage: 'Setup a Native Connector' } - )} - , - { - e.stopPropagation(); - onConnectorSelect(false); + {showNativeBadge && ( + + { + e.stopPropagation(); + e.preventDefault(); + setIsNativePopoverOpen(true); + }} + /> + } + isOpen={isNativePopoverOpen} + closePopover={() => { + setIsNativePopoverOpen(false); }} > - {i18n.translate( - 'xpack.enterpriseSearch.connectorCheckable.setupAConnectorClientContextMenuItemLabel', - { defaultMessage: 'Setup a Connector Client' } - )} - , - ]} - /> - - - )} -
- } - > - - - - - - - - {showNativeBadge ? NATIVE_LABEL : CONNECTOR_CLIENT_LABEL} - - + { + e.stopPropagation(); + onConnectorSelect(true); + }} + > + {i18n.translate( + 'xpack.enterpriseSearch.connectorCheckable.setupANativeConnectorContextMenuItemLabel', + { defaultMessage: 'Setup a Native Connector' } + )} + , + { + e.stopPropagation(); + onConnectorSelect(false); + }} + > + {i18n.translate( + 'xpack.enterpriseSearch.connectorCheckable.setupAConnectorClientContextMenuItemLabel', + { defaultMessage: 'Setup a Connector Client' } + )} + , + ]} + /> + + + )} + - {isBeta && ( - - - {BETA_LABEL} - - - )} - {isTechPreview && ( - - - - {i18n.translate( - 'xpack.enterpriseSearch.content.indices.selectConnector.connectorCheckable.techPreviewLabel', - { - defaultMessage: 'Tech preview', - } + + + + + + + + {showNativeBadge ? NATIVE_LABEL : CONNECTOR_CLIENT_LABEL} + + + + {isBeta && ( + + + {BETA_LABEL} + + )} - - - - )} + {isTechPreview && ( + + + + {i18n.translate( + 'xpack.enterpriseSearch.content.indices.selectConnector.connectorCheckable.techPreviewLabel', + { + defaultMessage: 'Tech preview', + } + )} + + + + )} + +
+ {documentationUrl && ( + + + + {i18n.translate( + 'xpack.enterpriseSearch.content.indices.selectConnector.connectorCheckable.documentationLinkLabel', + { + defaultMessage: 'Documentation', + } + )} + + + + )} +
+
- {documentationUrl && ( - - - - {i18n.translate( - 'xpack.enterpriseSearch.content.indices.selectConnector.connectorCheckable.documentationLinkLabel', - { - defaultMessage: 'Documentation', - } - )} - - - - )}
-
+ ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/select_connector/connector_description_badge_popout.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/select_connector/connector_description_badge_popout.tsx new file mode 100644 index 0000000000000..7b1d9bbee5e95 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/select_connector/connector_description_badge_popout.tsx @@ -0,0 +1,153 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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, + EuiIcon, + EuiPanel, + EuiPopover, + EuiText, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; + +import connectorLogo from '../../../../../assets/source_icons/network_drive.svg'; + +const nativePopoverPanels = [ + { + description: i18n.translate( + 'xpack.enterpriseSearch.connectorDescriptionBadge.native.chooseADataSourceLabel', + { defaultMessage: "Choose a data source you'd like to sync" } + ), + icons: [], + id: 'native-choose-source', + }, + { + description: i18n.translate( + 'xpack.enterpriseSearch.connectorDescriptionBadge.native.configureConnectorLabel', + { defaultMessage: 'Configure your connector using our Kibana UI' } + ), + icons: [, ], + id: 'native-configure-connector', + }, +]; + +const connectorClientPopoverPanels = [ + { + description: i18n.translate( + 'xpack.enterpriseSearch.connectorDescriptionBadge.client.chooseADataSourceLabel', + { defaultMessage: "Choose a data source you'd like to sync" } + ), + icons: [], + id: 'client-choose-source', + }, + { + description: i18n.translate( + 'xpack.enterpriseSearch.connectorDescriptionBadge.client.configureConnectorLabel', + { + defaultMessage: + 'Deploy connector code on your own infrastructure by running from source, or using Docker', + } + ), + icons: [ + , + , + , + ], + id: 'client-deploy', + }, + { + description: i18n.translate( + 'xpack.enterpriseSearch.connectorDescriptionBadge.client.enterDetailsLabel', + { + defaultMessage: 'Enter access and connection details for your data source', + } + ), + icons: [ + , + , + , + , + , + ], + id: 'client-configure-connector', + }, +]; + +export interface ConnectorDescriptionBadgeProps { + isNative: boolean; +} + +export const ConnectorDescriptionBadge: React.FC = ({ + isNative, +}) => { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const panels = isNative ? nativePopoverPanels : connectorClientPopoverPanels; + return ( + setIsPopoverOpen(true)} + onClickAriaLabel={i18n.translate( + 'xpack.enterpriseSearch.selectConnector.badgeOnClick.ariaLabel', + { + defaultMessage: 'Click to open connector explanation popover', + } + )} + > + {isNative + ? i18n.translate('xpack.enterpriseSearch.selectConnector.nativeBadgeLabel', { + defaultMessage: 'Native', + }) + : i18n.translate('xpack.enterpriseSearch.selectConnector.connectorClientBadgeLabel', { + defaultMessage: 'Connector client', + })} + + } + isOpen={isPopoverOpen} + closePopover={() => { + setIsPopoverOpen(false); + }} + > + + + {panels.map((panel) => { + return ( + + + + + {panel.icons.map((icon, index) => ( + + {icon} + + ))} + + + + +

{panel.description}

+
+
+
+
+ ); + })} +
+
+
+ ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/select_connector/select_connector.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/select_connector/select_connector.tsx index 764d3ac0d8242..8c75fa9a4b1b6 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/select_connector/select_connector.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/new_index/select_connector/select_connector.tsx @@ -13,8 +13,8 @@ import { css } from '@emotion/react'; import { useValues } from 'kea'; import { - EuiBadge, EuiButton, + EuiCallOut, EuiFacetButton, EuiFacetGroup, EuiFieldSearch, @@ -32,6 +32,7 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; import { CONNECTOR_CLIENTS_TYPE, @@ -55,6 +56,7 @@ import { CONNECTORS } from '../../search_index/connector/constants'; import { baseBreadcrumbs } from '../../search_indices'; import { ConnectorCheckable } from './connector_checkable'; +import { ConnectorDescriptionBadge } from './connector_description_badge_popout'; export type ConnectorFilter = typeof CONNECTOR_NATIVE_TYPE | typeof CONNECTOR_CLIENTS_TYPE; @@ -108,7 +110,12 @@ export const SelectConnector: React.FC = () => { return ( {
- - {i18n.translate('xpack.enterpriseSearch.selectConnector.nativeBadgeLabel', { - defaultMessage: 'Native', - })} - +

@@ -222,7 +225,7 @@ export const SelectConnector: React.FC = () => { 'xpack.enterpriseSearch.selectConnector.p.areAvailableDirectlyWithinLabel', { defaultMessage: - 'Are available directly within Elastic Cloud deployments No additional infrastructure is required You can also convert them as self hosted Connectors client at any moment', + 'Available directly within Elastic Cloud deployments. No additional infrastructure is required. You can also convert native connectors to self-hosted connector clients.', } )}

@@ -248,12 +251,7 @@ export const SelectConnector: React.FC = () => { - - {i18n.translate( - 'xpack.enterpriseSearch.selectConnector.connectorClientBadgeLabel', - { defaultMessage: 'Connector client' } - )} - +

@@ -261,7 +259,7 @@ export const SelectConnector: React.FC = () => { 'xpack.enterpriseSearch.selectConnector.p.deployConnectorsOnYourLabel', { defaultMessage: - 'Deploy connectors on your own infrastructure You can also customize existing Connector clients or build your own using our connector framework', + 'Deploy connectors on your own infrastructure. You can also customize existing connector clients, or build your own using our connector framework.', } )}

@@ -327,6 +325,41 @@ export const SelectConnector: React.FC = () => { ))} + {!hasNativeAccess && useNativeFilter && ( + <> + + +

+ +

+ + + +
+ + )} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/getting_started/getting_started.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/getting_started/getting_started.tsx index d142746e308f4..7bf59e89d7d80 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/getting_started/getting_started.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/getting_started/getting_started.tsx @@ -5,65 +5,29 @@ * 2.0. */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect } from 'react'; -import { css } from '@emotion/react'; -import dedent from 'dedent'; import { useActions, useValues } from 'kea'; -import { - EuiButton, - EuiCodeBlock, - EuiFlexGroup, - EuiFlexItem, - EuiPanel, - EuiSpacer, - EuiSplitPanel, - EuiText, - EuiThemeProvider, - EuiTitle, -} from '@elastic/eui'; +import { EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { - SelectClientPanel, - LanguageDefinition, - LanguageDefinitionSnippetArguments, - LanguageClientPanel, - InstallClientPanel, - OverviewPanel, - CodeBox, - getLanguageDefinitionCodeSnippet, - getConsoleRequest, -} from '@kbn/search-api-panels'; +import { LanguageDefinitionSnippetArguments } from '@kbn/search-api-panels'; -import { PLUGIN_ID } from '../../../../../../../common/constants'; -import { KibanaDeps } from '../../../../../../../common/types'; - -import { icons } from '../../../../../../assets/client_libraries'; import { useCloudDetails } from '../../../../../shared/cloud_details/cloud_details'; -import { docLinks } from '../../../../../shared/doc_links'; -import { HttpLogic } from '../../../../../shared/http'; -import { KibanaLogic } from '../../../../../shared/kibana'; +import { GettingStarted } from '../../../../../shared/getting_started/getting_started'; import { IndexViewLogic } from '../../index_view_logic'; import { OverviewLogic } from '../../overview.logic'; import { GenerateApiKeyModal } from '../generate_api_key_modal/modal'; -import { consoleDefinition } from './languages/console'; -import { curlDefinition } from './languages/curl'; -import { languageDefinitions } from './languages/languages'; - const DEFAULT_URL = 'https://localhost:9200'; export const APIGettingStarted = () => { - const { http } = useValues(HttpLogic); const { apiKey, isGenerateModalOpen, indexPipelineParameters } = useValues(OverviewLogic); const { fetchIndexPipelineParameters, openGenerateModal, closeGenerateModal } = useActions(OverviewLogic); const { indexName } = useValues(IndexViewLogic); - const { services } = useKibana(); const cloudContext = useCloudDetails(); @@ -83,9 +47,7 @@ export const APIGettingStarted = () => { ingestPipeline: indexPipelineParameters.name, url: cloudContext.elasticsearchUrl || DEFAULT_URL, }; - const assetBasePath = http.basePath.prepend(`/plugins/${PLUGIN_ID}/assets/client_libraries`); - const [selectedLanguage, setSelectedLanguage] = useState(curlDefinition); return ( <> {isGenerateModalOpen && ( @@ -93,322 +55,12 @@ export const APIGettingStarted = () => { )}

- {i18n.translate('xpack.enterpriseSearch.content.overview.gettingStarted.pageTitle', { - defaultMessage: 'Getting Started with Elastic API', + {i18n.translate('xpack.enterpriseSearch.gettingStarted.pageTitle', { + defaultMessage: 'Getting started with Elastic API', })}

- - {languageDefinitions.map((language, index) => ( - - - - ))} - - - - - - - -
- {i18n.translate( - 'xpack.enterpriseSearch.content.overview.gettingStarted.generateApiKeyPanel.apiKeytitle', - { - defaultMessage: 'Generate an API key', - } - )} -
-
- - - {i18n.translate( - 'xpack.enterpriseSearch.content.overview.gettingStarted.generateApiKeyPanel.apiKeydesc', - { - defaultMessage: - 'Your private, unique identifier for authentication and authorization.', - } - )} - -
- - - - - -

- {i18n.translate( - 'xpack.enterpriseSearch.content.overview.documementExample.generateApiKeyButton.createNew', - { defaultMessage: 'New' } - )} -

-
-
-
- - - KibanaLogic.values.navigateToUrl('/app/management/security/api_keys', { - shouldNotCreateHref: true, - }) - } - > - -

- {i18n.translate( - 'xpack.enterpriseSearch.content.overview.documementExample.generateApiKeyButton.viewAll', - { defaultMessage: 'Manage' } - )} -

-
-
-
-
-
-
- - } - links={[]} - title={i18n.translate( - 'xpack.enterpriseSearch.content.overview.gettingStarted.generateApiKeyPanel.panelTitle', - { - defaultMessage: 'Generate an API key', - } - )} - overviewPanelProps={{ color: 'plain', hasShadow: false }} - /> - - - - -
- {i18n.translate( - 'xpack.enterpriseSearch.content.overview.gettingStarted.cloudId.elasticTitle', - { - defaultMessage: 'Store your Elasticsearch URL', - } - )} -
-
- - {i18n.translate( - 'xpack.enterpriseSearch.content.overview.gettingStarted.cloudId.desc', - { - defaultMessage: 'Unique identifier for your deployment. ', - } - )} - -
- - - - {codeArgs.cloudId - ? dedent`{ - CloudID: "${codeArgs.cloudId}", - Url: "${codeArgs.url}", - }` - : codeArgs.url} - - - - - } - links={[]} - title={i18n.translate( - 'xpack.enterpriseSearch.overview.gettingStarted.cloudId.panelTitleElastic', - { - defaultMessage: 'Copy your Elasticsearch URL', - } - )} - overviewPanelProps={{ color: 'plain', hasShadow: false }} - /> - - - } - links={[]} - title={i18n.translate( - 'xpack.enterpriseSearch.overview.gettingStarted.configureClient.title', - { - defaultMessage: 'Configure your client', - } - )} - overviewPanelProps={{ color: 'plain', hasShadow: false }} - /> - - - } - links={[]} - title={i18n.translate( - 'xpack.enterpriseSearch.overview.gettingStarted.testConnection.title', - { - defaultMessage: 'Test your connection', - } - )} - overviewPanelProps={{ color: 'plain', hasShadow: false }} - /> - - } - links={[]} - title={i18n.translate('xpack.enterpriseSearch.overview.gettingStarted.ingestData.title', { - defaultMessage: 'Ingest Data', - })} - overviewPanelProps={{ color: 'plain', hasShadow: false }} - /> - - - } - links={[]} - title={i18n.translate('xpack.enterpriseSearch.overview.gettingStarted.searchQuery.title', { - defaultMessage: 'Build your first search query', - })} - overviewPanelProps={{ color: 'plain', hasShadow: false }} - /> + ); }; 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 6a5f9a17e5e3d..93566c21fe999 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 @@ -319,7 +319,7 @@ export const ConnectorConfiguration: React.FC = () => { title: i18n.translate( 'xpack.enterpriseSearch.content.indices.configurationConnector.steps.schedule.title', { - defaultMessage: 'Advanced configuration', + defaultMessage: 'Sync your data', } ), titleSize: 'xs', diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/constants.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/constants.ts index b60699a263603..1da16c533a337 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/constants.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/constants.ts @@ -21,6 +21,7 @@ export const CONNECTORS_DICT: Record = { externalAuthDocsUrl: 'https://learn.microsoft.com/azure/storage/common/authorize-data-access', externalDocsUrl: 'https://learn.microsoft.com/azure/storage/blobs/', icon: CONNECTOR_ICONS.azure_blob_storage, + platinumOnly: true, }, box: { docsUrl: docLinks.connectorsBox, @@ -68,6 +69,7 @@ export const CONNECTORS_DICT: Record = { externalAuthDocsUrl: 'https://cloud.google.com/storage/docs/authentication', externalDocsUrl: 'https://cloud.google.com/storage/docs', icon: CONNECTOR_ICONS.google_cloud_storage, + platinumOnly: true, }, google_drive: { docsUrl: docLinks.connectorsGoogleDrive, @@ -88,6 +90,7 @@ export const CONNECTORS_DICT: Record = { externalAuthDocsUrl: 'https://www.mongodb.com/docs/atlas/app-services/authentication/', externalDocsUrl: 'https://www.mongodb.com/docs/', icon: CONNECTOR_ICONS.mongodb, + platinumOnly: true, }, mssql: { docsUrl: docLinks.connectorsMicrosoftSQL, @@ -95,11 +98,13 @@ export const CONNECTORS_DICT: Record = { 'https://learn.microsoft.com/sql/relational-databases/security/authentication-access/getting-started-with-database-engine-permissions', externalDocsUrl: 'https://learn.microsoft.com/sql/', icon: CONNECTOR_ICONS.microsoft_sql, + platinumOnly: true, }, mysql: { docsUrl: docLinks.connectorsMySQL, externalDocsUrl: 'https://dev.mysql.com/doc/', icon: CONNECTOR_ICONS.mysql, + platinumOnly: true, }, network_drive: { docsUrl: docLinks.connectorsNetworkDrive, @@ -140,6 +145,7 @@ export const CONNECTORS_DICT: Record = { externalAuthDocsUrl: 'https://docs.aws.amazon.com/s3/index.html', externalDocsUrl: '', icon: CONNECTOR_ICONS.amazon_s3, + platinumOnly: true, }, salesforce: { docsUrl: docLinks.connectorsSalesforce, diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/native_connector_configuration/native_connector_configuration.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/native_connector_configuration/native_connector_configuration.tsx index df4155cb28d65..252a135f44a55 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/native_connector_configuration/native_connector_configuration.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/connector/native_connector_configuration/native_connector_configuration.tsx @@ -174,7 +174,7 @@ export const NativeConnectorConfiguration: React.FC = () => { title: i18n.translate( 'xpack.enterpriseSearch.content.indices.configurationConnector.nativeConnector.steps.advancedConfigurationTitle', { - defaultMessage: 'Advanced configuration', + defaultMessage: 'Sync your data', } ), titleSize: 'xs', diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/inference_pipeline_card.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/inference_pipeline_card.test.tsx index 970b1488010c0..62ac53004a773 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/inference_pipeline_card.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/inference_pipeline_card.test.tsx @@ -11,11 +11,11 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { EuiButtonIcon, EuiPanel, EuiTextColor, EuiTitle } from '@elastic/eui'; +import { EuiButtonEmpty, EuiPanel, EuiText, EuiTitle } from '@elastic/eui'; import { InferencePipeline, TrainedModelState } from '../../../../../../common/types/pipelines'; -import { InferencePipelineCard } from './inference_pipeline_card'; +import { InferencePipelineCard, TrainedModelHealthPopover } from './inference_pipeline_card'; import { MLModelTypeBadge } from './ml_model_type_badge'; export const DEFAULT_VALUES: InferencePipeline = { @@ -40,7 +40,7 @@ describe('InferencePipelineCard', () => { it('renders pipeline as title', () => { const wrapper = shallow(); expect(wrapper.find(EuiTitle)).toHaveLength(1); - const title = wrapper.find(EuiTitle).dive(); + const title = wrapper.find(EuiTitle).at(0).children(); expect(title.text()).toBe(DEFAULT_VALUES.pipelineName); }); it('renders pipeline as title with unknown model type', () => { @@ -51,14 +51,14 @@ describe('InferencePipelineCard', () => { const wrapper = shallow(); expect(wrapper.find(EuiTitle)).toHaveLength(1); // does not render subtitle - expect(wrapper.find(EuiTextColor)).toHaveLength(0); - const title = wrapper.find(EuiTitle).dive(); + expect(wrapper.find(EuiText)).toHaveLength(0); + const title = wrapper.find(EuiTitle).at(0).children(); expect(title.text()).toBe(DEFAULT_VALUES.pipelineName); }); it('renders model ID as subtitle', () => { const wrapper = shallow(); - expect(wrapper.find(EuiTextColor)).toHaveLength(1); - const subtitle = wrapper.find(EuiTextColor).dive(); + expect(wrapper.find(EuiText)).toHaveLength(1); + const subtitle = wrapper.find(EuiText).at(0).children(); expect(subtitle.text()).toBe(DEFAULT_VALUES.modelId); }); it('renders model type as badge', () => { @@ -67,20 +67,28 @@ describe('InferencePipelineCard', () => { const badge = wrapper.find(MLModelTypeBadge).render(); expect(badge.text()).toBe('ner'); }); - it('renders fix button when model not deployed', () => { +}); + +describe('TrainedModelHealthPopover', () => { + beforeEach(() => { + jest.clearAllMocks(); + setMockValues(mockValues); + }); + it('popover renders fix button when model not deployed', () => { const values = { ...DEFAULT_VALUES, modelState: TrainedModelState.NotDeployed, }; - const wrapper = shallow(); - expect(wrapper.find(EuiButtonIcon)).toHaveLength(1); + const wrapper = shallow(); + expect(wrapper.find(EuiButtonEmpty)).toHaveLength(3); - const fixButton = wrapper.find(EuiButtonIcon); + const fixButton = wrapper.find(EuiButtonEmpty).at(0); expect(fixButton.prop('iconType')).toBe('wrench'); expect(fixButton.prop('href')).toBe('/app/ml/trained_models'); + expect(fixButton.children().text()).toBe('Fix issue in Trained Models'); }); - it('does not render fix button when model deployed', () => { - const wrapper = shallow(); - expect(wrapper.find(EuiButtonIcon)).toHaveLength(0); + it('popover does not render fix button when model deployed', () => { + const wrapper = shallow(); + expect(wrapper.find(EuiButtonEmpty)).toHaveLength(2); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/inference_pipeline_card.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/inference_pipeline_card.tsx index dcc4b2a0cad8f..342d05c398878 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/inference_pipeline_card.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/inference_pipeline_card.tsx @@ -11,16 +11,14 @@ import { useActions, useValues } from 'kea'; import { EuiButtonEmpty, - EuiButtonIcon, EuiConfirmModal, EuiFlexGroup, EuiFlexItem, EuiPanel, EuiPopover, EuiText, - EuiTextColor, EuiTitle, - EuiToolTip, + useIsWithinMaxBreakpoint, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -39,20 +37,18 @@ import { TrainedModelHealth } from './ml_model_health'; import { MLModelTypeBadge } from './ml_model_type_badge'; import { PipelinesLogic } from './pipelines_logic'; -export const InferencePipelineCard: React.FC = (pipeline) => { +export const TrainedModelHealthPopover: React.FC = (pipeline) => { const { http } = useValues(HttpLogic); const { indexName } = useValues(IndexNameLogic); const { ingestionMethod } = useValues(IndexViewLogic); + + const { deleteMlPipeline, detachMlPipeline } = useActions(PipelinesLogic); + const [isPopOverOpen, setIsPopOverOpen] = useState(false); const [showConfirmDelete, setShowConfirmDelete] = useState(false); - const { deleteMlPipeline, detachMlPipeline } = useActions(PipelinesLogic); - const showConfirmDeleteModal = () => { - setShowConfirmDelete(true); - setIsPopOverOpen(false); - }; - const { modelId, pipelineName, types: modelTypes } = pipeline; - const modelType = getMLType(modelTypes); - const modelTitle = getModelDisplayTitle(modelType); + + const { pipelineName } = pipeline; + const actionButton = ( = (pipeline) => ); + const showConfirmDeleteModal = () => { + setShowConfirmDelete(true); + setIsPopOverOpen(false); + }; + return ( - - - - + <> + setIsPopOverOpen(false)} + > + + {pipeline.modelState === TrainedModelState.NotDeployed && ( - - - -

{pipelineName ?? modelTitle}

-
-
- -
-
- {modelTitle && ( - - - - {modelId} - - - - - - - - - )} -
-
- - setIsPopOverOpen(false)} - > - {pipeline.modelState === TrainedModelState.NotDeployed && ( - - + + {i18n.translate( 'xpack.enterpriseSearch.inferencePipelineCard.modelState.notDeployed.fixLink', - { defaultMessage: 'Fix issue in Trained Models' } + { + defaultMessage: 'Fix issue in Trained Models', + } )} - > - - - - )} - - -
- - {i18n.translate('xpack.enterpriseSearch.inferencePipelineCard.action.view', { - defaultMessage: 'View in Stack Management', - })} - -
-
- -
- { - detachMlPipeline({ indexName, pipelineName }); - setIsPopOverOpen(false); - }} - > - {i18n.translate('xpack.enterpriseSearch.inferencePipelineCard.action.detach', { - defaultMessage: 'Detach pipeline', - })} - -
-
- -
- -
-
-
-
-
-
+ + +
+ )} + + + + {i18n.translate('xpack.enterpriseSearch.inferencePipelineCard.action.view', { + defaultMessage: 'View in Stack Management', + })} + + + + + + { + detachMlPipeline({ indexName, pipelineName }); + setIsPopOverOpen(false); + }} + > + {i18n.translate('xpack.enterpriseSearch.inferencePipelineCard.action.detach', { + defaultMessage: 'Detach pipeline', + })} + + + + + + + + +
+ {showConfirmDelete && ( = (pipeline) =>
)} + + ); +}; + +export const InferencePipelineCard: React.FC = (pipeline) => { + const { modelId, pipelineName, types: modelTypes } = pipeline; + const modelType = getMLType(modelTypes); + const modelTitle = getModelDisplayTitle(modelType); + const isSmallScreen = useIsWithinMaxBreakpoint('s'); + + return ( + + + + + + +

{pipelineName ?? modelTitle}

+
+
+ {modelTitle && ( + + + + + {modelId} + + + + + + + + + + )} +
+
+ + + +
); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/configure_pipeline.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/configure_pipeline.tsx index cc318831555af..52d4f38b45408 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/configure_pipeline.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/configure_pipeline.tsx @@ -19,6 +19,7 @@ import { EuiTabbedContentTab, EuiTitle, EuiText, + EuiTextColor, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -130,15 +131,27 @@ export const ConfigurePipeline: React.FC = () => { )} - - - + + +
+ {i18n.translate( + 'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.titleSelectTrainedModel', + { defaultMessage: 'Select a trained ML Model' } + )} +
+
+ {formErrors.modelStatus !== undefined && ( + <> + + +

+ {formErrors.modelStatus} +

+
+ + )} + + ), diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/ml_inference_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/ml_inference_logic.test.ts index a725371de0242..7412d861d7136 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/ml_inference_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/ml_inference_logic.test.ts @@ -550,6 +550,25 @@ describe('MlInferenceLogic', () => { pipelineName: 'Name already used by another pipeline.', }); }); + it('has errors when non-deployed model is selected', () => { + MLInferenceLogic.actions.setInferencePipelineConfiguration({ + ...MLInferenceLogic.values.addInferencePipelineModal.configuration, + pipelineName: 'unit-test-pipeline', + modelID: 'unit-test-model', + existingPipeline: false, + fieldMappings: [ + { + sourceField: 'body', + targetField: 'ml.inference.body', + }, + ], + isModelPlaceholderSelected: true, + }); + + expect(MLInferenceLogic.values.formErrors).toEqual({ + modelStatus: 'Model must be deployed before use.', + }); + }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_select.test.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_select.test.tsx index 15fb492fae56d..9bd006f65883e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_select.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_select.test.tsx @@ -98,6 +98,23 @@ describe('ModelSelect', () => { }) ); }); + it('sets placeholder flag on selecting a placeholder item', () => { + setMockValues(DEFAULT_VALUES); + + const wrapper = shallow(); + expect(wrapper.find(EuiSelectable)).toHaveLength(1); + const selectable = wrapper.find(EuiSelectable); + selectable.simulate('change', [ + { modelId: 'model_1' }, + { modelId: 'model_2', isPlaceholder: true, checked: 'on' }, + ]); + expect(MOCK_ACTIONS.setInferencePipelineConfiguration).toHaveBeenCalledWith( + expect.objectContaining({ + modelID: 'model_2', + isModelPlaceholderSelected: true, + }) + ); + }); it('generates pipeline name on selecting an item', () => { setMockValues(DEFAULT_VALUES); diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_select.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_select.tsx index 86c91c483702f..ac3900b6ed662 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_select.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/model_select.tsx @@ -44,6 +44,7 @@ export const ModelSelect: React.FC = () => { ...configuration, inferenceConfig: undefined, modelID: selectedOption?.modelId ?? '', + isModelPlaceholderSelected: selectedOption?.isPlaceholder ?? false, fieldMappings: undefined, pipelineName: isPipelineNameUserSupplied ? pipelineName diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/pipeline_select.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/pipeline_select.tsx index 582348fd86e2b..9295c5440a054 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/pipeline_select.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/pipeline_select.tsx @@ -5,11 +5,11 @@ * 2.0. */ -import React from 'react'; +import React, { useState } from 'react'; import { useActions, useValues } from 'kea'; -import { EuiSelectable, useIsWithinMaxBreakpoint } from '@elastic/eui'; +import { EuiSelectable, useEuiTheme, useIsWithinMaxBreakpoint } from '@elastic/eui'; import { MLInferenceLogic, MLInferencePipelineOption } from './ml_inference_logic'; import { PipelineSelectOption, PipelineSelectOptionProps } from './pipeline_select_option'; @@ -23,6 +23,15 @@ export const PipelineSelect: React.FC = () => { const { pipelineName } = configuration; + const { euiTheme } = useEuiTheme(); + const largeScreenRowHeight = euiTheme.base * 6; + const smallScreenRowHeight = euiTheme.base * 8; + const maxVisibleOptions = 4.5; + const rowHeight: number = useIsWithinMaxBreakpoint('s') + ? smallScreenRowHeight + : largeScreenRowHeight; + const [height, setHeight] = useState(maxVisibleOptions * rowHeight); + const getPipelineOptions = ( pipelineOptions: MLInferencePipelineOption[] ): PipelineSelectOptionProps[] => { @@ -45,28 +54,25 @@ export const PipelineSelect: React.FC = () => { } }; - const getActiveOptionIndex = (): number | undefined => { - const index = existingInferencePipelines.findIndex( - (pipelineOption) => pipelineOption.pipelineName === pipelineName - ); - - return index >= 0 ? index : undefined; - }; - return ( { + setHeight(Math.min(maxVisibleOptions, matchingOptions.length) * rowHeight); + }, }} searchable singleSelection="always" onChange={onChange} renderOption={renderPipelineOption} + height={height} > {(list, search) => ( <> diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/pipeline_select_option.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/pipeline_select_option.tsx index deea940cfe68d..49e00f30c12d4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/pipeline_select_option.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/pipeline_select_option.tsx @@ -45,21 +45,21 @@ export const PipelineSelectOption: React.FC = ({ pipe // TODO: Add model state & pipeline info link. Make sure to check mobile rendering when doing this! - -
{pipeline.pipelineName}
+ +

{pipeline.pipelineName}

- + {modelIdDisplay} {pipeline.modelType.length > 0 && ( - {/* Wrap in a div to prevent the badge from growing to a whole row on mobile*/} -
+ {/* Wrap in a span to prevent the badge from growing to a whole row on mobile*/} + -
+
)}
diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/types.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/types.ts index 3a645dcbba3b4..87aed3eb4d714 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/types.ts @@ -13,6 +13,7 @@ export interface InferencePipelineConfiguration { existingPipeline?: boolean; inferenceConfig?: InferencePipelineInferenceConfig; isPipelineNameUserSupplied?: boolean; + isModelPlaceholderSelected?: boolean; modelID: string; pipelineName: string; fieldMappings?: FieldMapping[]; @@ -21,6 +22,7 @@ export interface InferencePipelineConfiguration { export interface AddInferencePipelineFormErrors { modelID?: string; + modelStatus?: string; fieldMappings?: string; pipelineName?: string; } diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/utils.ts b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/utils.ts index 02cf5a7463dde..5a01a3823a71d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/utils.ts +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/pipelines/ml_inference/utils.ts @@ -37,6 +37,12 @@ const PIPELINE_NAME_EXISTS_ERROR = i18n.translate( defaultMessage: 'Name already used by another pipeline.', } ); +const MODEL_NOT_DEPLOYED_ERROR = i18n.translate( + 'xpack.enterpriseSearch.content.indices.pipelines.addInferencePipelineModal.steps.configure.modelNotDeployedError', + { + defaultMessage: 'Model must be deployed before use.', + } +); export const validateInferencePipelineConfiguration = ( config: InferencePipelineConfiguration @@ -55,6 +61,8 @@ export const validateInferencePipelineConfiguration = ( } if (config.modelID.trim().length === 0) { errors.modelID = FIELD_REQUIRED_ERROR; + } else if (config.isModelPlaceholderSelected) { + errors.modelStatus = MODEL_NOT_DEPLOYED_ERROR; } return errors; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/icons/connector.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/icons/connector.tsx new file mode 100644 index 0000000000000..612a759b46c17 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/icons/connector.tsx @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +export const ConnectorIcon = () => { + return ( + + + + + + + + + + + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/icons/crawler.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/icons/crawler.tsx new file mode 100644 index 0000000000000..73b38816e8a54 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/icons/crawler.tsx @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +export const CrawlerIcon = () => { + return ( + + + + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/ingestion_selector.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/ingestion_selector.tsx index 49a5d068b437c..af8ffe36172f2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/ingestion_selector.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/ingestion_selector.tsx @@ -30,9 +30,7 @@ import { INGESTION_METHOD_IDS, } from '../../../../../common/constants'; -import apiLogo from '../../../../assets/images/api_cloud.svg'; -import connectorIcon from '../../../../assets/images/connector.svg'; -import crawlerIcon from '../../../../assets/images/crawler.svg'; +import apiLogo from '../../../../assets/images/api_image.png'; import fileUploadLogo from '../../../../assets/images/file_upload_logo.svg'; import sampleDataLogo from '../../../../assets/images/sample_data_logo.svg'; import connectorLogo from '../../../../assets/images/search_connector.svg'; @@ -48,6 +46,9 @@ import { HttpLogic } from '../../../shared/http/http_logic'; import { KibanaLogic } from '../../../shared/kibana'; import { EuiLinkTo } from '../../../shared/react_router_helpers'; +import { ConnectorIcon } from './icons/connector'; +import { CrawlerIcon } from './icons/crawler'; + export const IngestionSelector: React.FC = () => { const { application: { navigateToApp }, @@ -91,7 +92,7 @@ export const IngestionSelector: React.FC = () => { defaultMessage: 'Crawl URL', } )} - buttonIcon={crawlerIcon} + buttonIcon={CrawlerIcon} description={i18n.translate( 'xpack.enterpriseSearch.ingestSelector.method.crawler.description', { @@ -119,7 +120,7 @@ export const IngestionSelector: React.FC = () => { defaultMessage: 'Create a connector', } )} - buttonIcon={connectorIcon} + buttonIcon={ConnectorIcon} description={i18n.translate( 'xpack.enterpriseSearch.ingestSelector.method.connectors.description', { diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/product_selector.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/product_selector.tsx index d03c220e16cb0..f6762e0e59bab 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/product_selector.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/product_selector.tsx @@ -59,7 +59,7 @@ export const ProductSelector: React.FC = () => { - +

diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/welcome_banner.tsx b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/welcome_banner.tsx index cca519fa6f194..1f7b5f8652a33 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/welcome_banner.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/enterprise_search_overview/components/product_selector/welcome_banner.tsx @@ -37,7 +37,7 @@ export const WelcomeBanner: React.FC = ({ user, image }) => {i18n.translate('xpack.enterpriseSearch.welcomeBanner.header.titleDescription', { defaultMessage: - "There's endless ways to ingest and explore data with Elasticsearch, but here's a few of the most popular", + 'There are endless ways to ingest and explore data with Elasticsearch, connect to your Elasticsearch instance and start indexing data', })} diff --git a/x-pack/plugins/enterprise_search/public/applications/index.test.tsx b/x-pack/plugins/enterprise_search/public/applications/index.test.tsx index bb5e1a35f18a0..d4a21f4cd6c36 100644 --- a/x-pack/plugins/enterprise_search/public/applications/index.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/index.test.tsx @@ -10,10 +10,14 @@ import React from 'react'; import { act } from '@testing-library/react'; import { getContext } from 'kea'; +import { Observable } from 'rxjs'; + import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { coreMock } from '@kbn/core/public/mocks'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; import { guidedOnboardingMock } from '@kbn/guided-onboarding-plugin/public/mocks'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; import { lensPluginMock } from '@kbn/lens-plugin/public/mocks'; import { licensingMock } from '@kbn/licensing-plugin/public/mocks'; import { mlPluginMock } from '@kbn/ml-plugin/public/mocks'; @@ -53,7 +57,13 @@ describe('renderApp', () => { }); const mockContainer = kibanaDeps.params.element; - const MockApp = () =>
Hello world!
; + const MockApp = () => ( +
+ {i18n.translate('xpack.enterpriseSearch.mockApp.div.helloWorldLabel', { + defaultMessage: 'Hello world', + })} +
+ ); it('mounts and unmounts UI', () => { const unmount = renderApp(MockApp, kibanaDeps, pluginData); @@ -102,12 +112,24 @@ describe('renderApp', () => { describe('renderHeaderActions', () => { const mockHeaderEl = document.createElement('header'); - const MockHeaderActions = () => ; + const MockHeaderActions = () => ( + + ); it('mounts and unmounts any HeaderActions component', () => { const store = getContext().store; - const unmountHeader = renderHeaderActions(MockHeaderActions, store, mockHeaderEl); + const unmountHeader = renderHeaderActions( + MockHeaderActions, + store, + { theme$: new Observable() } as any, + mockHeaderEl + ); expect(mockHeaderEl.querySelector('.hello-world')).not.toBeNull(); unmountHeader(); diff --git a/x-pack/plugins/enterprise_search/public/applications/index.tsx b/x-pack/plugins/enterprise_search/public/applications/index.tsx index 3421da8967ff5..55362dbfa8430 100644 --- a/x-pack/plugins/enterprise_search/public/applications/index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/index.tsx @@ -15,7 +15,8 @@ import { Store } from 'redux'; import { AppMountParameters, CoreStart } from '@kbn/core/public'; import { I18nProvider } from '@kbn/i18n-react'; -import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; +import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public'; +import { KibanaThemeProvider } from '@kbn/react-kibana-context-theme'; import { AuthenticatedUser } from '@kbn/security-plugin/public'; import { Router } from '@kbn/shared-ux-router'; @@ -116,7 +117,7 @@ export const renderApp = ( productFeatures, renderHeaderActions: (HeaderActions) => params.setHeaderActionMenu( - HeaderActions ? renderHeaderActions.bind(null, HeaderActions, store) : undefined + HeaderActions ? renderHeaderActions.bind(null, HeaderActions, store, params) : undefined ), security, setBreadcrumbs: chrome.setBreadcrumbs, @@ -139,7 +140,7 @@ export const renderApp = ( ReactDOM.render( - + @@ -184,12 +185,17 @@ export const renderApp = ( export const renderHeaderActions = ( HeaderActions: React.FC, store: Store, + params: AppMountParameters, kibanaHeaderEl: HTMLElement ) => { ReactDOM.render( - - - , + + + + + + + , kibanaHeaderEl ); return () => ReactDOM.unmountComponentAtNode(kibanaHeaderEl); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/api_key/api_key_panel.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/api_key/api_key_panel.tsx index ba2c4a86fcb86..5556b284d8d4a 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/api_key/api_key_panel.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/api_key/api_key_panel.tsx @@ -7,6 +7,7 @@ import React, { useEffect, useState } from 'react'; +import { css } from '@emotion/react'; import { useActions, useValues } from 'kea'; import { @@ -25,29 +26,19 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { ELASTICSEARCH_URL_PLACEHOLDER } from '@kbn/search-api-panels/constants'; -import { AuthenticatedUser } from '@kbn/security-plugin/common'; -import { Status } from '../../../../common/types/api'; - -import { CreateApiKeyAPILogic } from '../../enterprise_search_overview/api/create_elasticsearch_api_key_logic'; import { FetchApiKeysAPILogic } from '../../enterprise_search_overview/api/fetch_api_keys_logic'; import { KibanaLogic } from '../kibana'; import { CreateApiKeyFlyout } from './create_api_key_flyout'; -interface ApiKeyPanelProps { - user: AuthenticatedUser | null; -} - const COPIED_LABEL = i18n.translate('xpack.enterpriseSearch.overview.apiKey.copied', { defaultMessage: 'Copied', }); -export const ApiKeyPanel: React.FC = ({ user }) => { +export const ApiKeyPanel: React.FC = () => { const { cloud, navigateToUrl } = useValues(KibanaLogic); const { makeRequest } = useActions(FetchApiKeysAPILogic); - const { makeRequest: saveApiKey } = useActions(CreateApiKeyAPILogic); - const { error, status } = useValues(CreateApiKeyAPILogic); const { data } = useValues(FetchApiKeysAPILogic); const [isFlyoutOpen, setIsFlyoutOpen] = useState(false); @@ -59,15 +50,7 @@ export const ApiKeyPanel: React.FC = ({ user }) => { return ( <> - {isFlyoutOpen && ( - setIsFlyoutOpen(false)} - setApiKey={saveApiKey} - username={user?.full_name || user?.username || ''} - /> - )} + {isFlyoutOpen && setIsFlyoutOpen(false)} />} {Boolean(cloud) && ( @@ -80,7 +63,13 @@ export const ApiKeyPanel: React.FC = ({ user }) => { - {elasticsearchEndpoint} + + {elasticsearchEndpoint} + @@ -99,35 +88,45 @@ export const ApiKeyPanel: React.FC = ({ user }) => { - - - {i18n.translate('xpack.enterpriseSearch.apiKey.cloudId', { - defaultMessage: 'Cloud ID:', - })} - - + {Boolean(cloudId) && ( + <> + + + {i18n.translate('xpack.enterpriseSearch.apiKey.cloudId', { + defaultMessage: 'Cloud ID:', + })} + + - - - {cloudId} - - - - {(copy) => ( - + + + {cloudId} + + + + + {(copy) => ( + )} - /> - )} - - - + + + + + )} )} diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/api_key/create_api_key_flyout.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/api_key/create_api_key_flyout.tsx index 8641070a5a88a..fe298fbd98f4b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/api_key/create_api_key_flyout.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/api_key/create_api_key_flyout.tsx @@ -4,10 +4,12 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useState } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { css } from '@emotion/react'; +import { useValues, useActions } from 'kea'; + import { useEuiTheme, EuiAccordion, @@ -31,12 +33,14 @@ import { EuiText, EuiTitle, } from '@elastic/eui'; + import { i18n } from '@kbn/i18n'; -import { - CreateAPIKeyArgs, - CreateApiKeyResponse, -} from '../../enterprise_search_overview/api/create_elasticsearch_api_key_logic'; +import { Status } from '../../../../common/types/api'; + +import { CreateApiKeyAPILogic } from '../../enterprise_search_overview/api/create_elasticsearch_api_key_logic'; + +import { KibanaLogic } from '../kibana'; import { BasicSetupForm, DEFAULT_EXPIRES_VALUE } from './basic_setup_form'; import { MetadataForm } from './metadata_form'; @@ -57,12 +61,7 @@ const DEFAULT_METADATA = `{ }`; interface CreateApiKeyFlyoutProps { - createdApiKey?: CreateApiKeyResponse; - error?: string; - isLoading: boolean; onClose: () => void; - setApiKey: (apiKey: CreateAPIKeyArgs) => void; - username: string; } export const CANCEL_LABEL: string = i18n.translate('xpack.enterpriseSearch.cancel', { @@ -93,14 +92,7 @@ const INVALID_JSON_ERROR: string = i18n.translate('xpack.enterpriseSearch.invali defaultMessage: 'Invalid JSON', }); -export const CreateApiKeyFlyout: React.FC = ({ - createdApiKey, - error, - isLoading, - onClose, - username, - setApiKey, -}) => { +export const CreateApiKeyFlyout: React.FC = ({ onClose }) => { const { euiTheme } = useEuiTheme(); const [name, setName] = useState(''); const [expires, setExpires] = useState(DEFAULT_EXPIRES_VALUE); @@ -113,6 +105,14 @@ export const CreateApiKeyFlyout: React.FC = ({ const [metadataEnabled, setMetadataEnabled] = useState(false); const [metadataOpen, setMetadataOpen] = useState<'open' | 'closed'>('closed'); + const { user } = useValues(KibanaLogic); + const { makeRequest: saveApiKey, apiReset } = useActions(CreateApiKeyAPILogic); + const { data: createdApiKey, error, status } = useValues(CreateApiKeyAPILogic); + + const isLoading = status === Status.LOADING; + + const username = user?.full_name || user?.username || user?.email || ''; + const togglePrivileges = (e: EuiSwitchEvent) => { const enabled = e.target.checked; setPrivilegesEnabled(enabled); @@ -151,7 +151,7 @@ export const CreateApiKeyFlyout: React.FC = ({ if (metadataError) setMetadataError(undefined); const expiration = expires !== null ? `${expires}d` : undefined; - setApiKey({ + saveApiKey({ expiration, metadata: parsedMetadata, name, @@ -159,9 +159,22 @@ export const CreateApiKeyFlyout: React.FC = ({ }); }; + const apiKeyRef = useRef(null); + + useEffect(() => { + if (createdApiKey && apiKeyRef) { + apiKeyRef.current?.scrollIntoView(); + } + }, [createdApiKey, apiKeyRef]); + + const closeFlyOut = () => { + apiReset(); + onClose(); + }; + return ( = ({ +
{createdApiKey && ( <> - + + = ({ })} data-test-subj="create-api-key-error-callout" > - {error} + {error.body?.message} )} @@ -384,7 +394,7 @@ export const CreateApiKeyFlyout: React.FC = ({ {CANCEL_LABEL} diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/getting_started.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/getting_started.tsx new file mode 100644 index 0000000000000..afe579827a019 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/getting_started.tsx @@ -0,0 +1,335 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useState } from 'react'; + +import { useValues } from 'kea'; + +import { EuiButton, EuiFlexItem, EuiLink, EuiSpacer, EuiText } from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { + SelectClientPanel, + LanguageDefinition, + LanguageDefinitionSnippetArguments, + LanguageClientPanel, + InstallClientPanel, + OverviewPanel, + getLanguageDefinitionCodeSnippet, + getConsoleRequest, + CloudDetailsPanel, +} from '@kbn/search-api-panels'; + +import { ApiKey } from '@kbn/security-plugin/common'; + +import { PLUGIN_ID } from '../../../../common/constants'; +import { KibanaDeps } from '../../../../common/types'; + +import { icons } from '../../../assets/client_libraries'; +import { docLinks } from '../doc_links'; + +import { HttpLogic } from '../http'; + +import { curlDefinition } from './languages/curl'; +import { languageDefinitions } from './languages/languages'; +import { AddDataPanelContent } from './panels/add_data_panel_content'; +import { ApiKeyPanelContent } from './panels/api_key_panel_content'; +import { InitializeClientPanelContent } from './panels/initialize_client_panel_content'; +import { GettingStartedPipelinePanel } from './panels/pipeline_panel'; +import { SearchQueryPanelContent } from './panels/search_query_panel_content'; +import { TestConnectionPanelContent } from './panels/test_connection_panel_content'; + +interface GettingStartedProps { + apiKeys?: ApiKey[]; + codeArgs: LanguageDefinitionSnippetArguments; + isPanelLeft?: boolean; + openApiKeyModal: () => void; + showPipelinesPanel?: boolean; +} + +export const GettingStarted: React.FC = ({ + apiKeys, + codeArgs, + isPanelLeft = false, + openApiKeyModal, + showPipelinesPanel, +}) => { + const { http } = useValues(HttpLogic); + const { services } = useKibana(); + + const assetBasePath = http.basePath.prepend(`/plugins/${PLUGIN_ID}/assets/client_libraries`); + + const [selectedLanguage, setSelectedLanguage] = useState(curlDefinition); + + return ( + <> + + {languageDefinitions.map((language, index) => ( + + + + ))} + + + + + ) : undefined + } + rightPanelContent={ + isPanelLeft ? undefined : ( + + ) + } + links={[]} + title={i18n.translate( + 'xpack.enterpriseSearch.content.overview.gettingStarted.generateApiKeyPanel.panelTitle', + { + defaultMessage: 'Generate an API key', + } + )} + overviewPanelProps={{ color: 'plain', hasShadow: false }} + /> + + + + + ) : undefined + } + rightPanelContent={ + isPanelLeft ? undefined : ( + + ) + } + links={[]} + title={i18n.translate( + 'xpack.enterpriseSearch.overview.gettingStarted.configureClient.title', + { + defaultMessage: 'Configure your client', + } + )} + overviewPanelProps={{ color: 'plain', hasShadow: false }} + /> + + + ) : undefined + } + rightPanelContent={ + isPanelLeft ? undefined : ( + + ) + } + links={[]} + title={i18n.translate( + 'xpack.enterpriseSearch.overview.gettingStarted.testConnection.title', + { + defaultMessage: 'Test your connection', + } + )} + overviewPanelProps={{ color: 'plain', hasShadow: false }} + /> + + ) : undefined + } + rightPanelContent={ + isPanelLeft ? undefined : ( + + ) + } + links={[]} + title={i18n.translate('xpack.enterpriseSearch.overview.gettingStarted.ingestData.title', { + defaultMessage: 'Ingest Data', + })} + overviewPanelProps={{ color: 'plain', hasShadow: false }} + /> + + + ) : undefined + } + rightPanelContent={ + isPanelLeft ? undefined : ( + + ) + } + links={[]} + title={i18n.translate('xpack.enterpriseSearch.overview.gettingStarted.searchQuery.title', { + defaultMessage: 'Build your first search query', + })} + overviewPanelProps={{ color: 'plain', hasShadow: false }} + /> + {showPipelinesPanel && ( + + + {i18n.translate( + 'xpack.enterpriseSearch.gettingStarted.description.ingestPipelinesLink.link', + { + defaultMessage: 'ingest pipelines', + } + )} + + ), + }} + /> + + + + {i18n.translate( + 'xpack.enterpriseSearch.gettingStarted.pipeline.description.createButtonLabel', + { + defaultMessage: 'Create a pipeline', + } + )} + + + + } + leftPanelContent={} + links={[]} + overviewPanelProps={{ color: 'plain', hasShadow: false }} + title={i18n.translate('xpack.enterpriseSearch.pipeline.title', { + defaultMessage: 'Transform and enrich your data', + })} + /> + )} + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/getting_started/languages/console.ts b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/languages/console.ts similarity index 85% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/getting_started/languages/console.ts rename to x-pack/plugins/enterprise_search/public/applications/shared/getting_started/languages/console.ts index 9d395b0a68c67..6f1877ed57ddd 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/getting_started/languages/console.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/languages/console.ts @@ -7,10 +7,14 @@ import { LanguageDefinition } from '@kbn/search-api-panels'; +import { INDEX_NAME_PLACEHOLDER } from './constants'; + import { ingestKeysToJSON } from './helpers'; export const consoleDefinition: Partial = { - buildSearchQuery: ({ indexName }) => `POST /${indexName ?? 'books'}/_search?pretty + buildSearchQuery: ({ indexName = INDEX_NAME_PLACEHOLDER }) => `POST /${ + indexName ?? 'books' + }/_search?pretty { "query": { "query_string": { @@ -18,7 +22,11 @@ export const consoleDefinition: Partial = { } } }`, - ingestData: ({ indexName, ingestPipeline, extraIngestDocumentValues }) => { + ingestData: ({ + indexName = INDEX_NAME_PLACEHOLDER, + ingestPipeline, + extraIngestDocumentValues, + }) => { const ingestDocumentKeys = ingestPipeline ? ingestKeysToJSON(extraIngestDocumentValues) : ''; return `POST _bulk?pretty${ingestPipeline ? `&pipeline=${ingestPipeline}` : ''} { "index" : { "_index" : "${indexName}" } } diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/getting_started/languages/constants.ts b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/languages/constants.ts similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/getting_started/languages/constants.ts rename to x-pack/plugins/enterprise_search/public/applications/shared/getting_started/languages/constants.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/getting_started/languages/curl.ts b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/languages/curl.ts similarity index 85% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/getting_started/languages/curl.ts rename to x-pack/plugins/enterprise_search/public/applications/shared/getting_started/languages/curl.ts index 8bac32d754064..4670d58deac2e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/getting_started/languages/curl.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/languages/curl.ts @@ -8,12 +8,16 @@ import { i18n } from '@kbn/i18n'; import { Languages, LanguageDefinition } from '@kbn/search-api-panels'; -import { docLinks } from '../../../../../../shared/doc_links'; +import { docLinks } from '../../doc_links'; + +import { INDEX_NAME_PLACEHOLDER } from './constants'; import { ingestKeysToJSON } from './helpers'; export const curlDefinition: LanguageDefinition = { - buildSearchQuery: ({ indexName }) => `curl -X POST "\$\{ES_URL\}/${indexName}/_search?pretty" \\ + buildSearchQuery: ({ + indexName = INDEX_NAME_PLACEHOLDER, + }) => `curl -X POST "\$\{ES_URL\}/${indexName}/_search?pretty" \\ -H "Authorization: ApiKey "\$\{API_KEY\}"" \\ -H "Content-Type: application/json" \\ -d' @@ -35,7 +39,11 @@ export API_KEY="${apiKey}"`, }, iconType: 'curl.svg', id: Languages.CURL, - ingestData: ({ indexName, ingestPipeline, extraIngestDocumentValues }) => { + ingestData: ({ + indexName = INDEX_NAME_PLACEHOLDER, + ingestPipeline, + extraIngestDocumentValues, + }) => { const ingestDocumentKeys = ingestPipeline ? ingestKeysToJSON(extraIngestDocumentValues) : ''; return `curl -X POST "\$\{ES_URL\}/_bulk?pretty${ ingestPipeline ? `&pipeline=${ingestPipeline}` : '' @@ -67,7 +75,7 @@ brew install curl`, defaultMessage: 'cURL', }), languageStyling: 'shell', - testConnection: ({ indexName }) => `curl "\$\{ES_URL\}/${indexName}" \\ + testConnection: ({ indexName = INDEX_NAME_PLACEHOLDER }) => `curl "\$\{ES_URL\}/${indexName}" \\ -H "Authorization: ApiKey "\$\{API_KEY\}"" \\ -H "Content-Type: application/json"`, }; diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/getting_started/languages/go.ts b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/languages/go.ts similarity index 90% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/getting_started/languages/go.ts rename to x-pack/plugins/enterprise_search/public/applications/shared/getting_started/languages/go.ts index 6c504bba26bdc..9d5853664fc92 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/getting_started/languages/go.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/languages/go.ts @@ -8,12 +8,14 @@ import { i18n } from '@kbn/i18n'; import { Languages, LanguageDefinition } from '@kbn/search-api-panels'; -import { docLinks } from '../../../../../../shared/doc_links'; +import { docLinks } from '../../doc_links'; + +import { INDEX_NAME_PLACEHOLDER } from './constants'; import { ingestKeysToJSON } from './helpers'; export const goDefinition: LanguageDefinition = { - buildSearchQuery: ({ indexName }) => `searchResp, err := es.Search( + buildSearchQuery: ({ indexName = INDEX_NAME_PLACEHOLDER }) => `searchResp, err := es.Search( es.Search.WithContext(context.Background()), es.Search.WithIndex("${indexName}"), es.Search.WithQuery("snow"), @@ -58,7 +60,11 @@ if err != nil { }, iconType: 'go.svg', id: Languages.GO, - ingestData: ({ indexName, ingestPipeline, extraIngestDocumentValues }) => { + ingestData: ({ + indexName = INDEX_NAME_PLACEHOLDER, + ingestPipeline, + extraIngestDocumentValues, + }) => { const ingestDocumentKeys = ingestPipeline ? ingestKeysToJSON(extraIngestDocumentValues) : ''; return `buf := bytes.NewBufferString(\` {"index":{"_id":"9780553351927"}} diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/getting_started/languages/helpers.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/languages/helpers.test.ts similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/getting_started/languages/helpers.test.ts rename to x-pack/plugins/enterprise_search/public/applications/shared/getting_started/languages/helpers.test.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/getting_started/languages/helpers.ts b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/languages/helpers.ts similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/getting_started/languages/helpers.ts rename to x-pack/plugins/enterprise_search/public/applications/shared/getting_started/languages/helpers.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/getting_started/languages/javascript.ts b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/languages/javascript.ts similarity index 91% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/getting_started/languages/javascript.ts rename to x-pack/plugins/enterprise_search/public/applications/shared/getting_started/languages/javascript.ts index 51d7e4ef68625..f0d9758f25657 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/getting_started/languages/javascript.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/languages/javascript.ts @@ -8,12 +8,14 @@ import { i18n } from '@kbn/i18n'; import { Languages, LanguageDefinition } from '@kbn/search-api-panels'; -import { docLinks } from '../../../../../../shared/doc_links'; +import { docLinks } from '../../doc_links'; + +import { INDEX_NAME_PLACEHOLDER } from './constants'; import { ingestKeysToJSON } from './helpers'; export const javascriptDefinition: LanguageDefinition = { - buildSearchQuery: ({ indexName }) => `// Let's search! + buildSearchQuery: ({ indexName = INDEX_NAME_PLACEHOLDER }) => `// Let's search! const searchResult = await client.search({ index: '${indexName}', q: 'snow' @@ -37,7 +39,11 @@ const client = new Client({ }, iconType: 'javascript.svg', id: Languages.JAVASCRIPT, - ingestData: ({ indexName, ingestPipeline, extraIngestDocumentValues }) => { + ingestData: ({ + indexName = INDEX_NAME_PLACEHOLDER, + ingestPipeline, + extraIngestDocumentValues, + }) => { const ingestDocumentKeys = ingestPipeline ? ingestKeysToJSON(extraIngestDocumentValues) : ''; return `// Sample data books const dataset = [ diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/getting_started/languages/languages.ts b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/languages/languages.ts similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/getting_started/languages/languages.ts rename to x-pack/plugins/enterprise_search/public/applications/shared/getting_started/languages/languages.ts diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/getting_started/languages/php.ts b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/languages/php.ts similarity index 91% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/getting_started/languages/php.ts rename to x-pack/plugins/enterprise_search/public/applications/shared/getting_started/languages/php.ts index 3146ca60af306..ace1bcfa4cb75 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/getting_started/languages/php.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/languages/php.ts @@ -8,12 +8,14 @@ import { i18n } from '@kbn/i18n'; import { Languages, LanguageDefinition } from '@kbn/search-api-panels'; -import { docLinks } from '../../../../../../shared/doc_links'; +import { docLinks } from '../../doc_links'; + +import { INDEX_NAME_PLACEHOLDER } from './constants'; import { ingestKeysToPHP } from './helpers'; export const phpDefinition: LanguageDefinition = { - buildSearchQuery: ({ indexName }) => `$params = [ + buildSearchQuery: ({ indexName = INDEX_NAME_PLACEHOLDER }) => `$params = [ 'index' => '${indexName}', 'body' => [ 'q' => 'snow' @@ -35,7 +37,11 @@ print_r($response->asArray());`, }, iconType: 'php.svg', id: Languages.PHP, - ingestData: ({ indexName, ingestPipeline, extraIngestDocumentValues }) => { + ingestData: ({ + indexName = INDEX_NAME_PLACEHOLDER, + ingestPipeline, + extraIngestDocumentValues, + }) => { const ingestDocumentKeys = ingestPipeline ? ingestKeysToPHP(extraIngestDocumentValues) : ''; return `$params = [${ingestPipeline ? `\n 'pipeline' => '${ingestPipeline}',` : ''} 'body' => [ diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/getting_started/languages/python.ts b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/languages/python.ts similarity index 88% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/getting_started/languages/python.ts rename to x-pack/plugins/enterprise_search/public/applications/shared/getting_started/languages/python.ts index 24723ba3632da..74d7e3a0236c8 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/getting_started/languages/python.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/languages/python.ts @@ -8,12 +8,15 @@ import { i18n } from '@kbn/i18n'; import { Languages, LanguageDefinition } from '@kbn/search-api-panels'; -import { docLinks } from '../../../../../../shared/doc_links'; +import { docLinks } from '../../doc_links'; + +import { INDEX_NAME_PLACEHOLDER } from './constants'; import { ingestKeysToJSON } from './helpers'; export const pythonDefinition: LanguageDefinition = { - buildSearchQuery: ({ indexName }) => `client.search(index="${indexName}", q="snow")`, + buildSearchQuery: ({ indexName = INDEX_NAME_PLACEHOLDER }) => + `client.search(index="${indexName}", q="snow")`, configureClient: ({ url, apiKey }) => `from elasticsearch import Elasticsearch client = Elasticsearch( @@ -29,7 +32,11 @@ client = Elasticsearch( }, iconType: 'python.svg', id: Languages.PYTHON, - ingestData: ({ indexName, ingestPipeline, extraIngestDocumentValues }) => { + ingestData: ({ + indexName = INDEX_NAME_PLACEHOLDER, + ingestPipeline, + extraIngestDocumentValues, + }) => { const ingestDocumentKeys = ingestPipeline ? ingestKeysToJSON(extraIngestDocumentValues) : ''; return `documents = [ { "index": { "_index": "${indexName}", "_id": "9780553351927"}}, diff --git a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/getting_started/languages/ruby.ts b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/languages/ruby.ts similarity index 87% rename from x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/getting_started/languages/ruby.ts rename to x-pack/plugins/enterprise_search/public/applications/shared/getting_started/languages/ruby.ts index 43a104b0f7a8b..3fa860baa0396 100644 --- a/x-pack/plugins/enterprise_search/public/applications/enterprise_search_content/components/search_index/components/getting_started/languages/ruby.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/languages/ruby.ts @@ -8,12 +8,15 @@ import { i18n } from '@kbn/i18n'; import { Languages, LanguageDefinition } from '@kbn/search-api-panels'; -import { docLinks } from '../../../../../../shared/doc_links'; +import { docLinks } from '../../doc_links'; + +import { INDEX_NAME_PLACEHOLDER } from './constants'; import { ingestKeysToRuby } from './helpers'; export const rubyDefinition: LanguageDefinition = { - buildSearchQuery: ({ indexName }) => `client.search(index: '${indexName}', q: 'snow')`, + buildSearchQuery: ({ indexName = INDEX_NAME_PLACEHOLDER }) => + `client.search(index: '${indexName}', q: 'snow')`, configureClient: ({ url, apiKey, cloudId }) => `client = Elasticsearch::Client.new( api_key: '${apiKey}', ${cloudId ? `cloud_id: ${cloudId},` : `url: '${url}',`} @@ -28,7 +31,11 @@ export const rubyDefinition: LanguageDefinition = { }, iconType: 'ruby.svg', id: Languages.RUBY, - ingestData: ({ indexName, ingestPipeline, extraIngestDocumentValues }) => { + ingestData: ({ + indexName = INDEX_NAME_PLACEHOLDER, + ingestPipeline, + extraIngestDocumentValues, + }) => { const ingestDocumentKeys = ingestPipeline ? ingestKeysToRuby(extraIngestDocumentValues) : ''; return `documents = [ { index: { _index: '${indexName}', data: {name: "Snow Crash", author: "Neal Stephenson", release_date: "1992-06-01", page_count: 470${ingestDocumentKeys}} } }, diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/add_data_panel_content.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/add_data_panel_content.tsx new file mode 100644 index 0000000000000..e834e9ff45fe5 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/add_data_panel_content.tsx @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { + CodeBox, + getConsoleRequest, + getLanguageDefinitionCodeSnippet, + LanguageDefinition, + LanguageDefinitionSnippetArguments, +} from '@kbn/search-api-panels'; + +import { KibanaDeps } from '../../../../../common/types'; + +import { languageDefinitions } from '../languages/languages'; + +interface AddDataPanelContentProps { + assetBasePath: string; + codeArgs: LanguageDefinitionSnippetArguments; + selectedLanguage: LanguageDefinition; + setSelectedLanguage: (selectedLanguage: LanguageDefinition) => void; +} + +export const AddDataPanelContent: React.FC = ({ + assetBasePath, + codeArgs, + selectedLanguage, + setSelectedLanguage, +}) => { + const { services } = useKibana(); + return ( + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/api_key_panel_content.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/api_key_panel_content.tsx new file mode 100644 index 0000000000000..987e6fa4232d0 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/api_key_panel_content.tsx @@ -0,0 +1,129 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { + EuiPanel, + EuiFlexGroup, + EuiFlexItem, + EuiTitle, + EuiSpacer, + EuiText, + EuiButton, + EuiBadge, +} from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; + +import { ApiKey } from '@kbn/security-plugin/common'; + +import { KibanaLogic } from '../../kibana'; + +interface ApiKeyPanelContent { + apiKeys?: ApiKey[]; + openApiKeyModal: () => void; +} + +export const ApiKeyPanelContent: React.FC = ({ apiKeys, openApiKeyModal }) => { + return ( + + + + +
+ {i18n.translate( + 'xpack.enterpriseSearch.content.overview.gettingStarted.generateApiKeyPanel.apiKeytitle', + { + defaultMessage: 'Generate an API key', + } + )} +
+
+ + + {i18n.translate( + 'xpack.enterpriseSearch.content.overview.gettingStarted.generateApiKeyPanel.apiKeydesc', + { + defaultMessage: + 'Your private, unique identifier for authentication and authorization.', + } + )} + +
+ + + + + + + +

+ {i18n.translate( + 'xpack.enterpriseSearch.content.overview.documementExample.generateApiKeyButton.createNew', + { defaultMessage: 'New' } + )} +

+
+
+
+ + + KibanaLogic.values.navigateToUrl('/app/management/security/api_keys', { + shouldNotCreateHref: true, + }) + } + > + +

+ {i18n.translate( + 'xpack.enterpriseSearch.content.overview.documementExample.generateApiKeyButton.viewAll', + { defaultMessage: 'Manage' } + )} +

+
+
+
+
+
+ + + + + 0 ? 'success' : 'warning'} + data-test-subj="api-keys-count-badge" + > + {apiKeys?.length || 0} + + ), + }} + /> + + + + +
+
+
+
+ ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/initialize_client_panel_content.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/initialize_client_panel_content.tsx new file mode 100644 index 0000000000000..33260011d8381 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/initialize_client_panel_content.tsx @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { + CodeBox, + getConsoleRequest, + getLanguageDefinitionCodeSnippet, + LanguageDefinition, + LanguageDefinitionSnippetArguments, +} from '@kbn/search-api-panels'; + +import { KibanaDeps } from '../../../../../common/types'; + +import { languageDefinitions } from '../languages/languages'; + +interface InitializeClientPanelContentProps { + assetBasePath: string; + codeArgs: LanguageDefinitionSnippetArguments; + selectedLanguage: LanguageDefinition; + setSelectedLanguage: (selectedLanguage: LanguageDefinition) => void; +} + +export const InitializeClientPanelContent: React.FC = ({ + assetBasePath, + codeArgs, + selectedLanguage, + setSelectedLanguage, +}) => { + const { services } = useKibana(); + return ( + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/pipeline_panel.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/pipeline_panel.tsx new file mode 100644 index 0000000000000..8ccd789d230c4 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/pipeline_panel.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 from 'react'; + +import { PipelinePanel } from '@kbn/search-api-panels'; + +import clusterImage from '../../../../assets/images/cluster.svg'; +import cutImage from '../../../../assets/images/cut.svg'; +import reporterImage from '../../../../assets/images/reporter.svg'; + +export const GettingStartedPipelinePanel: React.FC = () => { + return ( + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/search_query_panel_content.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/search_query_panel_content.tsx new file mode 100644 index 0000000000000..2ce2801f033e0 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/search_query_panel_content.tsx @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { + CodeBox, + getLanguageDefinitionCodeSnippet, + LanguageDefinition, + LanguageDefinitionSnippetArguments, +} from '@kbn/search-api-panels'; + +import { KibanaDeps } from '../../../../../common/types'; + +import { consoleDefinition } from '../languages/console'; +import { languageDefinitions } from '../languages/languages'; + +interface SearchQueryPanelContentProps { + assetBasePath: string; + codeArgs: LanguageDefinitionSnippetArguments; + selectedLanguage: LanguageDefinition; + setSelectedLanguage: (selectedLanguage: LanguageDefinition) => void; +} + +export const SearchQueryPanelContent: React.FC = ({ + assetBasePath, + codeArgs, + selectedLanguage, + setSelectedLanguage, +}) => { + const { services } = useKibana(); + return ( + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/test_connection_panel_content.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/test_connection_panel_content.tsx new file mode 100644 index 0000000000000..f185671fabbb4 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/getting_started/panels/test_connection_panel_content.tsx @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { + CodeBox, + getConsoleRequest, + getLanguageDefinitionCodeSnippet, + LanguageDefinition, + LanguageDefinitionSnippetArguments, +} from '@kbn/search-api-panels'; + +import { KibanaDeps } from '../../../../../common/types'; + +import { languageDefinitions } from '../languages/languages'; + +interface TestConnectionPanelContentProps { + assetBasePath: string; + codeArgs: LanguageDefinitionSnippetArguments; + selectedLanguage: LanguageDefinition; + setSelectedLanguage: (selectedLanguage: LanguageDefinition) => void; +} + +export const TestConnectionPanelContent: React.FC = ({ + assetBasePath, + codeArgs, + selectedLanguage, + setSelectedLanguage, +}) => { + const { services } = useKibana(); + return ( + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/layout/endpoint_icon.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/layout/endpoint_icon.tsx new file mode 100644 index 0000000000000..157fbfd363fa3 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/layout/endpoint_icon.tsx @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +// Remove this file once `endpoint` is available in Kibana +// Coming in EUI 91.1.0 + +export const EndpointIcon = () => { + return ( + + + + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/layout/endpoints_header_action.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/layout/endpoints_header_action.tsx index 9a2fc6e01c59a..bbb27c342c992 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/layout/endpoints_header_action.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/layout/endpoints_header_action.tsx @@ -9,6 +9,7 @@ import React from 'react'; import { useState, useEffect } from 'react'; +import { css } from '@emotion/react'; import { useValues, useActions } from 'kea'; import { @@ -26,34 +27,30 @@ import { EuiBadge, EuiHorizontalRule, EuiButton, + EuiHeaderLinks, + useEuiTheme, } from '@elastic/eui'; + import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { ELASTICSEARCH_URL_PLACEHOLDER } from '@kbn/search-api-panels/constants'; -import endpointIcon from '../../../assets/images/endpoint_icon.svg'; import { FetchApiKeysAPILogic } from '../../enterprise_search_overview/api/fetch_api_keys_logic'; +import { CreateApiKeyFlyout } from '../api_key/create_api_key_flyout'; import { KibanaLogic } from '../kibana'; -interface EndpointsHeaderActionProps { - isFlyoutOpen: boolean; - setIsFlyoutOpen: (isOpen: boolean) => void; -} +import { EndpointIcon } from './endpoint_icon'; -export const EndpointsHeaderAction: React.FC = ({ - setIsFlyoutOpen, -}) => { +export const EndpointsHeaderAction: React.FC = ({ children }) => { const [isPopoverOpen, setPopoverOpen] = useState(false); const { cloud, navigateToUrl } = useValues(KibanaLogic); const { makeRequest } = useActions(FetchApiKeysAPILogic); const { data } = useValues(FetchApiKeysAPILogic); + const [isFlyoutOpen, setIsFlyoutOpen] = useState(false); + const { euiTheme } = useEuiTheme(); useEffect(() => makeRequest({}), []); - if (!cloud) { - return null; - } - const COPIED_LABEL = i18n.translate('xpack.enterpriseSearch.pageTemplate.apiKey.copied', { defaultMessage: 'Copied', }); @@ -63,149 +60,172 @@ export const EndpointsHeaderAction: React.FC = ({ const elasticsearchEndpoint = cloud?.elasticsearchUrl || ELASTICSEARCH_URL_PLACEHOLDER; const button = ( - setPopoverOpen(!isPopoverOpen)} - > + setPopoverOpen(!isPopoverOpen)}> {i18n.translate('xpack.enterpriseSearch.pageTemplate.endpointsButtonLabel', { - defaultMessage: 'Endpoints', + defaultMessage: 'Endpoints & API keys', })} ); return ( - <> - setPopoverOpen(false)} - panelPaddingSize="none" - anchorPosition="downLeft" - > - - - {i18n.translate( - 'xpack.enterpriseSearch.pageTemplate.apiKey.elasticsearchEndpoint', - { - defaultMessage: 'Elasticsearch endpoint:', - } - )} - - + + + {Boolean(children) && {children}} + + {isFlyoutOpen && setIsFlyoutOpen(false)} />} + setPopoverOpen(false)} + panelPaddingSize="none" + anchorPosition="downLeft" + > + + + {i18n.translate( + 'xpack.enterpriseSearch.pageTemplate.apiKey.elasticsearchEndpoint', + { + defaultMessage: 'Elasticsearch endpoint:', + } + )} + + - - - {elasticsearchEndpoint} - - - - {(copy) => ( - + + + {elasticsearchEndpoint} + + + + + {(copy) => ( + )} - /> - )} - - - - , - - - {i18n.translate('xpack.enterpriseSearch.apiKey.cloudId', { - defaultMessage: 'Cloud ID:', - })} - - + + + + , + ...(Boolean(cloudId) + ? [ + + + {i18n.translate('xpack.enterpriseSearch.apiKey.cloudId', { + defaultMessage: 'Cloud ID:', + })} + + - - - {cloudId} - - - - {(copy) => ( + + + + {cloudId} + + + + + {(copy) => ( + + )} + + + + , + ] + : []), + + + + + 0 ? 'success' : 'warning'} + data-test-subj="api-keys-count-badge" + > + {apiKeys.length} + + ), + }} + /> + + + + navigateToUrl('/app/management/security/api_keys', { + shouldNotCreateHref: true, + }) + } /> - )} - - - - , - - - - - 0 ? 'success' : 'warning'} - data-test-subj="api-keys-count-badge" - > - {apiKeys.length} - - ), - }} - /> - - - - - navigateToUrl('/app/management/security/api_keys', { - shouldNotCreateHref: true, - }) - } - /> - - - , - , - - setIsFlyoutOpen(true)} - data-test-subj="new-api-key-button" - fullWidth - > - - {i18n.translate('xpack.enterpriseSearch.pageTemplate.apiKey.newButtonLabel', { - defaultMessage: 'New API key', - })} - - - , - ]} - /> - - +
+ + , + , + + { + setIsFlyoutOpen(true); + setPopoverOpen(false); + }} + data-test-subj="new-api-key-button" + fullWidth + > + + {i18n.translate('xpack.enterpriseSearch.pageTemplate.apiKey.newButtonLabel', { + defaultMessage: 'New API key', + })} + + + , + ]} + /> + + + + ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/layout/page_template.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/layout/page_template.tsx index bb5a126a653e7..2553a12dc17c5 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/layout/page_template.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/layout/page_template.tsx @@ -5,22 +5,17 @@ * 2.0. */ -import React, { useState } from 'react'; +import React, { useLayoutEffect } from 'react'; import classNames from 'classnames'; -import { useActions, useValues } from 'kea'; +import { useValues } from 'kea'; import { EuiCallOut, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { I18nProvider } from '@kbn/i18n-react'; import { KibanaPageTemplate, KibanaPageTemplateProps } from '@kbn/shared-ux-page-kibana-template'; -import { Status } from '../../../../common/types/api'; - -import { CreateApiKeyAPILogic } from '../../enterprise_search_overview/api/create_elasticsearch_api_key_logic'; -import { CreateApiKeyFlyout } from '../api_key/create_api_key_flyout'; import { FlashMessages } from '../flash_messages'; import { HttpLogic } from '../http'; import { KibanaLogic } from '../kibana'; @@ -69,25 +64,21 @@ export const EnterpriseSearchPageTemplateWrapper: React.FC = ...pageTemplateProps }) => { const { readOnlyMode } = useValues(HttpLogic); - const { cloud, renderHeaderActions, user } = useValues(KibanaLogic); - const { makeRequest: saveApiKey } = useActions(CreateApiKeyAPILogic); - const { data: apiKey, error, status } = useValues(CreateApiKeyAPILogic); + const { renderHeaderActions } = useValues(KibanaLogic); const hasCustomEmptyState = !!emptyState; const showCustomEmptyState = hasCustomEmptyState && isEmptyState; const navIcon = solutionNavIcon ?? 'logoEnterpriseSearch'; - const [isFlyoutOpen, setIsFlyoutOpen] = useState(false); - const HeaderAction: React.FC<{}> = () => ( - - - - ); - - if (cloud && useEndpointHeaderActions) { - renderHeaderActions(HeaderAction); - } + useLayoutEffect(() => { + if (useEndpointHeaderActions) { + renderHeaderActions(EndpointsHeaderAction); + } + return () => { + renderHeaderActions(); + }; + }, []); return ( = isEmptyState={isEmptyState && !isLoading} solutionNav={solutionNav && solutionNav.items ? { icon: navIcon, ...solutionNav } : undefined} > - {isFlyoutOpen && ( - setIsFlyoutOpen(false)} - setApiKey={saveApiKey} - username={user?.full_name || user?.username || ''} - /> - )} {setPageChrome} {readOnlyMode && ( <> diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/search_labs_banner/search_labs_banner.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/search_labs_banner/search_labs_banner.tsx index 4692c306ab431..d9f1be314a56e 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/search_labs_banner/search_labs_banner.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/shared/search_labs_banner/search_labs_banner.tsx @@ -12,40 +12,40 @@ import { css } from '@emotion/react'; import { useValues } from 'kea'; import { - EuiImage, - EuiCallOut, EuiSpacer, EuiText, EuiTitle, EuiFlexGroup, EuiFlexItem, EuiButton, + EuiPanel, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import searchLabsLogo from '../../../assets/images/search_labs_logo.svg'; import { HttpLogic } from '../http'; +import { SearchLabsLogo } from './search_labs_logo'; + export const SearchLabsBanner: React.FC = () => { const { http } = useValues(HttpLogic); const backgroundImagePath = http.basePath.prepend( '/plugins/enterpriseSearch/assets/images/search_labs_banner_background.svg' ); return ( - - @@ -96,6 +96,6 @@ export const SearchLabsBanner: React.FC = () => { - +
); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/search_labs_banner/search_labs_logo.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/search_labs_banner/search_labs_logo.tsx new file mode 100644 index 0000000000000..f72c89c7ee5f1 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/search_labs_banner/search_labs_logo.tsx @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +export const SearchLabsLogo: React.FC = () => { + return ( + + + + + + + + + + + + + + + + + + + + + ); +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/kibana_header_actions.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/kibana_header_actions.tsx index c508e4d9e4224..d1eb78b184223 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/kibana_header_actions.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/layout/kibana_header_actions.tsx @@ -7,9 +7,10 @@ import React from 'react'; -import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiHeaderLinks } from '@elastic/eui'; +import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { externalUrl, getWorkplaceSearchUrl } from '../../../shared/enterprise_search_url'; +import { EndpointsHeaderAction } from '../../../shared/layout/endpoints_header_action'; import { EuiButtonEmptyTo } from '../../../shared/react_router_helpers'; import { NAV } from '../../constants'; import { PRIVATE_SOURCES_PATH } from '../../routes'; @@ -18,7 +19,7 @@ export const WorkplaceSearchHeaderActions: React.FC = () => { if (!externalUrl.enterpriseSearchUrl) return null; return ( - + { - + ); }; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/gated_form_page.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/gated_form_page.tsx index c4a4634695c56..2e3939675ac69 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/gated_form_page.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/views/overview/gated_form_page.tsx @@ -23,9 +23,7 @@ import { SendWorkplaceSearchTelemetry } from '../../../shared/telemetry'; import { WorkplaceSearchGate } from './gated_form'; -export const WorkplaceSearchGatePage: React.FC< - Omit -> = ({ isLoading }) => { +export const WorkplaceSearchGatePage: React.FC = ({ isLoading }) => { return ( diff --git a/x-pack/plugins/enterprise_search/public/assets/images/api_image.png b/x-pack/plugins/enterprise_search/public/assets/images/api_image.png new file mode 100644 index 0000000000000..dfc31f24813a3 Binary files /dev/null and b/x-pack/plugins/enterprise_search/public/assets/images/api_image.png differ diff --git a/x-pack/plugins/enterprise_search/public/assets/images/cluster.svg b/x-pack/plugins/enterprise_search/public/assets/images/cluster.svg new file mode 100644 index 0000000000000..99d4d0a731377 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/assets/images/cluster.svg @@ -0,0 +1,4 @@ + + + + diff --git a/x-pack/plugins/enterprise_search/public/assets/images/cut.svg b/x-pack/plugins/enterprise_search/public/assets/images/cut.svg new file mode 100644 index 0000000000000..58d33d453d716 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/assets/images/cut.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/x-pack/plugins/enterprise_search/public/assets/images/reporter.svg b/x-pack/plugins/enterprise_search/public/assets/images/reporter.svg new file mode 100644 index 0000000000000..bfd2a2afe869c --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/assets/images/reporter.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/x-pack/plugins/enterprise_search/public/assets/images/search_labs_logo.svg b/x-pack/plugins/enterprise_search/public/assets/images/search_labs_logo.svg deleted file mode 100644 index 63204cae3cf0a..0000000000000 --- a/x-pack/plugins/enterprise_search/public/assets/images/search_labs_logo.svg +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/x-pack/plugins/enterprise_search/server/collectors/connectors/telemetry.test.ts b/x-pack/plugins/enterprise_search/server/collectors/connectors/telemetry.test.ts new file mode 100644 index 0000000000000..13ec0803ec8f5 --- /dev/null +++ b/x-pack/plugins/enterprise_search/server/collectors/connectors/telemetry.test.ts @@ -0,0 +1,85 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createCollectorFetchContextMock } from '@kbn/usage-collection-plugin/server/mocks'; + +import { registerTelemetryUsageCollector } from './telemetry'; + +const indexNotFoundError = { + meta: { + body: { + error: { + type: 'index_not_found_exception', + }, + }, + }, +}; + +describe('Connectors Telemetry Usage Collector', () => { + const makeUsageCollectorStub = jest.fn(); + const registerStub = jest.fn(); + const usageCollectionMock = { + makeUsageCollector: makeUsageCollectorStub, + registerCollector: registerStub, + } as any; + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('registerTelemetryUsageCollector', () => { + it('should make and register the usage collector', () => { + registerTelemetryUsageCollector(usageCollectionMock); + + expect(registerStub).toHaveBeenCalledTimes(1); + expect(makeUsageCollectorStub).toHaveBeenCalledTimes(1); + expect(makeUsageCollectorStub.mock.calls[0][0].type).toBe('connectors'); + expect(makeUsageCollectorStub.mock.calls[0][0].isReady()).toBe(true); + }); + }); + + describe('fetchTelemetryMetrics', () => { + it('should return telemetry data', async () => { + const fetchContextMock = createCollectorFetchContextMock(); + fetchContextMock.esClient.count = jest.fn().mockImplementation((query: any) => + Promise.resolve({ + count: query.query.bool.filter[0].term.is_native ? 5 : 2, + }) + ); + registerTelemetryUsageCollector(usageCollectionMock); + const telemetryMetrics = await makeUsageCollectorStub.mock.calls[0][0].fetch( + fetchContextMock + ); + + expect(telemetryMetrics).toEqual({ + native: { + total: 5, + }, + clients: { + total: 2, + }, + }); + }); + it('should return default telemetry on index not found error', async () => { + const fetchContextMock = createCollectorFetchContextMock(); + fetchContextMock.esClient.count = jest + .fn() + .mockImplementation(() => Promise.reject(indexNotFoundError)); + registerTelemetryUsageCollector(usageCollectionMock); + const telemetryMetrics = await makeUsageCollectorStub.mock.calls[0][0].fetch( + fetchContextMock + ); + expect(telemetryMetrics).toEqual({ + native: { + total: 0, + }, + clients: { + total: 0, + }, + }); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/server/collectors/connectors/telemetry.ts b/x-pack/plugins/enterprise_search/server/collectors/connectors/telemetry.ts new file mode 100644 index 0000000000000..1a4712379b161 --- /dev/null +++ b/x-pack/plugins/enterprise_search/server/collectors/connectors/telemetry.ts @@ -0,0 +1,116 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ElasticsearchClient } from '@kbn/core/server'; + +import { CONNECTORS_INDEX } from '@kbn/search-connectors'; +import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server'; + +import { isIndexNotFoundException } from '../../utils/identify_exceptions'; + +interface Telemetry { + native: { + total: number; + }; + clients: { + total: number; + }; +} + +const defaultTelemetryMetrics: Telemetry = { + native: { + total: 0, + }, + clients: { + total: 0, + }, +}; + +/** + * Register the telemetry collector + */ + +export const registerTelemetryUsageCollector = (usageCollection: UsageCollectionSetup) => { + const telemetryUsageCollector = usageCollection.makeUsageCollector({ + type: 'connectors', + isReady: () => true, + schema: { + native: { + total: { type: 'long' }, + }, + clients: { + total: { type: 'long' }, + }, + }, + async fetch({ esClient }) { + return await fetchTelemetryMetrics(esClient); + }, + }); + usageCollection.registerCollector(telemetryUsageCollector); +}; + +/** + * Fetch the aggregated telemetry metrics + */ + +// @ts-ignore +export const fetchTelemetryMetrics = async (client: ElasticsearchClient): Promise => { + try { + const [nativeCountResponse, clientsCountResponse] = await Promise.all([ + client.count({ + index: CONNECTORS_INDEX, + query: { + bool: { + filter: [ + { + term: { + is_native: true, + }, + }, + ], + must_not: [ + { + term: { + service_type: { + value: 'elastic-crawler', + }, + }, + }, + ], + }, + }, + }), + client.count({ + index: CONNECTORS_INDEX, + query: { + bool: { + filter: [ + { + term: { + is_native: false, + }, + }, + ], + }, + }, + }), + ]); + + return { + native: { + total: nativeCountResponse.count, + }, + clients: { + total: clientsCountResponse.count, + }, + } as Telemetry; + } catch (error) { + if (isIndexNotFoundException(error)) { + return defaultTelemetryMetrics; + } + } +}; diff --git a/x-pack/plugins/enterprise_search/server/lib/ml/fetch_ml_models.ts b/x-pack/plugins/enterprise_search/server/lib/ml/fetch_ml_models.ts index 1062356a5dbeb..8807eabe14da7 100644 --- a/x-pack/plugins/enterprise_search/server/lib/ml/fetch_ml_models.ts +++ b/x-pack/plugins/enterprise_search/server/lib/ml/fetch_ml_models.ts @@ -6,6 +6,7 @@ */ import { MlTrainedModelConfig, MlTrainedModelStats } from '@elastic/elasticsearch/lib/api/types'; +import { i18n } from '@kbn/i18n'; import { MlTrainedModels } from '@kbn/ml-plugin/server'; import { MlModelDeploymentState, MlModel } from '../../../common/types/ml'; @@ -207,7 +208,9 @@ const getUserFriendlyTitle = (modelId: string, modelType: string) => { return MODEL_TITLES_BY_TYPE[modelType] !== undefined ? MODEL_TITLES_BY_TYPE[modelType]! : modelId === LANG_IDENT_MODEL_ID - ? 'Lanugage Identification' + ? i18n.translate('xpack.enterpriseSearch.content.ml_inference.lang_ident', { + defaultMessage: 'Language Identification', + }) : modelId; }; diff --git a/x-pack/plugins/enterprise_search/server/plugin.ts b/x-pack/plugins/enterprise_search/server/plugin.ts index 6231673a8a153..1132de4fbcccf 100644 --- a/x-pack/plugins/enterprise_search/server/plugin.ts +++ b/x-pack/plugins/enterprise_search/server/plugin.ts @@ -50,6 +50,7 @@ import { } from '../common/guided_onboarding/search_guide_config'; import { registerTelemetryUsageCollector as registerASTelemetryUsageCollector } from './collectors/app_search/telemetry'; +import { registerTelemetryUsageCollector as registerCNTelemetryUsageCollector } from './collectors/connectors/telemetry'; import { registerTelemetryUsageCollector as registerESTelemetryUsageCollector } from './collectors/enterprise_search/telemetry'; import { registerTelemetryUsageCollector as registerWSTelemetryUsageCollector } from './collectors/workplace_search/telemetry'; import { registerEnterpriseSearchIntegrations } from './integrations'; @@ -262,6 +263,7 @@ export class EnterpriseSearchPlugin implements Plugin { if (usageCollection) { registerESTelemetryUsageCollector(usageCollection, savedObjectsStarted, this.logger); + registerCNTelemetryUsageCollector(usageCollection); if (config.canDeployEntSearch) { registerASTelemetryUsageCollector(usageCollection, savedObjectsStarted, this.logger); registerWSTelemetryUsageCollector(usageCollection, savedObjectsStarted, this.logger); diff --git a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/search.test.ts b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/search.test.ts index 0d7fd264b143f..08c82e8523e44 100644 --- a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/search.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/search.test.ts @@ -19,12 +19,15 @@ jest.mock('@kbn/search-index-documents/lib', () => ({ describe('Elasticsearch Search', () => { let mockRouter: MockRouter; - const mockClient = {}; - + const mockClient = { + asCurrentUser: jest.fn(), + }; beforeEach(() => { const context = { - core: Promise.resolve({ elasticsearch: { client: mockClient } }), - } as jest.Mocked; + core: Promise.resolve({ + elasticsearch: { client: mockClient }, + }), + } as unknown as jest.Mocked; mockRouter = new MockRouter({ context, @@ -90,7 +93,7 @@ describe('Elasticsearch Search', () => { beforeEach(() => { const context = { core: Promise.resolve({ elasticsearch: { client: mockClient } }), - } as jest.Mocked; + } as unknown as jest.Mocked; mockRouterNoQuery = new MockRouter({ context, @@ -137,7 +140,7 @@ describe('Elasticsearch Search', () => { }); expect(fetchSearchResults).toHaveBeenCalledWith( - mockClient, + mockClient.asCurrentUser, 'search-index-name', 'banana', 0, diff --git a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/search.ts b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/search.ts index f1f6862c58a8f..430740f85644c 100644 --- a/x-pack/plugins/enterprise_search/server/routes/enterprise_search/search.ts +++ b/x-pack/plugins/enterprise_search/server/routes/enterprise_search/search.ts @@ -43,7 +43,7 @@ export function registerSearchRoute({ router, log }: RouteDependencies) { elasticsearchErrorHandler(log, async (context, request, response) => { const indexName = decodeURIComponent(request.params.index_name); const searchQuery = request.body.searchQuery; - const { client } = (await context.core).elasticsearch; + const client = (await context.core).elasticsearch.client.asCurrentUser; const { page = 0, size = ENTERPRISE_SEARCH_DOCUMENTS_DEFAULT_DOC_COUNT } = request.query; const from = page * size; try { diff --git a/x-pack/plugins/enterprise_search/tsconfig.json b/x-pack/plugins/enterprise_search/tsconfig.json index 758b8d92997f5..7b91d019e6a49 100644 --- a/x-pack/plugins/enterprise_search/tsconfig.json +++ b/x-pack/plugins/enterprise_search/tsconfig.json @@ -65,5 +65,6 @@ "@kbn/core-http-browser-mocks", "@kbn/core-application-browser", "@kbn/core-capabilities-common", + "@kbn/react-kibana-context-theme", ] } diff --git a/x-pack/plugins/exploratory_view/e2e/journeys/step_duration.journey.ts b/x-pack/plugins/exploratory_view/e2e/journeys/step_duration.journey.ts index a109740d74496..86291929afcc3 100644 --- a/x-pack/plugins/exploratory_view/e2e/journeys/step_duration.journey.ts +++ b/x-pack/plugins/exploratory_view/e2e/journeys/step_duration.journey.ts @@ -59,9 +59,7 @@ journey('Step Duration series', async ({ page, params }) => { await page.click(byTestId('seriesBreakdown')); await page.click('button[role="option"]:has-text("Step name")'); await page.click('.euiComboBox__inputWrap'); - await page.click( - 'text=Search Monitor nameCombo box. Selected. Combo box input. Search Monitor name. Ty' - ); + await page.click('[role="combobox"][placeholder="Search Monitor name"]'); await page.click('button[role="option"]:has-text("test-monitor - inline")'); await page.click('button:has-text("Apply changes")'); }); diff --git a/x-pack/plugins/exploratory_view/public/application/application.test.tsx b/x-pack/plugins/exploratory_view/public/application/application.test.tsx index 23203930616e4..a0adaca0dbded 100644 --- a/x-pack/plugins/exploratory_view/public/application/application.test.tsx +++ b/x-pack/plugins/exploratory_view/public/application/application.test.tsx @@ -13,7 +13,7 @@ import { AppMountParameters, CoreStart } from '@kbn/core/public'; import { themeServiceMock } from '@kbn/core/public/mocks'; import { ExploratoryViewPublicPluginsStart } from '../plugin'; import { renderApp } from '.'; -import { mockObservabilityAIAssistantService } from '@kbn/observability-ai-assistant-plugin/public'; +import { mockService } from '@kbn/observability-ai-assistant-plugin/public/mock'; describe('renderApp', () => { const originalConsole = global.console; @@ -43,7 +43,7 @@ describe('renderApp', () => { }, }, usageCollection: { reportUiCounter: noop }, - observabilityAIAssistant: { service: mockObservabilityAIAssistantService }, + observabilityAIAssistant: { service: mockService }, } as unknown as ExploratoryViewPublicPluginsStart; const core = { diff --git a/x-pack/plugins/exploratory_view/public/application/index.tsx b/x-pack/plugins/exploratory_view/public/application/index.tsx index c59aa090f75b7..f3189e7cf660b 100644 --- a/x-pack/plugins/exploratory_view/public/application/index.tsx +++ b/x-pack/plugins/exploratory_view/public/application/index.tsx @@ -16,7 +16,6 @@ import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-pl import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; import { Storage } from '@kbn/kibana-utils-plugin/public'; import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; -import { ObservabilityAIAssistantProvider } from '@kbn/observability-ai-assistant-plugin/public'; import { PluginContext } from '../context/plugin_context'; import { routes } from '../routes'; import { ExploratoryViewPublicPluginsStart } from '../plugin'; @@ -68,47 +67,43 @@ export const renderApp = ({ const ApplicationUsageTrackingProvider = usageCollection?.components.ApplicationUsageTrackingProvider ?? React.Fragment; - const aiAssistantService = plugins.observabilityAIAssistant.service; - ReactDOM.render( - - + - - - - -
+ + +
+ - - - -
-
-
- - - - + + +
+
+
+
+
+
, diff --git a/x-pack/plugins/exploratory_view/public/application/types.ts b/x-pack/plugins/exploratory_view/public/application/types.ts index 7d6cc14dfe771..3cdaee088db7a 100644 --- a/x-pack/plugins/exploratory_view/public/application/types.ts +++ b/x-pack/plugins/exploratory_view/public/application/types.ts @@ -20,6 +20,7 @@ import { EmbeddableStateTransfer } from '@kbn/embeddable-plugin/public'; import { NavigationPublicPluginStart } from '@kbn/navigation-plugin/public'; import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import type { ObservabilityAIAssistantPluginStart } from '@kbn/observability-ai-assistant-plugin/public'; import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import { LensPublicStart } from '@kbn/lens-plugin/public'; import { SharePluginStart } from '@kbn/share-plugin/public'; @@ -41,6 +42,7 @@ export interface ObservabilityAppServices { lens: LensPublicStart; navigation: NavigationPublicPluginStart; notifications: NotificationsStart; + observabilityAIAssistant: ObservabilityAIAssistantPluginStart; overlays: OverlayStart; savedObjectsClient: SavedObjectsStart['client']; share: SharePluginStart; diff --git a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/components/action_menu/action_menu.test.tsx b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/components/action_menu/action_menu.test.tsx index 5d43f5bd1e6df..0070517a81ca4 100644 --- a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/components/action_menu/action_menu.test.tsx +++ b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/components/action_menu/action_menu.test.tsx @@ -12,6 +12,7 @@ import { sampleAttribute } from '../../configurations/test_data/sample_attribute import * as pluginHook from '../../../../../hooks/use_plugin_context'; import { TypedLensByValueInput } from '@kbn/lens-plugin/public'; import { ExpViewActionMenuContent } from './action_menu'; +import { observabilityAIAssistantPluginMock } from '@kbn/observability-ai-assistant-plugin/public/mock'; jest.spyOn(pluginHook, 'usePluginContext').mockReturnValue({ appMountParameters: { @@ -19,6 +20,24 @@ jest.spyOn(pluginHook, 'usePluginContext').mockReturnValue({ }, } as any); +const mockObservabilityAIAssistant = observabilityAIAssistantPluginMock.createStartContract(); + +jest.mock('../../hooks/use_kibana', () => { + const originalModule = jest.requireActual('../../hooks/use_kibana'); + return { + ...originalModule, + useKibana: () => { + const { services } = originalModule.useKibana(); + return { + services: { + ...services, + observabilityAIAssistant: mockObservabilityAIAssistant, + }, + }; + }, + }; +}); + describe('Action Menu', function () { afterAll(() => { jest.clearAllMocks(); diff --git a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/components/action_menu/action_menu.tsx b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/components/action_menu/action_menu.tsx index f8e86388131aa..f0e387a1554df 100644 --- a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/components/action_menu/action_menu.tsx +++ b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/components/action_menu/action_menu.tsx @@ -9,14 +9,9 @@ import React, { useState } from 'react'; import { EuiButton, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { LensEmbeddableInput, TypedLensByValueInput } from '@kbn/lens-plugin/public'; -import { - ObservabilityAIAssistantActionMenuItem, - useObservabilityAIAssistantOptional, -} from '@kbn/observability-ai-assistant-plugin/public'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; import { EmbedAction } from '../../header/embed_action'; -import { ObservabilityAppServices } from '../../../../../application/types'; import { AddToCaseAction } from '../../header/add_to_case_action'; +import { useKibana } from '../../hooks/use_kibana'; export function ExpViewActionMenuContent({ timeRange, @@ -25,16 +20,16 @@ export function ExpViewActionMenuContent({ timeRange?: { from: string; to: string }; lensAttributes: TypedLensByValueInput['attributes'] | null; }) { - const kServices = useKibana().services; - - const { lens, isDev } = kServices; + const { + lens, + isDev, + observabilityAIAssistant: { ObservabilityAIAssistantActionMenuItem }, + } = useKibana().services; const [isSaveOpen, setIsSaveOpen] = useState(false); const LensSaveModalComponent = lens.SaveModalComponent; - const service = useObservabilityAIAssistantOptional(); - return ( <> - {service?.isEnabled() ? ( + {ObservabilityAIAssistantActionMenuItem ? ( diff --git a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/embeddable/embeddable.test.tsx b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/embeddable/embeddable.test.tsx index 5f451e6e7c86b..e8f6ee48dc206 100644 --- a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/embeddable/embeddable.test.tsx +++ b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/embeddable/embeddable.test.tsx @@ -11,12 +11,14 @@ import { DataViewState } from '../hooks/use_app_data_view'; import { render } from '../rtl_helpers'; import { AddToCaseAction } from '../header/add_to_case_action'; import { ActionTypes } from './use_actions'; +import * as lensHook from './use_embeddable_attributes'; jest.mock('../header/add_to_case_action', () => ({ AddToCaseAction: jest.fn(() =>
mockAddToCaseAction
), })); const mockLensAttrs = { + title: '', hidePanelTitles: true, description: '', visualizationType: 'lnsMetric', @@ -102,17 +104,19 @@ describe('Embeddable', () => { jest.clearAllMocks(); }); + jest.spyOn(lensHook, 'useEmbeddableAttributes').mockReturnValue(mockLensAttrs as any); + it('renders title', async () => { const { container, getByText } = render( ); expect(container.querySelector(`[data-test-subj="exploratoryView-title"]`)).toBeInTheDocument(); @@ -123,12 +127,12 @@ describe('Embeddable', () => { const { container } = render( ); expect( @@ -140,12 +144,12 @@ describe('Embeddable', () => { const { container } = render( ); @@ -174,13 +178,13 @@ describe('Embeddable', () => { render( ); diff --git a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/embeddable/embeddable.tsx b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/embeddable/embeddable.tsx index c3405225b2e01..ed06d786124f3 100644 --- a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/embeddable/embeddable.tsx +++ b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/embeddable/embeddable.tsx @@ -4,6 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { i18n } from '@kbn/i18n'; import { Position } from '@elastic/charts'; import React, { useState } from 'react'; @@ -17,28 +18,22 @@ import { import { ViewMode } from '@kbn/embeddable-plugin/common'; import { observabilityFeatureId } from '@kbn/observability-shared-plugin/public'; import styled from 'styled-components'; -import { useTheme, useKibanaSpace } from '@kbn/observability-shared-plugin/public'; -import { HeatMapLensAttributes } from '../configurations/lens_attributes/heatmap_attributes'; -import { SingleMetricLensAttributes } from '../configurations/lens_attributes/single_metric_attributes'; -import { AllSeries, ReportTypes } from '../../../..'; -import { LayerConfig, LensAttributes } from '../configurations/lens_attributes'; +import { AllSeries } from '../../../..'; import { AppDataType, ReportViewType } from '../types'; -import { getLayerConfigs } from '../hooks/use_lens_attributes'; import { OperationTypeComponent } from '../series_editor/columns/operation_type_select'; import { DataViewState } from '../hooks/use_app_data_view'; import { ReportConfigMap } from '../contexts/exploratory_view_config'; -import { obsvReportConfigMap } from '../obsv_exploratory_view'; import { ActionTypes, useActions } from './use_actions'; import { AddToCaseAction } from '../header/add_to_case_action'; +import { useEmbeddableAttributes } from './use_embeddable_attributes'; export interface ExploratoryEmbeddableProps { id?: string; appendTitle?: JSX.Element; - attributes?: AllSeries; + attributes: AllSeries; axisTitlesVisibility?: XYState['axisTitlesVisibilitySettings']; gridlinesVisibilitySettings?: XYState['gridlinesVisibilitySettings']; customHeight?: string; - customLensAttrs?: any; // Takes LensAttributes customTimeRange?: { from: string; to: string }; // required if rendered with LensAttributes dataTypesIndexPatterns?: Partial>; isSingleMetric?: boolean; @@ -69,77 +64,42 @@ export interface ExploratoryEmbeddableComponentProps extends ExploratoryEmbeddab } // eslint-disable-next-line import/no-default-export -export default function Embeddable({ - appendTitle, - attributes = [], - axisTitlesVisibility, - gridlinesVisibilitySettings, - customHeight, - customLensAttrs, - customTimeRange, - dataViewState, - legendIsVisible, - legendPosition, - lens, - onBrushEnd, - caseOwner = observabilityFeatureId, - reportConfigMap = {}, - reportType, - showCalculationMethod = false, - title, - withActions = true, - lensFormulaHelper, - hideTicks, - align, - noLabel, - fontSize = 27, - lineHeight = 32, - searchSessionId, - onLoad, -}: ExploratoryEmbeddableComponentProps) { +export default function Embeddable(props: ExploratoryEmbeddableComponentProps) { + const { + appendTitle, + attributes = [], + axisTitlesVisibility, + gridlinesVisibilitySettings, + customHeight, + customTimeRange, + legendIsVisible, + legendPosition, + lens, + onBrushEnd, + caseOwner = observabilityFeatureId, + reportType, + showCalculationMethod = false, + title, + withActions = true, + hideTicks, + align, + noLabel, + fontSize = 27, + lineHeight = 32, + searchSessionId, + onLoad, + } = props; const LensComponent = lens?.EmbeddableComponent; const LensSaveModalComponent = lens?.SaveModalComponent; const [isSaveOpen, setIsSaveOpen] = useState(false); const [isAddToCaseOpen, setAddToCaseOpen] = useState(false); - const spaceId = useKibanaSpace(); - const series = Object.entries(attributes)[0]?.[1]; const [operationType, setOperationType] = useState(series?.operationType); - const theme = useTheme(); - - const layerConfigs: LayerConfig[] = getLayerConfigs( - attributes, - reportType, - theme, - dataViewState, - { ...reportConfigMap, ...obsvReportConfigMap }, - spaceId.space?.id - ); - let lensAttributes; - let attributesJSON = customLensAttrs; - if (!customLensAttrs) { - try { - if (reportType === ReportTypes.SINGLE_METRIC) { - lensAttributes = new SingleMetricLensAttributes( - layerConfigs, - reportType, - lensFormulaHelper! - ); - attributesJSON = lensAttributes?.getJSON('lnsLegacyMetric'); - } else if (reportType === ReportTypes.HEATMAP) { - lensAttributes = new HeatMapLensAttributes(layerConfigs, reportType, lensFormulaHelper!); - attributesJSON = lensAttributes?.getJSON('lnsHeatmap'); - } else { - lensAttributes = new LensAttributes(layerConfigs, reportType, lensFormulaHelper); - attributesJSON = lensAttributes?.getJSON(); - } - // eslint-disable-next-line no-empty - } catch (error) {} - } + const attributesJSON = useEmbeddableAttributes(props); const timeRange = customTimeRange ?? series?.time; @@ -182,17 +142,20 @@ export default function Embeddable({ }; } - if (!attributesJSON && layerConfigs.length < 1) { + if (!attributesJSON) { return null; } if (!LensComponent) { - return No lens component; + return ( + + {i18n.translate('xpack.exploratoryView.embeddable.noLensComponentTextLabel', { + defaultMessage: 'No lens component', + })} + + ); } - attributesJSON.state.searchSessionId = searchSessionId; - attributesJSON.searchSessionId = searchSessionId; - return ( { + const spaceId = useKibanaSpace(); + const theme = useTheme(); + + return useMemo(() => { + try { + const layerConfigs: LayerConfig[] = getLayerConfigs( + attributes, + reportType, + theme, + dataViewState, + { ...reportConfigMap, ...obsvReportConfigMap }, + spaceId.space?.id + ); + + if (reportType === ReportTypes.SINGLE_METRIC) { + const lensAttributes = new SingleMetricLensAttributes( + layerConfigs, + reportType, + lensFormulaHelper! + ); + return lensAttributes?.getJSON('lnsLegacyMetric'); + } else if (reportType === ReportTypes.HEATMAP) { + const lensAttributes = new HeatMapLensAttributes( + layerConfigs, + reportType, + lensFormulaHelper! + ); + return lensAttributes?.getJSON('lnsHeatmap'); + } else { + const lensAttributes = new LensAttributes(layerConfigs, reportType, lensFormulaHelper); + return lensAttributes?.getJSON(); + } + } catch (error) { + console.error(error); + } + }, [ + attributes, + dataViewState, + lensFormulaHelper, + reportConfigMap, + reportType, + spaceId.space?.id, + theme, + ]); +}; diff --git a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/exploratory_view.test.tsx b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/exploratory_view.test.tsx index 83cae3e8b4ebb..e8b01b10316c3 100644 --- a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/exploratory_view.test.tsx +++ b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/exploratory_view.test.tsx @@ -12,6 +12,7 @@ import { ExploratoryView } from './exploratory_view'; import * as obsvDataViews from '../../../utils/observability_data_views/observability_data_views'; import * as pluginHook from '../../../hooks/use_plugin_context'; import { createStubIndexPattern } from '@kbn/data-plugin/common/stubs'; +import { observabilityAIAssistantPluginMock } from '@kbn/observability-ai-assistant-plugin/public/mock'; jest.spyOn(pluginHook, 'usePluginContext').mockReturnValue({ appMountParameters: { @@ -19,6 +20,24 @@ jest.spyOn(pluginHook, 'usePluginContext').mockReturnValue({ }, } as any); +const mockObservabilityAIAssistant = observabilityAIAssistantPluginMock.createStartContract(); + +jest.mock('./hooks/use_kibana', () => { + const originalModule = jest.requireActual('./hooks/use_kibana'); + return { + ...originalModule, + useKibana: () => { + const { services } = originalModule.useKibana(); + return { + services: { + ...services, + observabilityAIAssistant: mockObservabilityAIAssistant, + }, + }; + }, + }; +}); + describe('ExploratoryView', () => { mockAppDataView(); diff --git a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/exploratory_view.tsx b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/exploratory_view.tsx index a67618cf5f93c..9b5fadb9c1bf8 100644 --- a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/exploratory_view.tsx +++ b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/exploratory_view.tsx @@ -16,9 +16,8 @@ import { EuiFlexItem, } from '@elastic/eui'; import { PanelDirection } from '@elastic/eui/src/components/resizable_container/types'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; import { TypedLensByValueInput } from '@kbn/lens-plugin/public'; -import { ExploratoryViewPublicPluginsStart } from '../../../plugin'; +import { useKibana } from './hooks/use_kibana'; import { useSeriesStorage } from './hooks/use_series_storage'; import { useLensAttributes } from './hooks/use_lens_attributes'; import { useAppDataViewContext } from './hooks/use_app_data_view'; @@ -38,7 +37,7 @@ export function ExploratoryView({ }) { const { services: { lens }, - } = useKibana(); + } = useKibana(); const seriesBuilderRef = useRef(null); const wrapperRef = useRef(null); diff --git a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/hooks/use_kibana.ts b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/hooks/use_kibana.ts new file mode 100644 index 0000000000000..57c0c731d4245 --- /dev/null +++ b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/hooks/use_kibana.ts @@ -0,0 +1,20 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CoreStart } from '@kbn/core/public'; +import { useKibana } from '@kbn/kibana-react-plugin/public'; +import { ExploratoryViewPublicPluginsStart } from '../../../../plugin'; + +export type StartServices = CoreStart & + ExploratoryViewPublicPluginsStart & + AdditionalServices & { + isDev: boolean; + }; +const useTypedKibana = () => + useKibana>(); + +export { useTypedKibana as useKibana }; diff --git a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/index.tsx b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/index.tsx index 152593abaf06a..392420ee547f8 100644 --- a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/index.tsx +++ b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/index.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { useHistory } from 'react-router-dom'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; + import { createKbnUrlStateStorage, withNotifyOnErrors, @@ -17,7 +17,7 @@ import { import { TypedLensByValueInput } from '@kbn/lens-plugin/public'; import { useBreadcrumbs, useTrackPageview } from '@kbn/observability-shared-plugin/public'; import { ExploratoryView } from './exploratory_view'; -import { ExploratoryViewPublicPluginsStart } from '../../../plugin'; +import { useKibana } from './hooks/use_kibana'; import { DataViewContextProvider } from './hooks/use_app_data_view'; import { UrlStorageContextProvider } from './hooks/use_series_storage'; import { RefreshButton } from './header/refresh_button'; @@ -39,7 +39,7 @@ export function ExploratoryViewPage({ }: ExploratoryViewPageProps) { const { services: { uiSettings, notifications, observabilityShared }, - } = useKibana(); + } = useKibana(); const history = useHistory(); diff --git a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/lens_embeddable.tsx b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/lens_embeddable.tsx index 6ec631403b229..2613d39e79391 100644 --- a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/lens_embeddable.tsx +++ b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/lens_embeddable.tsx @@ -9,10 +9,9 @@ import { i18n } from '@kbn/i18n'; import React, { Dispatch, SetStateAction, useCallback, useState } from 'react'; import styled from 'styled-components'; import { LensEmbeddableInput, TypedLensByValueInput } from '@kbn/lens-plugin/public'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; import { useUiTracker } from '@kbn/observability-shared-plugin/public'; import { useSeriesStorage } from './hooks/use_series_storage'; -import { ExploratoryViewPublicPluginsStart } from '../../../plugin'; +import { useKibana } from './hooks/use_kibana'; import { useExpViewTimeRange } from './hooks/use_time_range'; import { parseRelativeDate } from './components/date_range_picker'; import { trackTelemetryOnLoad } from './utils/telemetry'; @@ -30,7 +29,7 @@ export function LensEmbeddable(props: Props) { const { lensAttributes, setChartTimeRangeContext } = props; const { services: { lens, notifications }, - } = useKibana(); + } = useKibana(); const LensComponent = lens?.EmbeddableComponent; const LensSaveModalComponent = lens?.SaveModalComponent; diff --git a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/series_editor/columns/report_definition_col.test.tsx b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/series_editor/columns/report_definition_col.test.tsx index 980c02a0ab153..17f62937812ee 100644 --- a/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/series_editor/columns/report_definition_col.test.tsx +++ b/x-pack/plugins/exploratory_view/public/components/shared/exploratory_view/series_editor/columns/report_definition_col.test.tsx @@ -39,7 +39,7 @@ describe('Series Builder ReportDefinitionCol', function () { await waitFor(() => { expect(screen.getByText('Web Application')).toBeInTheDocument(); expect(screen.getByText('Environment')).toBeInTheDocument(); - expect(screen.getByText('Search Environment')).toBeInTheDocument(); + expect(screen.getByPlaceholderText('Search Environment')).toBeInTheDocument(); }); }); diff --git a/x-pack/plugins/exploratory_view/public/components/shared/field_value_suggestions/field_value_selection.tsx b/x-pack/plugins/exploratory_view/public/components/shared/field_value_suggestions/field_value_selection.tsx index 888033d8183cd..4cf81e2a9976d 100644 --- a/x-pack/plugins/exploratory_view/public/components/shared/field_value_suggestions/field_value_selection.tsx +++ b/x-pack/plugins/exploratory_view/public/components/shared/field_value_suggestions/field_value_selection.tsx @@ -175,7 +175,7 @@ export function FieldValueSelection({ isOpen={isPopoverOpen || forceOpen} closePopover={closePopover} anchorPosition={anchorPosition} - style={{ width: '100%' }} + display="block" > settings: getSettings(), theme: { theme$: EMPTY, + getTheme: () => ({ darkMode: false }), }, plugins: {} as unknown as PluginsServiceStart, authz: { diff --git a/x-pack/plugins/fleet/common/constants/output.ts b/x-pack/plugins/fleet/common/constants/output.ts index 376e2c363225f..4d75f79df0a26 100644 --- a/x-pack/plugins/fleet/common/constants/output.ts +++ b/x-pack/plugins/fleet/common/constants/output.ts @@ -5,7 +5,7 @@ * 2.0. */ -import type { NewOutput } from '../types'; +import type { NewOutput, OutputType, ValueOf } from '../types'; export const OUTPUT_SAVED_OBJECT_TYPE = 'ingest-outputs'; @@ -120,4 +120,19 @@ export const kafkaSupportedVersions = [ '2.6.0', ]; +export const RESERVED_CONFIG_YML_KEYS = [ + 'bulk_max_size', + 'workers', + 'queue.mem.events', + 'flush.min_events', + 'flush.timeout', + 'compression', + 'idle_timeout', +]; + +export const OUTPUT_TYPES_WITH_PRESET_SUPPORT: Array> = [ + outputType.Elasticsearch, + outputType.RemoteElasticsearch, +]; + export const OUTPUT_HEALTH_DATA_STREAM = 'logs-fleet_server.output_health-default'; diff --git a/x-pack/plugins/fleet/common/experimental_features.ts b/x-pack/plugins/fleet/common/experimental_features.ts index 3ac7abb927b95..0c4a6b91fdb83 100644 --- a/x-pack/plugins/fleet/common/experimental_features.ts +++ b/x-pack/plugins/fleet/common/experimental_features.ts @@ -25,6 +25,7 @@ export const allowedExperimentalValues = Object.freeze>( kafkaOutput: true, outputSecretsStorage: true, remoteESOutput: true, + agentless: false, }); type ExperimentalConfigKeys = Array; diff --git a/x-pack/plugins/fleet/common/openapi/bundled.json b/x-pack/plugins/fleet/common/openapi/bundled.json index 580191a0bff2f..e204b0fb7885a 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.json +++ b/x-pack/plugins/fleet/common/openapi/bundled.json @@ -7952,6 +7952,16 @@ "config": { "type": "object" }, + "preset": { + "type": "string", + "enum": [ + "balanced", + "custom", + "throughput", + "scale", + "latency" + ] + }, "config_yaml": { "type": "string" }, @@ -8442,6 +8452,16 @@ "config": { "type": "object" }, + "preset": { + "type": "string", + "enum": [ + "balanced", + "custom", + "throughput", + "scale", + "latency" + ] + }, "config_yaml": { "type": "string" }, diff --git a/x-pack/plugins/fleet/common/openapi/bundled.yaml b/x-pack/plugins/fleet/common/openapi/bundled.yaml index 8bd2f5204f4f7..415089616e1a5 100644 --- a/x-pack/plugins/fleet/common/openapi/bundled.yaml +++ b/x-pack/plugins/fleet/common/openapi/bundled.yaml @@ -5130,6 +5130,14 @@ components: type: string config: type: object + preset: + type: string + enum: + - balanced + - custom + - throughput + - scale + - latency config_yaml: type: string ssl: @@ -5451,6 +5459,14 @@ components: type: string config: type: object + preset: + type: string + enum: + - balanced + - custom + - throughput + - scale + - latency config_yaml: type: string ssl: diff --git a/x-pack/plugins/fleet/common/openapi/components/schemas/output_create_request_elasticsearch.yaml b/x-pack/plugins/fleet/common/openapi/components/schemas/output_create_request_elasticsearch.yaml index aefa17057e065..0af1da40121d5 100644 --- a/x-pack/plugins/fleet/common/openapi/components/schemas/output_create_request_elasticsearch.yaml +++ b/x-pack/plugins/fleet/common/openapi/components/schemas/output_create_request_elasticsearch.yaml @@ -22,6 +22,9 @@ properties: type: string config: type: object + preset: + type: string + enum: ['balanced', 'custom', 'throughput', 'scale', 'latency'] config_yaml: type: string ssl: diff --git a/x-pack/plugins/fleet/common/openapi/components/schemas/output_update_request_elasticsearch.yaml b/x-pack/plugins/fleet/common/openapi/components/schemas/output_update_request_elasticsearch.yaml index bc10be5922883..570d0da0138bf 100644 --- a/x-pack/plugins/fleet/common/openapi/components/schemas/output_update_request_elasticsearch.yaml +++ b/x-pack/plugins/fleet/common/openapi/components/schemas/output_update_request_elasticsearch.yaml @@ -22,6 +22,9 @@ properties: type: string config: type: object + preset: + type: string + enum: ['balanced', 'custom', 'throughput', 'scale', 'latency'] config_yaml: type: string ssl: diff --git a/x-pack/plugins/fleet/common/services/is_agent_upgradeable.test.ts b/x-pack/plugins/fleet/common/services/is_agent_upgradeable.test.ts index 7ab016239702d..dbc5b5515b6bb 100644 --- a/x-pack/plugins/fleet/common/services/is_agent_upgradeable.test.ts +++ b/x-pack/plugins/fleet/common/services/is_agent_upgradeable.test.ts @@ -11,6 +11,7 @@ import { getRecentUpgradeInfoForAgent, isAgentUpgradeable, isAgentUpgrading, + getNotUpgradeableMessage, } from './is_agent_upgradeable'; const getAgent = ({ @@ -241,6 +242,154 @@ describe('Fleet - isAgentUpgradeable', () => { }); }); +describe('Fleet - getNotUpgradeableMessage', () => { + it('if agent reports not upgradeable with agent version < latest agent version', () => { + expect(getNotUpgradeableMessage(getAgent({ version: '7.9.0' }), '8.0.0')).toBe( + 'agent cannot be upgraded through Fleet. It may be running in a container or it is not installed as a service.' + ); + }); + + it('if agent reports not upgradeable with agent version > latest agent version', () => { + expect(getNotUpgradeableMessage(getAgent({ version: '8.0.0' }), '7.9.0')).toBe( + 'agent cannot be upgraded through Fleet. It may be running in a container or it is not installed as a service.' + ); + }); + + it('returns false if agent reports not upgradeable with agent version === latest agent version', () => { + expect(getNotUpgradeableMessage(getAgent({ version: '8.0.0' }), '8.0.0')).toBe( + 'agent cannot be upgraded through Fleet. It may be running in a container or it is not installed as a service.' + ); + }); + + it('if agent reports upgradeable, with agent version === latest agent version', () => { + expect( + getNotUpgradeableMessage(getAgent({ version: '8.0.0', upgradeable: true }), '8.0.0') + ).toBe('agent is already running on the latest available version.'); + }); + + it('if agent reports upgradeable, with agent version > latest agent version', () => { + expect( + getNotUpgradeableMessage(getAgent({ version: '8.0.0', upgradeable: true }), '7.9.0') + ).toBe('agent is running on a version greater than the latest available version.'); + }); + + it('if agent reports upgradeable, but agent is unenrolling', () => { + expect( + getNotUpgradeableMessage( + getAgent({ version: '7.9.0', upgradeable: true, unenrolling: true }), + '8.0.0' + ) + ).toBe('agent is being unenrolled.'); + }); + + it('if agent reports upgradeable, but agent is unenrolled', () => { + expect( + getNotUpgradeableMessage( + getAgent({ version: '7.9.0', upgradeable: true, unenrolled: true }), + '8.0.0' + ) + ).toBe('agent has been unenrolled.'); + }); + + it('Returns no error message if agent reports upgradeable, with agent version < latest agent version', () => { + expect( + getNotUpgradeableMessage(getAgent({ version: '7.9.0', upgradeable: true }), '8.0.0') + ).toBeUndefined(); + }); + + it('if agent reports upgradeable, with agent snapshot version === latest agent version', () => { + expect( + getNotUpgradeableMessage(getAgent({ version: '7.9.0-SNAPSHOT', upgradeable: true }), '7.9.0') + ).toBe('agent is already running on the latest available version.'); + }); + + it('it does not return message if agent reports upgradeable, with upgrade to agent snapshot version newer than latest agent version', () => { + expect( + getNotUpgradeableMessage( + getAgent({ version: '8.10.2', upgradeable: true }), + '8.10.2', + '8.11.0-SNAPSHOT' + ) + ).toBeUndefined(); + }); + + it('if agent reports upgradeable, with target version < current agent version ', () => { + expect( + getNotUpgradeableMessage(getAgent({ version: '7.9.0', upgradeable: true }), '8.0.0', '7.8.0') + ).toBe('agent does not support downgrades.'); + }); + + it('if agent reports upgradeable, with target version == current agent version ', () => { + expect( + getNotUpgradeableMessage(getAgent({ version: '7.9.0', upgradeable: true }), '8.0.0', '7.9.0') + ).toBe('agent is already running on the selected version.'); + }); + + it('if agent with no upgrade details reports upgradeable, but is already upgrading', () => { + expect( + getNotUpgradeableMessage( + getAgent({ version: '7.9.0', upgradeable: true, upgrading: true }), + '8.0.0' + ) + ).toBe('agent is already being upgraded.'); + }); + + it('if agent reports upgradeable, but has an upgrade status other than failed', () => { + expect( + getNotUpgradeableMessage( + getAgent({ + version: '7.9.0', + upgradeable: true, + upgradeDetails: { + target_version: '8.0.0', + action_id: 'XXX', + state: 'UPG_REQUESTED', + }, + }), + '8.0.0' + ) + ).toBe('agent is already being upgraded.'); + }); + + it('it does not return a message if agent reports upgradeable and has a failed upgrade status', () => { + expect( + getNotUpgradeableMessage( + getAgent({ + version: '7.9.0', + upgradeable: true, + upgradeDetails: { + target_version: '8.0.0', + action_id: 'XXX', + state: 'UPG_FAILED', + metadata: { + error_msg: 'Upgrade timed out', + }, + }, + }), + '8.0.0' + ) + ).toBeUndefined(); + }); + + it('if the agent reports upgradeable but was upgraded less than 10 minutes ago', () => { + expect( + getNotUpgradeableMessage( + getAgent({ version: '7.9.0', upgradeable: true, minutesSinceUpgrade: 9 }), + '8.0.0' + ) + ).toContain('please wait'); + }); + + it('if agent reports upgradeable and was upgraded more than 10 minutes ago', () => { + expect( + getNotUpgradeableMessage( + getAgent({ version: '7.9.0', upgradeable: true, minutesSinceUpgrade: 11 }), + '8.0.0' + ) + ).toBeUndefined(); + }); +}); + describe('hasAgentBeenUpgradedRecently', () => { it('returns true if the agent was upgraded less than 10 minutes ago', () => { expect( diff --git a/x-pack/plugins/fleet/common/services/is_agent_upgradeable.ts b/x-pack/plugins/fleet/common/services/is_agent_upgradeable.ts index ac21e24520c76..d36010aa13b98 100644 --- a/x-pack/plugins/fleet/common/services/is_agent_upgradeable.ts +++ b/x-pack/plugins/fleet/common/services/is_agent_upgradeable.ts @@ -8,11 +8,27 @@ import semverCoerce from 'semver/functions/coerce'; import semverLt from 'semver/functions/lt'; import semverGt from 'semver/functions/gt'; +import semverEq from 'semver/functions/eq'; +import moment from 'moment'; import type { Agent } from '../types'; export const AGENT_UPGRADE_COOLDOWN_IN_MIN = 10; +// Error messages for agent not upgradeable +export const VERSION_MISSING_ERROR = `agent version is missing.`; +export const UNENROLLED_ERROR = `agent has been unenrolled.`; +export const ONGOING_UNEROLLMENT_ERROR = `agent is being unenrolled.`; +export const NOT_UPGRADEABLE_ERROR = `agent cannot be upgraded through Fleet. It may be running in a container or it is not installed as a service.`; +export const ALREADY_UPGRADED_ERROR = `agent is already being upgraded.`; +export const INVALID_VERSION_ERROR = 'agent version is not valid.'; +export const SELECTED_VERSION_ERROR = 'the selected version is not valid.'; +export const RUNNING_SELECTED_VERSION_ERROR = `agent is already running on the selected version.`; +export const DOWNGRADE_NOT_ALLOWED_ERROR = `agent does not support downgrades.`; +export const LATEST_VERSION_NOT_VALID_ERROR = 'latest version is not valid.'; +export const AGENT_ALREADY_ON_LATEST_ERROR = `agent is already running on the latest available version.`; +export const AGENT_ON_GREATER_VERSION_ERROR = `agent is running on a version greater than the latest available version.`; + export function isAgentUpgradeable( agent: Agent, latestAgentVersion: string, @@ -42,21 +58,76 @@ export function isAgentUpgradeable( return isAgentVersionLessThanLatest(agentVersion, latestAgentVersion); } +// Based on the previous, returns a detailed message explaining why the agent is not upgradeable +export const getNotUpgradeableMessage = ( + agent: Agent, + latestAgentVersion?: string, + versionToUpgrade?: string +) => { + let agentVersion: string; + if (typeof agent?.local_metadata?.elastic?.agent?.version === 'string') { + agentVersion = agent.local_metadata.elastic.agent.version; + } else { + return VERSION_MISSING_ERROR; + } + if (agent.unenrolled_at) { + return UNENROLLED_ERROR; + } + if (agent.unenrollment_started_at) { + return ONGOING_UNEROLLMENT_ERROR; + } + if (!agent.local_metadata.elastic.agent.upgradeable) { + return NOT_UPGRADEABLE_ERROR; + } + if (isAgentUpgrading(agent)) { + return ALREADY_UPGRADED_ERROR; + } + if (getRecentUpgradeInfoForAgent(agent).hasBeenUpgradedRecently) { + const timeToWaitMins = getRecentUpgradeInfoForAgent(agent).timeToWaitMins; + const elapsedMinsSinceUpgrade = getRecentUpgradeInfoForAgent(agent).elapsedMinsSinceUpgrade; + return `agent was upgraded ${elapsedMinsSinceUpgrade} minutes ago, please wait ${timeToWaitMins} minutes before attempting the upgrade again.`; + } + const agentVersionNumber = semverCoerce(agentVersion); + if (!agentVersionNumber) return INVALID_VERSION_ERROR; + + if (versionToUpgrade !== undefined) { + const versionToUpgradeNumber = semverCoerce(versionToUpgrade); + if (!versionToUpgradeNumber) return SELECTED_VERSION_ERROR; + + if (semverEq(agentVersionNumber, versionToUpgradeNumber)) return RUNNING_SELECTED_VERSION_ERROR; + + if (semverLt(versionToUpgradeNumber, agentVersionNumber)) return DOWNGRADE_NOT_ALLOWED_ERROR; + + // explicitly allow this case - the agent is upgradeable + if (semverGt(versionToUpgradeNumber, agentVersionNumber)) return undefined; + } + + const latestAgentVersionNumber = semverCoerce(latestAgentVersion); + if (!latestAgentVersionNumber) return LATEST_VERSION_NOT_VALID_ERROR; + + if (semverEq(agentVersionNumber, latestAgentVersionNumber)) return AGENT_ALREADY_ON_LATEST_ERROR; + + if (semverGt(agentVersionNumber, latestAgentVersionNumber)) return AGENT_ON_GREATER_VERSION_ERROR; + + // in all the other cases, the agent is upgradeable; don't return any message. + return undefined; +}; + const isAgentVersionLessThanLatest = (agentVersion: string, latestAgentVersion: string) => { // make sure versions are only the number before comparison const agentVersionNumber = semverCoerce(agentVersion); - if (!agentVersionNumber) throw new Error('agent version is not valid'); + if (!agentVersionNumber) throw new Error(`${INVALID_VERSION_ERROR}`); const latestAgentVersionNumber = semverCoerce(latestAgentVersion); - if (!latestAgentVersionNumber) throw new Error('latest version is not valid'); + if (!latestAgentVersionNumber) throw new Error(`${LATEST_VERSION_NOT_VALID_ERROR}`); return semverLt(agentVersionNumber, latestAgentVersionNumber); }; const isNotDowngrade = (agentVersion: string, versionToUpgrade: string) => { const agentVersionNumber = semverCoerce(agentVersion); - if (!agentVersionNumber) throw new Error('agent version is not valid'); + if (!agentVersionNumber) throw new Error(`${INVALID_VERSION_ERROR}`); const versionToUpgradeNumber = semverCoerce(versionToUpgrade); - if (!versionToUpgradeNumber) throw new Error('target version is not valid'); + if (!versionToUpgradeNumber) throw new Error(`${SELECTED_VERSION_ERROR}`); return semverGt(versionToUpgradeNumber, agentVersionNumber); }; @@ -64,19 +135,27 @@ const isNotDowngrade = (agentVersion: string, versionToUpgrade: string) => { export function getRecentUpgradeInfoForAgent(agent: Agent): { hasBeenUpgradedRecently: boolean; timeToWaitMs: number; + elapsedMinsSinceUpgrade: number; + timeToWaitMins: number; } { if (!agent.upgraded_at) { return { hasBeenUpgradedRecently: false, timeToWaitMs: 0, + timeToWaitMins: 0, + elapsedMinsSinceUpgrade: 0, }; } - const elaspedSinceUpgradeInMillis = Date.now() - Date.parse(agent.upgraded_at); - const timeToWaitMs = AGENT_UPGRADE_COOLDOWN_IN_MIN * 6e4 - elaspedSinceUpgradeInMillis; - const hasBeenUpgradedRecently = elaspedSinceUpgradeInMillis / 6e4 < AGENT_UPGRADE_COOLDOWN_IN_MIN; + const elapsedSinceUpgradeInMillis = Date.now() - Date.parse(agent.upgraded_at); + const elapsedMins = moment.duration(elapsedSinceUpgradeInMillis, 'milliseconds').asMinutes(); + const elapsedMinsSinceUpgrade = Math.ceil(elapsedMins); - return { hasBeenUpgradedRecently, timeToWaitMs }; + const timeToWaitMs = AGENT_UPGRADE_COOLDOWN_IN_MIN * 6e4 - elapsedSinceUpgradeInMillis; + const hasBeenUpgradedRecently = elapsedSinceUpgradeInMillis / 6e4 < AGENT_UPGRADE_COOLDOWN_IN_MIN; + const timeToWait = moment.duration(timeToWaitMs, 'milliseconds').asMinutes(); + const timeToWaitMins = Math.ceil(timeToWait); + return { hasBeenUpgradedRecently, timeToWaitMs, elapsedMinsSinceUpgrade, timeToWaitMins }; } export function isAgentUpgrading(agent: Agent) { diff --git a/x-pack/plugins/fleet/common/services/output_helpers.test.ts b/x-pack/plugins/fleet/common/services/output_helpers.test.ts index 13928eff31ca0..cba42f1b6c233 100644 --- a/x-pack/plugins/fleet/common/services/output_helpers.test.ts +++ b/x-pack/plugins/fleet/common/services/output_helpers.test.ts @@ -5,7 +5,12 @@ * 2.0. */ -import { getAllowedOutputTypeForPolicy } from './output_helpers'; +import { safeLoad } from 'js-yaml'; + +import { + getAllowedOutputTypeForPolicy, + outputYmlIncludesReservedPerformanceKey, +} from './output_helpers'; describe('getAllowedOutputTypeForPolicy', () => { it('should return all available output type for an agent policy without APM and Fleet Server', () => { @@ -45,3 +50,71 @@ describe('getAllowedOutputTypeForPolicy', () => { expect(res).toEqual(['elasticsearch']); }); }); + +describe('outputYmlIncludesReservedPerformanceKey', () => { + describe('dot notation', () => { + it('returns true when reserved key is present', () => { + const configYml = `queue.mem.events: 1000`; + + expect(outputYmlIncludesReservedPerformanceKey(configYml, safeLoad)).toBe(true); + }); + + it('returns false when no reserved key is present', () => { + const configYml = `some.random.key: 1000`; + + expect(outputYmlIncludesReservedPerformanceKey(configYml, safeLoad)).toBe(false); + }); + }); + + describe('object notation', () => { + it('returns true when reserved key is present', () => { + const configYml = ` + queue: + mem: + events: 1000 + `; + + expect(outputYmlIncludesReservedPerformanceKey(configYml, safeLoad)).toBe(true); + }); + + it('returns false when no reserved key is present', () => { + const configYml = ` + some: + random: + key: 1000 + `; + + expect(outputYmlIncludesReservedPerformanceKey(configYml, safeLoad)).toBe(false); + }); + }); + + describe('plain string', () => { + it('returns true when reserved key is present', () => { + const configYml = `bulk_max_size`; + + expect(outputYmlIncludesReservedPerformanceKey(configYml, safeLoad)).toBe(true); + }); + + it('returns false when no reserved key is present', () => { + const configYml = `just a string`; + + expect(outputYmlIncludesReservedPerformanceKey(configYml, safeLoad)).toBe(false); + }); + }); + + describe('comment', () => { + it('returns false when reserved key is present only in a comment', () => { + const configYml = `true`; + + expect(outputYmlIncludesReservedPerformanceKey(configYml, safeLoad)).toBe(false); + }); + }); + + describe('empty string', () => { + it('returns false when YML is empty', () => { + const configYml = ``; + + expect(outputYmlIncludesReservedPerformanceKey(configYml, safeLoad)).toBe(false); + }); + }); +}); diff --git a/x-pack/plugins/fleet/common/services/output_helpers.ts b/x-pack/plugins/fleet/common/services/output_helpers.ts index 00b480c378b61..988be3bc277f7 100644 --- a/x-pack/plugins/fleet/common/services/output_helpers.ts +++ b/x-pack/plugins/fleet/common/services/output_helpers.ts @@ -5,12 +5,18 @@ * 2.0. */ -import type { AgentPolicy } from '../types'; +import { isObject } from 'lodash'; + +import { getFlattenedObject } from '@kbn/std'; + +import type { AgentPolicy, OutputType, ValueOf } from '../types'; import { FLEET_APM_PACKAGE, FLEET_SERVER_PACKAGE, FLEET_SYNTHETICS_PACKAGE, outputType, + OUTPUT_TYPES_WITH_PRESET_SUPPORT, + RESERVED_CONFIG_YML_KEYS, } from '../constants'; /** @@ -22,9 +28,9 @@ export function getAllowedOutputTypeForPolicy(agentPolicy: AgentPolicy) { agentPolicy.package_policies && agentPolicy.package_policies.some( (p) => - p.package?.name === FLEET_APM_PACKAGE || p.package?.name === FLEET_SERVER_PACKAGE || - p.package?.name === FLEET_SYNTHETICS_PACKAGE + p.package?.name === FLEET_SYNTHETICS_PACKAGE || + p.package?.name === FLEET_APM_PACKAGE ); if (isRestrictedToSameClusterES) { @@ -33,3 +39,41 @@ export function getAllowedOutputTypeForPolicy(agentPolicy: AgentPolicy) { return Object.values(outputType); } + +export function outputYmlIncludesReservedPerformanceKey( + configYml: string, + // Dependency injection for `safeLoad` prevents bundle size issues 🤷‍♀️ + safeLoad: (yml: string) => any +) { + if (!configYml || configYml === '') { + return false; + } + + const parsedYml = safeLoad(configYml); + + if (!isObject(parsedYml)) { + if (typeof parsedYml === 'string') { + return RESERVED_CONFIG_YML_KEYS.some((key) => parsedYml.includes(key)); + } + return false; + } + + const flattenedYml = isObject(parsedYml) ? getFlattenedObject(parsedYml) : {}; + + return RESERVED_CONFIG_YML_KEYS.some((key) => Object.keys(flattenedYml).includes(key)); +} + +export function getDefaultPresetForEsOutput( + configYaml: string, + safeLoad: (yml: string) => any +): 'balanced' | 'custom' { + if (outputYmlIncludesReservedPerformanceKey(configYaml, safeLoad)) { + return 'custom'; + } + + return 'balanced'; +} + +export function outputTypeSupportPresets(type: ValueOf) { + return OUTPUT_TYPES_WITH_PRESET_SUPPORT.includes(type); +} diff --git a/x-pack/plugins/fleet/common/types/index.ts b/x-pack/plugins/fleet/common/types/index.ts index 2deac11481473..dfee7502c4ed1 100644 --- a/x-pack/plugins/fleet/common/types/index.ts +++ b/x-pack/plugins/fleet/common/types/index.ts @@ -45,6 +45,7 @@ export interface FleetConfigType { disableRegistryVersionCheck?: boolean; bundledPackageLocation?: string; testSecretsIndex?: string; + disableBundledPackagesCache?: boolean; }; internal?: { disableILMPolicies: boolean; diff --git a/x-pack/plugins/fleet/common/types/models/agent.ts b/x-pack/plugins/fleet/common/types/models/agent.ts index 120f0bc883e1d..1242a61124952 100644 --- a/x-pack/plugins/fleet/common/types/models/agent.ts +++ b/x-pack/plugins/fleet/common/types/models/agent.ts @@ -446,6 +446,7 @@ export interface AgentUpgradeDetails { metadata?: { scheduled_at?: string; download_percent?: number; + download_rate?: number; // bytes per second failed_state?: AgentUpgradeStateType; error_msg?: string; }; diff --git a/x-pack/plugins/fleet/common/types/models/epm.ts b/x-pack/plugins/fleet/common/types/models/epm.ts index 24bde5b5f5f5a..1a7c4cefca34d 100644 --- a/x-pack/plugins/fleet/common/types/models/epm.ts +++ b/x-pack/plugins/fleet/common/types/models/epm.ts @@ -132,7 +132,7 @@ export type ArchivePackage = PackageSpecManifest & export interface BundledPackage { name: string; version: string; - buffer: Buffer; + getBuffer: () => Promise; } export type RegistryPackage = PackageSpecManifest & @@ -618,6 +618,7 @@ export interface IndexTemplateMappings { properties: any; dynamic_templates?: any; runtime?: any; + subobjects?: boolean; } // This is an index template v2, see https://github.com/elastic/elasticsearch/issues/53101 diff --git a/x-pack/plugins/fleet/common/types/models/index.ts b/x-pack/plugins/fleet/common/types/models/index.ts index babbe3a8ce1ac..5af1294e52657 100644 --- a/x-pack/plugins/fleet/common/types/models/index.ts +++ b/x-pack/plugins/fleet/common/types/models/index.ts @@ -19,3 +19,4 @@ export * from './download_sources'; export * from './fleet_server_policy_config'; export * from './fleet_proxy'; export * from './secret'; +export * from './setup_technology'; diff --git a/x-pack/plugins/fleet/common/types/models/output.ts b/x-pack/plugins/fleet/common/types/models/output.ts index 8d721509495ea..842ade5452963 100644 --- a/x-pack/plugins/fleet/common/types/models/output.ts +++ b/x-pack/plugins/fleet/common/types/models/output.ts @@ -29,6 +29,9 @@ export type OutputSecret = id: string; hash?: string; }; + +export type OutputPreset = 'custom' | 'balanced' | 'throughput' | 'scale' | 'latency'; + interface NewBaseOutput { is_default: boolean; is_default_monitoring: boolean; @@ -49,6 +52,7 @@ interface NewBaseOutput { shipper?: ShipperOutput | null; allow_edit?: string[]; secrets?: {}; + preset?: OutputPreset; } export interface NewElasticsearchOutput extends NewBaseOutput { diff --git a/x-pack/plugins/fleet/common/types/models/secret.ts b/x-pack/plugins/fleet/common/types/models/secret.ts index 74341275859f2..93051f71c98ae 100644 --- a/x-pack/plugins/fleet/common/types/models/secret.ts +++ b/x-pack/plugins/fleet/common/types/models/secret.ts @@ -18,7 +18,7 @@ export interface VarSecretReference { isSecretRef: true; } export interface SecretPath { - path: string; + path: string[]; value: PackagePolicyConfigRecordEntry; } export interface OutputSecretPath { diff --git a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/index.ts b/x-pack/plugins/fleet/common/types/models/setup_technology.ts similarity index 73% rename from x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/index.ts rename to x-pack/plugins/fleet/common/types/models/setup_technology.ts index 901adb44b00d3..66fb4a09ff9a2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_client_instructions/index.ts +++ b/x-pack/plugins/fleet/common/types/models/setup_technology.ts @@ -5,4 +5,7 @@ * 2.0. */ -export { ElasticsearchClientInstructions } from './elasticsearch_client_instructions'; +export enum SetupTechnology { + AGENTLESS = 'agentless', + AGENT_BASED = 'agent-based', +} diff --git a/x-pack/plugins/fleet/cypress/e2e/fleet_settings_outputs.cy.ts b/x-pack/plugins/fleet/cypress/e2e/fleet_settings_outputs.cy.ts index 3b312241fb70f..5101c4d93fd39 100644 --- a/x-pack/plugins/fleet/cypress/e2e/fleet_settings_outputs.cy.ts +++ b/x-pack/plugins/fleet/cypress/e2e/fleet_settings_outputs.cy.ts @@ -21,8 +21,11 @@ import { loadESOutput, loadKafkaOutput, loadLogstashOutput, + loadRemoteESOutput, resetKafkaOutputForm, + selectESOutput, selectKafkaOutput, + selectRemoteESOutput, shouldDisplayError, validateOutputTypeChangeToKafka, validateSavedKafkaOutputForm, @@ -37,6 +40,175 @@ describe('Outputs', () => { login(); }); + describe('Elasticsearch', () => { + describe('Preset input', () => { + it('is set to balanced by default', () => { + selectESOutput(); + + cy.getBySel(SETTINGS_OUTPUTS.PRESET_INPUT).should('have.value', 'balanced'); + }); + + it('forces custom when reserved key is included in config YAML box', () => { + selectESOutput(); + + cy.getBySel('kibanaCodeEditor').click().focused().type('bulk_max_size: 1000'); + + cy.getBySel(SETTINGS_OUTPUTS.PRESET_INPUT) + .should('have.value', 'custom') + .should('be.disabled'); + }); + + it('allows balanced when reserved key is not included in config yaml box', () => { + selectESOutput(); + + cy.getBySel('kibanaCodeEditor').click().focused().type('some_random_key: foo'); + + cy.getBySel(SETTINGS_OUTPUTS.PRESET_INPUT) + .should('have.value', 'balanced') + .should('be.enabled'); + }); + + const cases = [ + { + name: 'Preset: Balanced', + preset: 'balanced', + }, + { + name: 'Preset: Custom', + preset: 'custom', + }, + { + name: 'Preset: Throughput', + preset: 'throughput', + }, + { + name: 'Preset: Scale', + preset: 'scale', + }, + { + name: 'Preset: Latency', + preset: 'latency', + }, + ]; + + for (const type of ['elasticsearch', 'remote_elasticsearch']) { + for (const testCase of cases) { + describe(`When type is ${type} and preset is ${testCase.preset}`, () => { + it('successfully creates output', () => { + if (type === 'elasticsearch') { + selectESOutput(); + } else if (type === 'remote_elasticsearch') { + selectRemoteESOutput(); + } + + cy.getBySel(SETTINGS_OUTPUTS.NAME_INPUT).type(`${type} - ${testCase.name}`); + cy.get(`[placeholder="Specify host URL"]`).type( + `http://${testCase.preset}.elasticsearch.com:9200` + ); + cy.getBySel(SETTINGS_OUTPUTS.PRESET_INPUT).select(testCase.preset); + + if (type === 'remote_elasticsearch') { + cy.getBySel('serviceTokenSecretInput').type('secret'); + } + + cy.intercept('POST', '**/api/fleet/outputs').as('saveOutput'); + cy.getBySel(SETTINGS_SAVE_BTN).click(); + + cy.wait('@saveOutput').then((interception) => { + const responseBody = interception.response?.body; + cy.visit(`/app/fleet/settings/outputs/${responseBody?.item?.id}`); + }); + + cy.getBySel(SETTINGS_OUTPUTS.NAME_INPUT).should( + 'have.value', + `${type} - ${testCase.name}` + ); + cy.get(`[placeholder="Specify host URL"]`).should( + 'have.value', + `http://${testCase.preset}.elasticsearch.com:9200` + ); + cy.getBySel(SETTINGS_OUTPUTS.PRESET_INPUT).should('have.value', testCase.preset); + }); + }); + } + } + }); + }); + + describe('Remote ES', () => { + it('displays proper error messages', () => { + selectRemoteESOutput(); + cy.getBySel(SETTINGS_SAVE_BTN).click(); + + cy.contains('Name is required'); + cy.contains('URL is required'); + cy.contains('Service Token is required'); + shouldDisplayError(SETTINGS_OUTPUTS.NAME_INPUT); + shouldDisplayError('serviceTokenSecretInput'); + }); + + describe('Form submit', () => { + let outputId: string; + + before(() => { + interceptOutputId((id) => { + outputId = id; + }); + }); + + after(() => { + cleanupOutput(outputId); + }); + + it('saves the output', () => { + selectRemoteESOutput(); + + cy.getBySel(SETTINGS_OUTPUTS.NAME_INPUT).type('name'); + cy.get('[placeholder="Specify host URL"').clear().type('https://localhost:5000'); + cy.getBySel('serviceTokenSecretInput').type('service_token'); + + cy.intercept('POST', '**/api/fleet/outputs').as('saveOutput'); + + cy.getBySel(SETTINGS_SAVE_BTN).click(); + + cy.wait('@saveOutput').then((interception) => { + const responseBody = interception.response?.body; + cy.visit(`/app/fleet/settings/outputs/${responseBody?.item?.id}`); + expect(responseBody?.item.service_token).to.equal(undefined); + expect(responseBody?.item.secrets.service_token.id).not.to.equal(undefined); + }); + + cy.get('[placeholder="Specify host URL"').should('have.value', 'https://localhost:5000'); + cy.getBySel(SETTINGS_OUTPUTS.NAME_INPUT).should('have.value', 'name'); + }); + }); + + describe('Form edit', () => { + let outputId: string; + + before(() => { + loadRemoteESOutput().then((data) => { + outputId = data.item.id; + }); + }); + after(() => { + cleanupOutput(outputId); + }); + + it('edits the output', () => { + visit(`/app/fleet/settings/outputs/${outputId}`); + + cy.get('[placeholder="Specify host URL"').clear().type('https://localhost:5001'); + + cy.getBySel(SETTINGS_SAVE_BTN).click(); + cy.getBySel(SETTINGS_CONFIRM_MODAL_BTN).click(); + visit(`/app/fleet/settings/outputs/${outputId}`); + + cy.get('[placeholder="Specify host URL"').should('have.value', 'https://localhost:5001'); + }); + }); + }); + describe('Kafka', () => { describe('Form validation', () => { it('renders all form fields', () => { @@ -204,7 +376,7 @@ describe('Outputs', () => { cy.contains('Name is required'); cy.contains('Host is required'); cy.contains('Username is required'); - // cy.contains('Password is required'); // TODO + cy.contains('Password is required'); cy.contains('Default topic is required'); cy.contains('Topic is required'); cy.contains( @@ -213,7 +385,7 @@ describe('Outputs', () => { cy.contains('Must be a key, value pair i.e. "http.response.code: 200"'); shouldDisplayError(SETTINGS_OUTPUTS.NAME_INPUT); shouldDisplayError(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_USERNAME_INPUT); - // shouldDisplayError(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_PASSWORD_INPUT); // TODO + shouldDisplayError(SETTINGS_OUTPUTS_KAFKA.AUTHENTICATION_PASSWORD_INPUT); shouldDisplayError(SETTINGS_OUTPUTS_KAFKA.TOPICS_DEFAULT_TOPIC_INPUT); shouldDisplayError(SETTINGS_OUTPUTS_KAFKA.TOPICS_CONDITION_INPUT); shouldDisplayError(SETTINGS_OUTPUTS_KAFKA.TOPICS_TOPIC_INPUT); diff --git a/x-pack/plugins/fleet/cypress/e2e/package_policy_pipelines_and_mappings_real.cy.ts b/x-pack/plugins/fleet/cypress/e2e/package_policy_pipelines_and_mappings_real.cy.ts index c1cf0ef4e0b09..6cbf5a881f5d5 100644 --- a/x-pack/plugins/fleet/cypress/e2e/package_policy_pipelines_and_mappings_real.cy.ts +++ b/x-pack/plugins/fleet/cypress/e2e/package_policy_pipelines_and_mappings_real.cy.ts @@ -100,7 +100,7 @@ describe('Input package create and edit package policy', () => { editPackagePolicyandShowAdvanced(INPUT_TEST_PACKAGE, packagePolicyName); cy.getBySel(POLICY_EDITOR.EDIT_MAPPINGS_BTN).click(); cy.getBySel(CONFIRM_MODAL.CONFIRM_BUTTON).click(); - cy.get('body').should('contain', `logs-${datasetName}@custom`); + cy.get('body').should('contain', `logs${datasetName}@custom`); }); }); diff --git a/x-pack/plugins/fleet/cypress/screens/fleet.ts b/x-pack/plugins/fleet/cypress/screens/fleet.ts index fa623389e7c92..1d0acb2f34739 100644 --- a/x-pack/plugins/fleet/cypress/screens/fleet.ts +++ b/x-pack/plugins/fleet/cypress/screens/fleet.ts @@ -124,6 +124,7 @@ export const SETTINGS_OUTPUTS = { ADD_HOST_ROW_BTN: 'fleetServerHosts.multiRowInput.addRowButton', WARNING_KAFKA_CALLOUT: 'settingsOutputsFlyout.kafkaOutputTypeCallout', WARNING_ELASTICSEARCH_CALLOUT: 'settingsOutputsFlyout.elasticsearchOutputTypeCallout', + PRESET_INPUT: 'settingsOutputsFlyout.presetInput', }; export const getSpecificSelectorId = (selector: string, id: number) => { diff --git a/x-pack/plugins/fleet/cypress/screens/fleet_outputs.ts b/x-pack/plugins/fleet/cypress/screens/fleet_outputs.ts index 5b3b0f8d4af16..e439961e287d7 100644 --- a/x-pack/plugins/fleet/cypress/screens/fleet_outputs.ts +++ b/x-pack/plugins/fleet/cypress/screens/fleet_outputs.ts @@ -15,6 +15,18 @@ import { SETTINGS_SAVE_BTN, } from './fleet'; +export const selectESOutput = () => { + visit('/app/fleet/settings'); + cy.getBySel(SETTINGS_OUTPUTS.ADD_BTN).click(); + cy.getBySel(SETTINGS_OUTPUTS.TYPE_INPUT).select('elasticsearch'); +}; + +export const selectRemoteESOutput = () => { + visit('/app/fleet/settings'); + cy.getBySel(SETTINGS_OUTPUTS.ADD_BTN).click(); + cy.getBySel(SETTINGS_OUTPUTS.TYPE_INPUT).select('remote_elasticsearch'); +}; + export const selectKafkaOutput = () => { visit('/app/fleet/settings'); cy.getBySel(SETTINGS_OUTPUTS.ADD_BTN).click(); @@ -72,6 +84,17 @@ export const loadESOutput = () => hosts: ['https://bla.co'], }); +export const loadRemoteESOutput = () => + loadOutput({ + name: 'remote_es', + type: 'remote_elasticsearch', + is_default: false, + is_default_monitoring: false, + hosts: ['https://bla.co'], + secrets: { service_token: 'token' }, + preset: 'balanced', + }); + export const loadLogstashOutput = () => loadOutput({ name: 'ls', diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/hooks.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/hooks.test.tsx index e027317bf621a..8c2983ee9fd83 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/hooks.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/hooks.test.tsx @@ -533,7 +533,7 @@ describe('useOutputOptions', () => { `); }); - it('should only enable remote es output for monitoring output', async () => { + it('should enable remote es output for data and monitoring output', async () => { const testRenderer = createFleetTestRendererMock(); mockedUseLicence.mockReturnValue({ hasAtLeast: () => true, @@ -545,7 +545,8 @@ describe('useOutputOptions', () => { expect(result.current.isLoading).toBeTruthy(); await waitForNextUpdate(); - expect(result.current.dataOutputOptions.length).toEqual(1); + expect(result.current.dataOutputOptions.length).toEqual(2); + expect(result.current.dataOutputOptions[1].value).toEqual('remote1'); expect(result.current.monitoringOutputOptions.length).toEqual(2); expect(result.current.monitoringOutputOptions[1].value).toEqual('remote1'); }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/hooks.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/hooks.tsx index 9ee8b1f225735..15681ea1dfbb6 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/hooks.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/components/agent_policy_advanced_fields/hooks.tsx @@ -16,7 +16,7 @@ import { useGetDownloadSources, useGetFleetServerHosts, } from '../../../../hooks'; -import { LICENCE_FOR_PER_POLICY_OUTPUT, outputType } from '../../../../../../../common/constants'; +import { LICENCE_FOR_PER_POLICY_OUTPUT } from '../../../../../../../common/constants'; import { getAllowedOutputTypeForPolicy, policyHasFleetServer, @@ -99,28 +99,26 @@ export function useOutputOptions(agentPolicy: Partial item.type !== outputType.RemoteElasticsearch) - .map((item) => { - const isOutputTypeUnsupported = !allowedOutputTypes.includes(item.type); + ...outputsRequest.data.items.map((item) => { + const isOutputTypeUnsupported = !allowedOutputTypes.includes(item.type); - return { - value: item.id, - inputDisplay: getOutputLabel( - item.name, - isOutputTypeUnsupported ? ( - - ) : undefined - ), - disabled: !isPolicyPerOutputAllowed || isOutputTypeUnsupported, - }; - }), + return { + value: item.id, + inputDisplay: getOutputLabel( + item.name, + isOutputTypeUnsupported ? ( + + ) : undefined + ), + disabled: !isPolicyPerOutputAllowed || isOutputTypeUnsupported, + }; + }), ]; }, [outputsRequest, isPolicyPerOutputAllowed, allowedOutputTypes]); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/index.tsx index 33d1cee841590..2d8b96c562e85 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/index.tsx @@ -7,3 +7,4 @@ export { useDevToolsRequest } from './devtools_request'; export { useOnSubmit } from './form'; +export { useSetupTechnology } from './setup_technology'; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.test.ts b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.test.ts new file mode 100644 index 0000000000000..dd2a1dcc81c79 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.test.ts @@ -0,0 +1,204 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { renderHook, act } from '@testing-library/react-hooks'; + +import { SetupTechnology } from '../../../../../../../../common/types'; +import { ExperimentalFeaturesService } from '../../../../../services'; +import { sendGetOneAgentPolicy, useStartServices } from '../../../../../hooks'; +import { SelectedPolicyTab } from '../../components'; + +import { useSetupTechnology } from './setup_technology'; + +jest.mock('../../../../../services'); +jest.mock('../../../../../hooks', () => ({ + ...jest.requireActual('../../../../../hooks'), + sendGetOneAgentPolicy: jest.fn(), + useStartServices: jest.fn(), +})); + +type MockFn = jest.MockedFunction; + +describe('useSetupTechnology', () => { + const updateNewAgentPolicyMock = jest.fn(); + const updateAgentPolicyMock = jest.fn(); + const setSelectedPolicyTabMock = jest.fn(); + const newAgentPolicyMock = { + name: 'mock_new_agent_policy', + namespace: 'default', + }; + const mockedExperimentalFeaturesService = jest.mocked(ExperimentalFeaturesService); + + beforeEach(() => { + mockedExperimentalFeaturesService.get.mockReturnValue({ + agentless: true, + } as any); + (sendGetOneAgentPolicy as MockFn).mockResolvedValue({ + data: { + item: { id: 'agentless-policy-id' }, + }, + }); + (useStartServices as MockFn).mockReturnValue({ + cloud: { + isServerlessEnabled: true, + }, + }); + jest.clearAllMocks(); + }); + + it('should initialize with default values when agentless is disabled', () => { + mockedExperimentalFeaturesService.get.mockReturnValue({ + agentless: false, + } as any); + + const { result } = renderHook(() => + useSetupTechnology({ + updateNewAgentPolicy: updateNewAgentPolicyMock, + newAgentPolicy: newAgentPolicyMock, + updateAgentPolicy: updateAgentPolicyMock, + setSelectedPolicyTab: setSelectedPolicyTabMock, + }) + ); + + expect(sendGetOneAgentPolicy).not.toHaveBeenCalled(); + expect(result.current.selectedSetupTechnology).toBe(SetupTechnology.AGENT_BASED); + expect(result.current.agentlessPolicy).toBeUndefined(); + }); + + it('should fetch agentless policy if agentless is enabled', async () => { + const { result, waitForNextUpdate } = renderHook(() => + useSetupTechnology({ + updateNewAgentPolicy: updateNewAgentPolicyMock, + newAgentPolicy: newAgentPolicyMock, + updateAgentPolicy: updateAgentPolicyMock, + setSelectedPolicyTab: setSelectedPolicyTabMock, + }) + ); + + await waitForNextUpdate(); + + expect(result.current.agentlessPolicy).toEqual({ id: 'agentless-policy-id' }); + }); + + it('should not fetch agentless policy if agentless is enabled but serverless is disabled', async () => { + (useStartServices as MockFn).mockReturnValue({ + cloud: { + isServerlessEnabled: false, + }, + }); + + const { result } = renderHook(() => + useSetupTechnology({ + updateNewAgentPolicy: updateNewAgentPolicyMock, + newAgentPolicy: newAgentPolicyMock, + updateAgentPolicy: updateAgentPolicyMock, + setSelectedPolicyTab: setSelectedPolicyTabMock, + }) + ); + + expect(sendGetOneAgentPolicy).not.toHaveBeenCalled(); + expect(result.current.selectedSetupTechnology).toBe(SetupTechnology.AGENT_BASED); + expect(result.current.agentlessPolicy).toBeUndefined(); + }); + + it('should update agent policy and selected policy tab when setup technology is agentless', async () => { + const { result, waitForNextUpdate } = renderHook(() => + useSetupTechnology({ + updateNewAgentPolicy: updateNewAgentPolicyMock, + newAgentPolicy: newAgentPolicyMock, + updateAgentPolicy: updateAgentPolicyMock, + setSelectedPolicyTab: setSelectedPolicyTabMock, + }) + ); + + await waitForNextUpdate(); + + act(() => { + result.current.handleSetupTechnologyChange(SetupTechnology.AGENTLESS); + }); + + expect(updateAgentPolicyMock).toHaveBeenCalledWith({ id: 'agentless-policy-id' }); + expect(setSelectedPolicyTabMock).toHaveBeenCalledWith(SelectedPolicyTab.EXISTING); + }); + + it('should update new agent policy and selected policy tab when setup technology is agent-based', async () => { + const { result, waitForNextUpdate } = renderHook(() => + useSetupTechnology({ + updateNewAgentPolicy: updateNewAgentPolicyMock, + newAgentPolicy: newAgentPolicyMock, + updateAgentPolicy: updateAgentPolicyMock, + setSelectedPolicyTab: setSelectedPolicyTabMock, + }) + ); + + await waitForNextUpdate(); + + expect(result.current.selectedSetupTechnology).toBe(SetupTechnology.AGENT_BASED); + + act(() => { + result.current.handleSetupTechnologyChange(SetupTechnology.AGENTLESS); + }); + + expect(result.current.selectedSetupTechnology).toBe(SetupTechnology.AGENTLESS); + + act(() => { + result.current.handleSetupTechnologyChange(SetupTechnology.AGENT_BASED); + }); + + expect(result.current.selectedSetupTechnology).toBe(SetupTechnology.AGENT_BASED); + + expect(updateNewAgentPolicyMock).toHaveBeenCalledWith(newAgentPolicyMock); + expect(setSelectedPolicyTabMock).toHaveBeenCalledWith(SelectedPolicyTab.NEW); + }); + + it('should not update agent policy and selected policy tab when agentless is disabled', async () => { + mockedExperimentalFeaturesService.get.mockReturnValue({ + agentless: false, + } as any); + + const { result } = renderHook(() => + useSetupTechnology({ + updateNewAgentPolicy: updateNewAgentPolicyMock, + newAgentPolicy: newAgentPolicyMock, + updateAgentPolicy: updateAgentPolicyMock, + setSelectedPolicyTab: setSelectedPolicyTabMock, + }) + ); + + expect(result.current.selectedSetupTechnology).toBe(SetupTechnology.AGENT_BASED); + + act(() => { + result.current.handleSetupTechnologyChange(SetupTechnology.AGENTLESS); + }); + + expect(result.current.selectedSetupTechnology).toBe(SetupTechnology.AGENT_BASED); + }); + + it('should not update agent policy and selected policy tab when setup technology matches the current one ', async () => { + const { result, waitForNextUpdate } = renderHook(() => + useSetupTechnology({ + updateNewAgentPolicy: updateNewAgentPolicyMock, + newAgentPolicy: newAgentPolicyMock, + updateAgentPolicy: updateAgentPolicyMock, + setSelectedPolicyTab: setSelectedPolicyTabMock, + }) + ); + + await waitForNextUpdate(); + + expect(result.current.selectedSetupTechnology).toBe(SetupTechnology.AGENT_BASED); + + act(() => { + result.current.handleSetupTechnologyChange(SetupTechnology.AGENT_BASED); + }); + + expect(result.current.selectedSetupTechnology).toBe(SetupTechnology.AGENT_BASED); + + expect(updateNewAgentPolicyMock).not.toHaveBeenCalled(); + expect(setSelectedPolicyTabMock).not.toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.ts b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.ts new file mode 100644 index 0000000000000..22972f4a9a6d4 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/setup_technology.ts @@ -0,0 +1,86 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useCallback, useEffect, useState } from 'react'; + +import { ExperimentalFeaturesService } from '../../../../../services'; +import type { AgentPolicy, NewAgentPolicy } from '../../../../../types'; +import { SetupTechnology } from '../../../../../types'; +import { sendGetOneAgentPolicy, useStartServices } from '../../../../../hooks'; +import { SelectedPolicyTab } from '../../components'; + +const AGENTLESS_POLICY_ID = 'agentless'; + +export function useSetupTechnology({ + updateNewAgentPolicy, + newAgentPolicy, + updateAgentPolicy, + setSelectedPolicyTab, +}: { + updateNewAgentPolicy: (policy: NewAgentPolicy) => void; + newAgentPolicy: NewAgentPolicy; + updateAgentPolicy: (policy: AgentPolicy) => void; + setSelectedPolicyTab: (tab: SelectedPolicyTab) => void; +}) { + const { agentless: isAgentlessEnabled } = ExperimentalFeaturesService.get(); + const { cloud } = useStartServices(); + const isServerless = cloud?.isServerlessEnabled ?? false; + const [selectedSetupTechnology, setSelectedSetupTechnology] = useState( + SetupTechnology.AGENT_BASED + ); + const [agentlessPolicy, setAgentlessPolicy] = useState(); + + useEffect(() => { + const fetchAgentlessPolicy = async () => { + const { data, error } = await sendGetOneAgentPolicy(AGENTLESS_POLICY_ID); + const isAgentlessAvailable = !error && data && data.item; + + if (isAgentlessAvailable) { + setAgentlessPolicy(data.item); + } + }; + + if (isAgentlessEnabled && isServerless) { + fetchAgentlessPolicy(); + } + }, [isAgentlessEnabled, isServerless]); + + const handleSetupTechnologyChange = useCallback( + (setupTechnology) => { + if (!isAgentlessEnabled || setupTechnology === selectedSetupTechnology) { + return; + } + + if (setupTechnology === SetupTechnology.AGENTLESS) { + if (agentlessPolicy) { + updateAgentPolicy(agentlessPolicy); + setSelectedPolicyTab(SelectedPolicyTab.EXISTING); + } + } else if (setupTechnology === SetupTechnology.AGENT_BASED) { + updateNewAgentPolicy(newAgentPolicy); + setSelectedPolicyTab(SelectedPolicyTab.NEW); + } + + setSelectedSetupTechnology(setupTechnology); + }, + [ + isAgentlessEnabled, + selectedSetupTechnology, + agentlessPolicy, + updateAgentPolicy, + setSelectedPolicyTab, + updateNewAgentPolicy, + newAgentPolicy, + ] + ); + + return { + handleSetupTechnologyChange, + agentlessPolicy, + selectedSetupTechnology, + }; +} diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx index d0b82cf66e641..b5e29245c63cc 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx @@ -5,21 +5,21 @@ * 2.0. */ -import React, { useState, useEffect, useMemo, useCallback } from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { useRouteMatch } from 'react-router-dom'; import styled from 'styled-components'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { - EuiButtonEmpty, - EuiButton, - EuiSteps, EuiBottomBar, + EuiButton, + EuiButtonEmpty, + EuiCallOut, + EuiErrorBoundary, EuiFlexGroup, EuiFlexItem, EuiSpacer, - EuiErrorBoundary, - EuiCallOut, + EuiSteps, } from '@elastic/eui'; import type { EuiStepProps } from '@elastic/eui/src/components/steps/step'; @@ -31,35 +31,38 @@ import { import { useCancelAddPackagePolicy } from '../hooks'; import { isRootPrivilegesRequired, splitPkgKey } from '../../../../../../../common/services'; -import type { NewAgentPolicy } from '../../../../types'; -import { useConfig, sendGetAgentStatus, useGetPackageInfoByKeyQuery } from '../../../../hooks'; +import type { NewAgentPolicy, PackagePolicyEditExtensionComponentProps } from '../../../../types'; +import { SetupTechnology } from '../../../../types'; import { - Loading, + sendGetAgentStatus, + useConfig, + useGetPackageInfoByKeyQuery, + useUIExtension, +} from '../../../../hooks'; +import { + DevtoolsRequestFlyoutButton, Error as ErrorComponent, ExtensionWrapper, - DevtoolsRequestFlyoutButton, + Loading, } from '../../../../components'; import { agentPolicyFormValidation, ConfirmDeployAgentPolicyModal } from '../../components'; -import { useUIExtension } from '../../../../hooks'; -import type { PackagePolicyEditExtensionComponentProps } from '../../../../types'; import { pkgKeyFromPackageInfo } from '../../../../services'; import type { AddToPolicyParams, CreatePackagePolicyParams } from '../types'; -import { IntegrationBreadcrumb } from '../components'; - import { + IntegrationBreadcrumb, + SelectedPolicyTab, StepConfigurePackagePolicy, StepDefinePackagePolicy, - SelectedPolicyTab, StepSelectHosts, } from '../components'; import { generateNewAgentPolicyWithDefaults } from '../../../../../../../common/services/generate_new_agent_policy'; import { CreatePackagePolicySinglePageLayout, PostInstallAddAgentModal } from './components'; -import { useDevToolsRequest, useOnSubmit } from './hooks'; +import { useDevToolsRequest, useOnSubmit, useSetupTechnology } from './hooks'; import { PostInstallCloudFormationModal } from './components/post_install_cloud_formation_modal'; import { PostInstallGoogleCloudShellModal } from './components/post_install_google_cloud_shell_modal'; import { PostInstallAzureArmTemplateModal } from './components/post_install_azure_arm_template_modal'; @@ -293,6 +296,14 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({ ); } + const { agentlessPolicy, handleSetupTechnologyChange, selectedSetupTechnology } = + useSetupTechnology({ + newAgentPolicy, + updateNewAgentPolicy, + updateAgentPolicy, + setSelectedPolicyTab, + }); + const replaceStepConfigurePackagePolicy = replaceDefineStepView && packageInfo?.name ? ( !isInitialized ? ( @@ -306,6 +317,8 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({ onChange={handleExtensionViewOnChange} validationResults={validationResults} isEditPage={false} + handleSetupTechnologyChange={handleSetupTechnologyChange} + agentlessPolicy={agentlessPolicy} /> ) @@ -374,13 +387,16 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({ 'data-test-subj': 'dataCollectionSetupStep', children: replaceStepConfigurePackagePolicy || stepConfigurePackagePolicy, }, - { + ]; + + if (selectedSetupTechnology !== SetupTechnology.AGENTLESS) { + steps.push({ title: i18n.translate('xpack.fleet.createPackagePolicy.stepSelectAgentPolicyTitle', { defaultMessage: 'Where to add this integration?', }), children: stepSelectAgentPolicy, - }, - ]; + }); + } // Display package error if there is one if (packageInfoError) { diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_details/agent_details_overview.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_details/agent_details_overview.tsx index 74fb11be866e1..f27d428bd42cc 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_details/agent_details_overview.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_details_page/components/agent_details/agent_details_overview.tsx @@ -29,6 +29,7 @@ import { AgentHealth } from '../../../components'; import { Tags } from '../../../components/tags'; import { formatAgentCPU, formatAgentMemory } from '../../../services/agent_metrics'; import { AgentDashboardLink } from '../agent_dashboard_link'; +import { AgentUpgradeStatus } from '../../../agent_list_page/components/agent_upgrade_status'; // Allows child text to be truncated const FlexItemWithMinWidth = styled(EuiFlexItem)` @@ -173,18 +174,20 @@ export const AgentDetailsOverviewSection: React.FunctionComponent<{ {agent.local_metadata.elastic.agent.version} - {latestAgentVersion && isAgentUpgradeable(agent, latestAgentVersion) ? ( - - - - - - ) : null} + + +
) : ( '-' diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_list_table.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_list_table.tsx index c52f1bb6588ae..d555c163c0fc0 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_list_table.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_list_table.tsx @@ -31,6 +31,8 @@ import { Tags } from '../../components/tags'; import type { AgentMetrics } from '../../../../../../../common/types'; import { formatAgentCPU, formatAgentMemory } from '../../services/agent_metrics'; +import { getNotUpgradeableMessage } from '../../../../../../../common/services/is_agent_upgradeable'; + import { AgentUpgradeStatus } from './agent_upgrade_status'; import { EmptyPrompt } from './empty_prompt'; @@ -303,6 +305,7 @@ export const AgentListTable: React.FC = (props: Props) => { agentUpgradeStartedAt={agent.upgrade_started_at} agentUpgradedAt={agent.upgraded_at} agentUpgradeDetails={agent.upgrade_details} + notUpgradeableMessage={getNotUpgradeableMessage(agent, latestAgentVersion)} /> diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_upgrade_status.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_upgrade_status.test.tsx index 1518a68fd6f0c..a5f3498fd0b59 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_upgrade_status.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_upgrade_status.test.tsx @@ -48,12 +48,38 @@ describe('getDownloadEstimate', () => { expect(getDownloadEstimate()).toEqual(''); }); - it('should return an empty string if the agent has a zero download percent', () => { - expect(getDownloadEstimate(0)).toEqual(''); + it('should display 0% if the agent has a zero download percent', () => { + expect(getDownloadEstimate({ download_percent: 0 })).toEqual(' (0%)'); + }); + + it('should display 0 Bps if the agent has a zero download rate', () => { + expect(getDownloadEstimate({ download_rate: 0 })).toEqual(' (at 0.0 Bps)'); }); it('should return a formatted string if the agent has a positive download percent', () => { - expect(getDownloadEstimate(16.4)).toEqual(' (16.4%)'); + expect(getDownloadEstimate({ download_percent: 16.4 })).toEqual(' (16.4%)'); + }); + + it('should return a formatted string if the agent has a kBps download rate', () => { + expect(getDownloadEstimate({ download_rate: 1024 })).toEqual(' (at 1.0 kBps)'); + }); + + it('should return a formatted string if the agent has a download rate and download percent', () => { + expect(getDownloadEstimate({ download_rate: 10, download_percent: 99 })).toEqual( + ' (99% at 10.0 Bps)' + ); + }); + + it('should return a formatted string if the agent has a MBps download rate', () => { + expect(getDownloadEstimate({ download_rate: 1200000 })).toEqual(' (at 1.1 MBps)'); + }); + + it('should return a formatted string if the agent has a GBps download rate', () => { + expect(getDownloadEstimate({ download_rate: 2400000000 })).toEqual(' (at 2.2 GBps)'); + }); + + it('should return a formatted string if the agent has a GBps download rate more than 1024', () => { + expect(getDownloadEstimate({ download_rate: 1200000000 * 1024 })).toEqual(' (at 1144.4 GBps)'); }); }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_upgrade_status.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_upgrade_status.tsx index ab4835757f94d..e5cf2eb7913c6 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_upgrade_status.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_list_page/components/agent_upgrade_status.tsx @@ -35,14 +35,34 @@ export function getUpgradeStartDelay(scheduledAt?: string): string { return ` The upgrade will start in less than ${Math.ceil(timeDiffMillis / 36e5)} hours.`; } -export function getDownloadEstimate(downloadPercent?: number): string { - if (!downloadPercent || downloadPercent === 0) { +export function getDownloadEstimate(metadata?: AgentUpgradeDetails['metadata']): string { + if ( + !metadata || + (metadata.download_percent === undefined && metadata.download_rate === undefined) + ) { return ''; } + let tooltip = ''; + if (metadata.download_percent !== undefined) { + tooltip = `${metadata.download_percent}%`; + } + if (metadata.download_rate !== undefined) { + tooltip += ` at ${formatRate(metadata.download_rate)}`; + } - return ` (${downloadPercent}%)`; + return ` (${tooltip.trim()})`; } +const formatRate = (downloadRate: number) => { + let i = 0; + const byteUnits = [' Bps', ' kBps', ' MBps', ' GBps']; + for (; i < byteUnits.length - 1; i++) { + if (downloadRate < 1024) break; + downloadRate = downloadRate / 1024; + } + return downloadRate.toFixed(1) + byteUnits[i]; +}; + function getStatusComponents(agentUpgradeDetails?: AgentUpgradeDetails) { switch (agentUpgradeDetails?.state) { case 'UPG_REQUESTED': @@ -97,9 +117,7 @@ function getStatusComponents(agentUpgradeDetails?: AgentUpgradeDetails) { id="xpack.fleet.agentUpgradeStatusTooltip.upgradeDownloading" defaultMessage="Downloading the new agent artifact version{downloadEstimate}." values={{ - downloadEstimate: getDownloadEstimate( - agentUpgradeDetails?.metadata?.download_percent - ), + downloadEstimate: getDownloadEstimate(agentUpgradeDetails?.metadata), }} /> ), @@ -219,7 +237,14 @@ export const AgentUpgradeStatus: React.FC<{ agentUpgradeStartedAt?: string | null; agentUpgradedAt?: string | null; agentUpgradeDetails?: AgentUpgradeDetails; -}> = ({ isAgentUpgradable, agentUpgradeStartedAt, agentUpgradedAt, agentUpgradeDetails }) => { + notUpgradeableMessage?: string | null; +}> = ({ + isAgentUpgradable, + agentUpgradeStartedAt, + agentUpgradedAt, + agentUpgradeDetails, + notUpgradeableMessage, +}) => { const isAgentUpgrading = useMemo( () => agentUpgradeStartedAt && !agentUpgradedAt, [agentUpgradeStartedAt, agentUpgradedAt] @@ -227,6 +252,24 @@ export const AgentUpgradeStatus: React.FC<{ const status = useMemo(() => getStatusComponents(agentUpgradeDetails), [agentUpgradeDetails]); const minVersion = '8.12'; + if (!isAgentUpgradable && notUpgradeableMessage) { + return ( + + } + color="subdued" + /> + ); + } + if (isAgentUpgradable) { return ( diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_upgrade_modal/index.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_upgrade_modal/index.test.tsx index 11dcd1a34bb2a..17e415691a826 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_upgrade_modal/index.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_upgrade_modal/index.test.tsx @@ -7,11 +7,11 @@ import React from 'react'; -import { act, fireEvent, waitFor } from '@testing-library/react'; +import { act, fireEvent, waitFor, within } from '@testing-library/react'; import { createFleetTestRendererMock } from '../../../../../../mock'; -import { sendPostBulkAgentUpgrade } from '../../../../hooks'; +import { sendGetAgentsAvailableVersions, sendPostBulkAgentUpgrade } from '../../../../hooks'; import { AgentUpgradeAgentModal } from '.'; import type { AgentUpgradeAgentModalProps } from '.'; @@ -29,11 +29,14 @@ jest.mock('../../../../hooks', () => { }), sendPostBulkAgentUpgrade: jest.fn(), useAgentVersion: jest.fn().mockReturnValue('8.10.2'), + useKibanaVersion: jest.fn().mockReturnValue('8.10.2'), }; }); const mockSendPostBulkAgentUpgrade = sendPostBulkAgentUpgrade as jest.Mock; +const mockSendGetAgentsAvailableVersions = sendGetAgentsAvailableVersions as jest.Mock; + function renderAgentUpgradeAgentModal(props: Partial) { const renderer = createFleetTestRendererMock(); @@ -45,126 +48,184 @@ function renderAgentUpgradeAgentModal(props: Partial { - it('should set the default to Immediately if there is less than 10 agents using kuery', async () => { - const { utils } = renderAgentUpgradeAgentModal({ - agents: '*', - agentCount: 3, + describe('maintenance window', () => { + it('should set the default to Immediately if there is less than 10 agents using kuery', async () => { + const { utils } = renderAgentUpgradeAgentModal({ + agents: '*', + agentCount: 3, + }); + + const container = utils.getByTestId('agentUpgradeModal.MaintenanceCombobox'); + const input = within(container).getByRole('combobox'); + expect(input?.value).toBe('Immediately'); }); - const el = utils.getByTestId('agentUpgradeModal.MaintenanceCombobox'); - expect(el?.textContent).toBe('Immediately'); - }); + it('should set the default to Immediately if there is less than 10 agents using selected agents', async () => { + const { utils } = renderAgentUpgradeAgentModal({ + agents: [{ id: 'agent1' }, { id: 'agent2' }] as any, + agentCount: 3, + }); - it('should set the default to Immediately if there is less than 10 agents using selected agents', async () => { - const { utils } = renderAgentUpgradeAgentModal({ - agents: [{ id: 'agent1' }, { id: 'agent2' }] as any, - agentCount: 3, + const container = utils.getByTestId('agentUpgradeModal.MaintenanceCombobox'); + const input = within(container).getByRole('combobox'); + expect(input?.value).toBe('Immediately'); }); - const el = utils.getByTestId('agentUpgradeModal.MaintenanceCombobox'); - expect(el?.textContent).toBe('Immediately'); - }); + it('should set the default to 1 hour if there is more than 10 agents', async () => { + const { utils } = renderAgentUpgradeAgentModal({ + agents: '*', + agentCount: 13, + }); - it('should set the default to 1 hour if there is more than 10 agents', async () => { - const { utils } = renderAgentUpgradeAgentModal({ - agents: '*', - agentCount: 13, + const container = utils.getByTestId('agentUpgradeModal.MaintenanceCombobox'); + const input = within(container).getByRole('combobox'); + expect(input?.value).toBe('1 hour'); }); - - const el = utils.getByTestId('agentUpgradeModal.MaintenanceCombobox'); - expect(el?.textContent).toBe('1 hour'); }); - it('should enable the version combo if agents is a query', async () => { - const { utils } = renderAgentUpgradeAgentModal({ - agents: '*', - agentCount: 30, + describe('version combo', () => { + it('should enable the version combo if agents is a query', async () => { + const { utils } = renderAgentUpgradeAgentModal({ + agents: '*', + agentCount: 30, + }); + + const el = utils.getByTestId('agentUpgradeModal.VersionCombobox'); + await waitFor(() => { + expect(el.classList.contains('euiComboBox-isDisabled')).toBe(false); + }); }); - const el = utils.getByTestId('agentUpgradeModal.VersionCombobox'); - await waitFor(() => { - expect(el.classList.contains('euiComboBox-isDisabled')).toBe(false); - }); - }); + it('should default the version combo to latest agent version', async () => { + const { utils } = renderAgentUpgradeAgentModal({ + agents: [{ id: 'agent1', local_metadata: { host: 'abc' } }] as any, + agentCount: 1, + }); - it('should default the version combo to latest agent version', async () => { - const { utils } = renderAgentUpgradeAgentModal({ - agents: [{ id: 'agent1', local_metadata: { host: 'abc' } }] as any, - agentCount: 1, - }); + const container = utils.getByTestId('agentUpgradeModal.VersionCombobox'); + const input = within(container).getByRole('combobox'); - const el = utils.getByTestId('agentUpgradeModal.VersionCombobox'); - await waitFor(() => { - expect(el.textContent).toEqual('8.10.2'); + await waitFor(() => { + expect(input?.value).toEqual('8.10.2'); + }); }); - }); - it('should restart uprade on updating agents if some agents in updating', async () => { - const { utils } = renderAgentUpgradeAgentModal({ - agents: [ - { status: 'updating', upgrade_started_at: '2022-11-21T12:27:24Z', id: 'agent1' }, - { id: 'agent2' }, - ] as any, - agentCount: 2, - isUpdating: true, - }); - - const el = utils.getByTestId('confirmModalTitleText'); - expect(el.textContent).toEqual('Restart upgrade on 1 out of 2 agents stuck in updating'); - - const btn = utils.getByTestId('confirmModalConfirmButton'); - await waitFor(() => { - expect(btn).toBeEnabled(); - }); - - act(() => { - fireEvent.click(btn); + it('should display available version options', async () => { + mockSendGetAgentsAvailableVersions.mockClear(); + mockSendGetAgentsAvailableVersions.mockResolvedValue({ + data: { + items: ['8.10.4', '8.10.2+build123456789', '8.10.2', '8.7.0'], + }, + }); + const { utils } = renderAgentUpgradeAgentModal({ + agents: [ + { + id: 'agent1', + local_metadata: { host: 'abc', elastic: { agent: { version: '8.10.2' } } }, + }, + ] as any, + agentCount: 1, + }); + fireEvent.click(await utils.findByTestId('comboBoxToggleListButton')); + const optionList = await utils.findByTestId( + 'comboBoxOptionsList agentUpgradeModal.VersionCombobox-optionsList' + ); + expect(optionList.textContent).toEqual(['8.10.4', '8.10.2+build123456789'].join('')); }); - - expect(mockSendPostBulkAgentUpgrade.mock.calls.at(-1)[0]).toEqual( - expect.objectContaining({ agents: ['agent1'], force: true }) - ); }); - it('should restart upgrade on updating agents if kuery', async () => { - const { utils } = renderAgentUpgradeAgentModal({ - agents: '*', - agentCount: 3, - isUpdating: true, + describe('restart upgrade', () => { + it('should restart uprade on updating agents if some agents in updating', async () => { + const { utils } = renderAgentUpgradeAgentModal({ + agents: [ + { status: 'updating', upgrade_started_at: '2022-11-21T12:27:24Z', id: 'agent1' }, + { id: 'agent2' }, + ] as any, + agentCount: 2, + isUpdating: true, + }); + + const el = utils.getByTestId('confirmModalTitleText'); + expect(el.textContent).toEqual('Restart upgrade on 1 out of 2 agents stuck in updating'); + + const btn = utils.getByTestId('confirmModalConfirmButton'); + await waitFor(() => { + expect(btn).toBeEnabled(); + }); + + act(() => { + fireEvent.click(btn); + }); + + expect(mockSendPostBulkAgentUpgrade.mock.calls.at(-1)[0]).toEqual( + expect.objectContaining({ agents: ['agent1'], force: true }) + ); }); - const el = await utils.findByTestId('confirmModalTitleText'); - expect(el.textContent).toEqual('Restart upgrade on 2 out of 3 agents stuck in updating'); - - const btn = utils.getByTestId('confirmModalConfirmButton'); - await waitFor(() => { - expect(btn).toBeEnabled(); + it('should restart upgrade on updating agents if kuery', async () => { + const { utils } = renderAgentUpgradeAgentModal({ + agents: '*', + agentCount: 3, + isUpdating: true, + }); + + const el = await utils.findByTestId('confirmModalTitleText'); + expect(el.textContent).toEqual('Restart upgrade on 2 out of 3 agents stuck in updating'); + + const btn = utils.getByTestId('confirmModalConfirmButton'); + await waitFor(() => { + expect(btn).toBeEnabled(); + }); + + act(() => { + fireEvent.click(btn); + }); + + expect(mockSendPostBulkAgentUpgrade.mock.calls.at(-1)[0]).toEqual( + expect.objectContaining({ + agents: + '(*) AND status:updating AND upgrade_started_at:* AND NOT upgraded_at:* AND upgrade_started_at < now-2h', + force: true, + }) + ); }); - act(() => { - fireEvent.click(btn); + it('should disable submit button if no agents stuck updating', () => { + const { utils } = renderAgentUpgradeAgentModal({ + agents: [ + { status: 'offline', upgrade_started_at: '2022-11-21T12:27:24Z', id: 'agent1' }, + { id: 'agent2' }, + ] as any, + agentCount: 2, + isUpdating: true, + }); + + const el = utils.getByTestId('confirmModalConfirmButton'); + expect(el).toBeDisabled(); }); - - expect(mockSendPostBulkAgentUpgrade.mock.calls.at(-1)[0]).toEqual( - expect.objectContaining({ - agents: - '(*) AND status:updating AND upgrade_started_at:* AND NOT upgraded_at:* AND upgrade_started_at < now-2h', - force: true, - }) - ); }); - it('should disable submit button if no agents stuck updating', () => { + it('should disable submit button and display a warning for a single agent that is not upgradeable', async () => { const { utils } = renderAgentUpgradeAgentModal({ agents: [ - { status: 'offline', upgrade_started_at: '2022-11-21T12:27:24Z', id: 'agent1' }, - { id: 'agent2' }, + { + status: 'offline', + upgrade_started_at: '2022-11-21T12:27:24Z', + id: 'agent1', + local_metadata: { elastic: { agent: { version: '8.9.0' } } }, + }, ] as any, agentCount: 2, - isUpdating: true, }); - - const el = utils.getByTestId('confirmModalConfirmButton'); - expect(el).toBeDisabled(); + await waitFor(() => { + expect(utils.queryByText(/The selected agent is not upgradeable/)).toBeInTheDocument(); + expect( + utils.queryByText( + /Reason: agent cannot be upgraded through Fleet. It may be running in a container or it is not installed as a service./ + ) + ).toBeInTheDocument(); + const el = utils.getByTestId('confirmModalConfirmButton'); + expect(el).toBeDisabled(); + }); }); }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_upgrade_modal/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_upgrade_modal/index.tsx index d361349b3f327..28bb86fdb5382 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_upgrade_modal/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/components/agent_upgrade_modal/index.tsx @@ -44,9 +44,14 @@ import { useConfig, sendGetAgentStatus, useAgentVersion, + differsOnlyInPatch, } from '../../../../hooks'; import { sendGetAgentsAvailableVersions } from '../../../../hooks'; +import { + isAgentUpgradeable, + getNotUpgradeableMessage, +} from '../../../../../../../common/services/is_agent_upgradeable'; import { FALLBACK_VERSIONS, @@ -126,7 +131,6 @@ export const AgentUpgradeAgentModal: React.FunctionComponent> = useMemo(() => { const displayVersions = minVersion - ? availableVersions.filter((v) => semverGt(v, minVersion)) + ? availableVersions.filter( + (v) => semverGt(v, minVersion) || differsOnlyInPatch(v, minVersion, false) + ) : availableVersions; const options = displayVersions.map((option) => ({ @@ -235,7 +241,9 @@ export const AgentUpgradeAgentModal: React.FunctionComponent Array.isArray(agentsOrQuery) ? agentsOrQuery.map((agent) => agent.id) : agentsOrQuery; const { error } = - isSingleAgent && !isScheduled + isSingleAgent && + !isScheduled && + isAgentUpgradeable(agents[0], latestAgentVersion || '', selectedVersion[0].value) ? await sendPostAgentUpgrade((agents[0] as Agent).id, { version, force: isUpdating, @@ -329,7 +337,13 @@ export const AgentUpgradeAgentModal: React.FunctionComponent } - confirmButtonDisabled={isSubmitting || noVersions || (isUpdating && updatingAgents === 0)} + confirmButtonDisabled={ + isSubmitting || + noVersions || + (isUpdating && updatingAgents === 0) || + (isSingleAgent && + !isAgentUpgradeable(agents[0], latestAgentVersion || '', selectedVersion[0].value)) + } confirmButtonText={ isSingleAgent ? ( ) : isSingleAgent ? ( - <> -

+ !isAgentUpgradeable(agents[0], latestAgentVersion || '', selectedVersion[0].value) ? ( + + } + > -

- {isUpdating && ( + + ) : ( + <>

- - - +

- )} - + {isUpdating && ( +

+ + + +

+ )} + + ) ) : ( + > + + ) : null} diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx index 7ceef20111042..daebac615d6eb 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/index.tsx @@ -7,6 +7,7 @@ import React, { useMemo } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; +import { safeLoad } from 'js-yaml'; import { EuiFlyout, @@ -31,14 +32,23 @@ import { EuiBetaBadge, useEuiTheme, EuiText, + EuiAccordion, + EuiCode, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { css } from '@emotion/react'; +import type { OutputType, ValueOf } from '../../../../../../../common/types'; + +import { + outputTypeSupportPresets, + outputYmlIncludesReservedPerformanceKey, +} from '../../../../../../../common/services/output_helpers'; + import { ExperimentalFeaturesService } from '../../../../../../services'; -import { outputType } from '../../../../../../../common/constants'; +import { outputType, RESERVED_CONFIG_YML_KEYS } from '../../../../../../../common/constants'; import { MultiRowInput } from '../multi_row_input'; import type { Output, FleetProxy } from '../../../../types'; @@ -87,7 +97,13 @@ export const EditOutputFlyout: React.FunctionComponent = const { kafkaOutput: isKafkaOutputEnabled, remoteESOutput: isRemoteESOutputEnabled } = ExperimentalFeaturesService.get(); + const isRemoteESOutput = inputs.typeInput.value === outputType.RemoteElasticsearch; + const isESOutput = inputs.typeInput.value === outputType.Elasticsearch; + const supportsPresets = inputs.typeInput.value + ? outputTypeSupportPresets(inputs.typeInput.value as ValueOf) + : false; + // Remote ES output not yet supported in serverless const isStateful = !cloud?.isServerlessEnabled; @@ -211,6 +227,7 @@ export const EditOutputFlyout: React.FunctionComponent = })} {...inputs.sslKeySecretInput.formRowProps} onUsePlainText={onUsePlainText} + cancelEdit={inputs.sslKeySecretInput.cancelEdit} > = }; const renderTypeSpecificWarning = () => { - const isESOutput = inputs.typeInput.value === outputType.Elasticsearch; const isKafkaOutput = inputs.typeInput.value === outputType.Kafka; if (!isKafkaOutput && !isESOutput && !isRemoteESOutput) { return null; @@ -507,30 +523,6 @@ export const EditOutputFlyout: React.FunctionComponent = /> )} - - {i18n.translate('xpack.fleet.settings.editOutputFlyout.yamlConfigInputLabel', { - defaultMessage: 'Advanced YAML configuration', - })} - - } - {...inputs.additionalYamlConfigInput.formRowProps} - fullWidth - > - - = }} /> } - disabled={isRemoteESOutput} /> @@ -574,7 +565,131 @@ export const EditOutputFlyout: React.FunctionComponent = } /> + {supportsPresets && ( + <> + + + } + helpText={ + Custom, + link: ( + + + + ), + }} + /> + } + > + inputs.presetInput.setValue(e.target.value)} + disabled={ + inputs.presetInput.props.disabled || + outputYmlIncludesReservedPerformanceKey( + inputs.additionalYamlConfigInput.value, + safeLoad + ) + } + options={[ + { value: 'balanced', text: 'Balanced' }, + { value: 'custom', text: 'Custom' }, + { value: 'throughput', text: 'Throughput' }, + { value: 'scale', text: 'Scale' }, + { value: 'latency', text: 'Latency' }, + ]} + /> + + + )} + {supportsPresets && + outputYmlIncludesReservedPerformanceKey( + inputs.additionalYamlConfigInput.value, + safeLoad + ) && ( + <> + + + } + > + + } + > +
    + {RESERVED_CONFIG_YML_KEYS.map((key) => ( +
  • + {key} +
  • + ))} +
+
+
+ + )} + + + {i18n.translate('xpack.fleet.settings.editOutputFlyout.yamlConfigInputLabel', { + defaultMessage: 'Advanced YAML configuration', + })} + + } + {...inputs.additionalYamlConfigInput.formRowProps} + fullWidth + > + { + if (outputYmlIncludesReservedPerformanceKey(value, safeLoad)) { + inputs.presetInput.setValue('custom'); + } + + inputs.additionalYamlConfigInput.setValue(value); + }} + disabled={inputs.additionalYamlConfigInput.props.disabled} + placeholder={i18n.translate( + 'xpack.fleet.settings.editOutputFlyout.yamlConfigInputPlaceholder', + { + defaultMessage: + '# YAML settings here will be added to the output section of each agent policy.', + } + )} + /> + {output?.id && output.type === 'remote_elasticsearch' ? ( diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_kafka_authentication.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_kafka_authentication.tsx index 30665eb0e7c44..b70a9828371a4 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_kafka_authentication.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_kafka_authentication.tsx @@ -182,6 +182,7 @@ export const OutputFormKafkaAuthentication: React.FunctionComponent<{ )} {...inputs.kafkaSslKeySecretInput.formRowProps} onUsePlainText={onUsePlainText} + cancelEdit={inputs.kafkaSslKeySecretInput.cancelEdit} > = (props) > = (props) defaultMessage: 'Service Token', })} {...inputs.serviceTokenSecretInput.formRowProps} + cancelEdit={inputs.serviceTokenSecretInput.cancelEdit} onUsePlainText={onUsePlainText} > { const initialValue = 'initial value'; const clear = jest.fn(); const onUsePlainText = jest.fn(); + const cancelEdit = jest.fn(); it('should switch to edit mode when the replace button is clicked', () => { const { getByText, queryByText, container } = render( @@ -23,6 +24,7 @@ describe('SecretFormRow', () => { initialValue={initialValue} clear={clear} onUsePlainText={onUsePlainText} + cancelEdit={cancelEdit} > @@ -38,13 +40,14 @@ describe('SecretFormRow', () => { expect(queryByText(initialValue)).not.toBeInTheDocument(); }); - it('should call the clear function when the cancel button is clicked', () => { + it('should call the cancelEdit function when the cancel button is clicked', () => { const { getByText } = render( @@ -53,12 +56,17 @@ describe('SecretFormRow', () => { fireEvent.click(getByText('Replace Test Secret')); fireEvent.click(getByText('Cancel Test Secret change')); - expect(clear).toHaveBeenCalled(); + expect(cancelEdit).toHaveBeenCalled(); }); it('should call the onUsePlainText function when the revert link is clicked', () => { const { getByText } = render( - + ); @@ -67,4 +75,20 @@ describe('SecretFormRow', () => { expect(onUsePlainText).toHaveBeenCalled(); }); + + it('should not display the cancel change button when no initial value is provided', () => { + const { queryByTestId } = render( + + + + ); + + expect(queryByTestId('secretCancelChangeBtn')).not.toBeInTheDocument(); + }); }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_secret_form_row.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_secret_form_row.tsx index fc55835557403..868c80b895fa3 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_secret_form_row.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_form_secret_form_row.tsx @@ -29,8 +29,19 @@ export const SecretFormRow: React.FC<{ clear: () => void; initialValue?: any; onUsePlainText: () => void; -}> = ({ fullWidth, error, isInvalid, children, clear, title, initialValue, onUsePlainText }) => { - const hasInitialValue = initialValue !== undefined; + cancelEdit: () => void; +}> = ({ + fullWidth, + error, + isInvalid, + children, + clear, + title, + initialValue, + onUsePlainText, + cancelEdit, +}) => { + const hasInitialValue = !!initialValue; const [editMode, setEditMode] = useState(!initialValue); const valueHiddenPanel = ( @@ -66,7 +77,7 @@ export const SecretFormRow: React.FC<{ { setEditMode(false); - clear(); + cancelEdit(); }} color="primary" iconType="refresh" @@ -87,7 +98,7 @@ export const SecretFormRow: React.FC<{ <> {children} {hasInitialValue && ( - + {cancelButton} )} 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 28218fed7e942..26e63803df0e7 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 @@ -11,11 +11,11 @@ import { safeLoad } from 'js-yaml'; const toSecretValidator = (validator: (value: string) => string[] | undefined) => (value: string | { id: string } | undefined) => { - if (!value || typeof value === 'object') { + if (typeof value === 'object') { return undefined; } - return validator(value); + return validator(value ?? ''); }; export function validateKafkaHosts(value: string[]) { diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_health.test.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_health.test.tsx index 7aa29322229db..a666dda3bac4c 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_health.test.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_health.test.tsx @@ -72,7 +72,7 @@ describe('OutputHealth', () => { await waitFor(async () => { expect(utils.getByTestId('outputHealthDegradedCallout').textContent).toContain( - 'Unable to connect to "Remote ES" at https://remote-es:443. Please check the details are correct.' + 'Unable to connect to "Remote ES" at https://remote-es:443.Please check the details are correct.' ); }); }); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_health.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_health.tsx index c26a122287d01..0d71bb075cf25 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_health.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/output_health.tsx @@ -51,7 +51,7 @@ export const OutputHealth: React.FunctionComponent = ({ output, showBadge iconType="error" data-test-subj="outputHealthDegradedCallout" > -

+

{i18n.translate('xpack.fleet.output.calloutText', { defaultMessage: 'Unable to connect to "{name}" at {host}.', values: { @@ -59,7 +59,7 @@ export const OutputHealth: React.FunctionComponent = ({ output, showBadge host: output.hosts?.join(',') ?? '', }, })} -

{' '} +

{i18n.translate('xpack.fleet.output.calloutPromptText', { defaultMessage: 'Please check the details are correct.', diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/use_output_form.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/use_output_form.tsx index cd203472d18c4..59b1189818225 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/use_output_form.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/components/edit_output_flyout/use_output_form.tsx @@ -10,6 +10,8 @@ import { useCallback, useState } from 'react'; import { i18n } from '@kbn/i18n'; import { safeLoad } from 'js-yaml'; +import { getDefaultPresetForEsOutput } from '../../../../../../../common/services/output_helpers'; + import type { KafkaOutput, NewElasticsearchOutput, @@ -84,6 +86,7 @@ export interface OutputFormInputsType { diskQueueCompressionEnabled: ReturnType; compressionLevelInput: ReturnType; logstashHostsInput: ReturnType; + presetInput: ReturnType; additionalYamlConfigInput: ReturnType; defaultOutputInput: ReturnType; defaultMonitoringOutputInput: ReturnType; @@ -176,6 +179,7 @@ export function useOutputForm(onSucess: () => void, output?: Output) { if (!isPreconfigured) { return false; } + return !allowEdit.includes(field); } @@ -211,6 +215,12 @@ export function useOutputForm(onSucess: () => void, output?: Output) { isDisabled('hosts') ); + const presetInput = useInput( + output?.preset ?? getDefaultPresetForEsOutput(output?.config_yaml ?? '', safeLoad), + () => undefined, + isDisabled('preset') + ); + // Remtote ES inputs const serviceTokenInput = useInput( (output as NewRemoteElasticsearchOutput)?.service_token ?? '', @@ -506,6 +516,7 @@ export function useOutputForm(onSucess: () => void, output?: Output) { diskQueueCompressionEnabled, compressionLevelInput, logstashHostsInput, + presetInput, additionalYamlConfigInput, defaultOutputInput, defaultMonitoringOutputInput, @@ -865,6 +876,7 @@ export function useOutputForm(onSucess: () => void, output?: Output) { hosts: elasticsearchUrlInput.value, is_default: false, is_default_monitoring: defaultMonitoringOutputInput.value, + preset: presetInput.value, config_yaml: additionalYamlConfigInput.value, service_token: serviceTokenInput.value || undefined, ...(!serviceTokenInput.value && @@ -884,6 +896,7 @@ export function useOutputForm(onSucess: () => void, output?: Output) { hosts: elasticsearchUrlInput.value, is_default: defaultOutputInput.value, is_default_monitoring: defaultMonitoringOutputInput.value, + preset: presetInput.value, config_yaml: additionalYamlConfigInput.value, ca_trusted_fingerprint: caTrustedFingerprintInput.value, proxy_id: proxyIdValue, @@ -940,26 +953,26 @@ export function useOutputForm(onSucess: () => void, output?: Output) { loadBalanceEnabledInput.value, typeInput.value, kafkaSslCertificateAuthoritiesInput.value, - kafkaCompressionInput.value, + kafkaSslKeyInput, + kafkaSslKeySecretInput, + kafkaAuthPasswordInput, + kafkaAuthPasswordSecretInput, nameInput.value, kafkaHostsInput.value, defaultOutputInput.value, defaultMonitoringOutputInput.value, additionalYamlConfigInput.value, + kafkaConnectionTypeInput.value, kafkaAuthMethodInput.value, kafkaSslCertificateInput.value, - kafkaSslKeyInput, - kafkaSslKeySecretInput, kafkaVerificationModeInput.value, kafkaClientIdInput.value, kafkaVersionInput.value, kafkaKeyInput.value, + kafkaCompressionInput.value, kafkaCompressionCodecInput.value, kafkaCompressionLevelInput.value, - kafkaConnectionTypeInput.value, kafkaAuthUsernameInput.value, - kafkaAuthPasswordInput, - kafkaAuthPasswordSecretInput, kafkaSaslMechanismInput.value, kafkaPartitionTypeInput.value, kafkaPartitionTypeRandomInput.value, @@ -974,12 +987,13 @@ export function useOutputForm(onSucess: () => void, output?: Output) { logstashHostsInput.value, sslCertificateInput.value, sslKeyInput.value, - sslKeySecretInput.value, sslCertificateAuthoritiesInput.value, + sslKeySecretInput.value, elasticsearchUrlInput.value, - caTrustedFingerprintInput.value, serviceTokenInput.value, serviceTokenSecretInput.value, + presetInput.value, + caTrustedFingerprintInput.value, confirm, notifications.toasts, ]); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/index.tsx index 34989d2920e73..420b1ef55ee11 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/settings/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/settings/index.tsx @@ -27,11 +27,11 @@ import { FleetServerFlyout } from '../../components'; import { SettingsPage } from './components/settings_page'; import { withConfirmModalProvider } from './hooks/use_confirm_modal'; import { FleetServerHostsFlyout } from './components/fleet_server_hosts_flyout'; -import { EditOutputFlyout } from './components/edit_output_flyout'; import { useDeleteOutput, useDeleteFleetServerHost, useDeleteProxy } from './hooks'; import { EditDownloadSourceFlyout } from './components/download_source_flyout'; import { useDeleteDownloadSource } from './components/download_source_flyout/use_delete_download_source'; import { FleetProxyFlyout } from './components/edit_fleet_proxy_flyout'; +import { EditOutputFlyout } from './components/edit_output_flyout'; function useSettingsAppData() { const outputs = useGetOutputs(); diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/integration_preference.stories.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/integration_preference.stories.tsx index ebc18d84487db..b0655a78fd1e1 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/integration_preference.stories.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/integration_preference.stories.tsx @@ -36,7 +36,7 @@ export const IntegrationPreference = () => { {}} + prereleaseIntegrationsEnabled={false} /> ); }; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/integration_preference.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/integration_preference.tsx index 71f0370d13832..9f1d716f6f396 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/integration_preference.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/integration_preference.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useCallback, useEffect } from 'react'; +import React, { useCallback } from 'react'; import styled from 'styled-components'; import { i18n } from '@kbn/i18n'; @@ -23,7 +23,7 @@ import { EuiSwitch, } from '@elastic/eui'; -import { sendPutSettings, useGetSettings, useStartServices } from '../../../hooks'; +import { usePutSettingsMutation, useStartServices } from '../../../hooks'; export type IntegrationPreferenceType = 'recommended' | 'beats' | 'agent'; @@ -35,7 +35,7 @@ interface Option { export interface Props { initialType: IntegrationPreferenceType; onChange: (type: IntegrationPreferenceType) => void; - onPrereleaseEnabledChange: (prerelease: boolean) => void; + prereleaseIntegrationsEnabled: boolean; } const recommendedTooltip = ( @@ -86,42 +86,39 @@ const options: Option[] = [ export const IntegrationPreference = ({ initialType, onChange, - onPrereleaseEnabledChange, + prereleaseIntegrationsEnabled, }: Props) => { const [idSelected, setIdSelected] = React.useState(initialType); - - const { docLinks } = useStartServices(); - - const [prereleaseIntegrationsEnabled, setPrereleaseIntegrationsEnabled] = React.useState< + const [prereleaseIntegrationsChecked, setPrereleaseIntegrationsChecked] = React.useState< boolean | undefined >(undefined); - const { data: settings, error: settingsError } = useGetSettings(); - - useEffect(() => { - const isEnabled = Boolean(settings?.item.prerelease_integrations_enabled); - if (settings?.item) { - setPrereleaseIntegrationsEnabled(isEnabled); - } else if (settingsError) { - setPrereleaseIntegrationsEnabled(false); - } - }, [settings?.item, settingsError]); - - useEffect(() => { - if (prereleaseIntegrationsEnabled !== undefined) { - onPrereleaseEnabledChange(prereleaseIntegrationsEnabled); - } - }, [onPrereleaseEnabledChange, prereleaseIntegrationsEnabled]); - - const updateSettings = useCallback(async (prerelease: boolean) => { - const res = await sendPutSettings({ - prerelease_integrations_enabled: prerelease, - }); - - if (res.error) { - throw res.error; - } - }, []); + const { docLinks, notifications } = useStartServices(); + + const { mutateAsync: mutateSettingsAsync } = usePutSettingsMutation(); + + const updateSettings = useCallback( + async (prerelease: boolean) => { + try { + setPrereleaseIntegrationsChecked(prerelease); + const res = await mutateSettingsAsync({ + prerelease_integrations_enabled: prerelease, + }); + + if (res.error) { + throw res.error; + } + } catch (error) { + setPrereleaseIntegrationsChecked(!prerelease); + notifications.toasts.addError(error, { + title: i18n.translate('xpack.fleet.errorUpdatingSettings', { + defaultMessage: 'Error updating settings', + }), + }); + } + }, + [mutateSettingsAsync, notifications.toasts] + ); const link = ( @@ -153,16 +150,18 @@ export const IntegrationPreference = ({ EventTarget & { checked: boolean } > ) => { - const isChecked = event.target.checked; - setPrereleaseIntegrationsEnabled(isChecked); - updateSettings(isChecked); + updateSettings(event.target.checked); }; return ( diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid/grid.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid/grid.tsx index 0e85390a4b327..214ce24463e54 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid/grid.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/components/package_list_grid/grid.tsx @@ -125,8 +125,7 @@ export const GridColumn = ({ // Ensure that cards wrapped in EuiTours/EuiPopovers correctly inherit the full grid row height css={css` & > .euiPopover, - & > .euiPopover > .euiPopover__anchor, - & > .euiPopover > .euiPopover__anchor > .euiCard { + & > .euiPopover > .euiCard { height: 100%; } `} diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/available_packages.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/available_packages.tsx index 47ee17dcc601c..8a3abb9fc962c 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/available_packages.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/available_packages.tsx @@ -97,9 +97,9 @@ function OnPremLink() { ); } -export const AvailablePackages: React.FC<{ - setPrereleaseEnabled: (isEnabled: boolean) => void; -}> = ({ setPrereleaseEnabled }) => { +export const AvailablePackages: React.FC<{ prereleaseIntegrationsEnabled: boolean }> = ({ + prereleaseIntegrationsEnabled, +}) => { useBreadcrumbs('integrations_all'); const { @@ -121,11 +121,10 @@ export const AvailablePackages: React.FC<{ setUrlandPushHistory, setUrlandReplaceHistory, filteredCards, - setPrereleaseIntegrationsEnabled, availableSubCategories, selectedSubCategory, setSelectedSubCategory, - } = useAvailablePackages(); + } = useAvailablePackages({ prereleaseIntegrationsEnabled }); const onCategoryChange = useCallback( ({ id }: { id: string }) => { @@ -137,14 +136,6 @@ export const AvailablePackages: React.FC<{ [setCategory, setSearchTerm, setSelectedSubCategory, setUrlandPushHistory] ); - const onPrereleaseEnabledChange = useCallback( - (isEnabled: boolean) => { - setPrereleaseIntegrationsEnabled(isEnabled); - setPrereleaseEnabled(isEnabled); - }, - [setPrereleaseIntegrationsEnabled, setPrereleaseEnabled] - ); - if (!isLoading && !categoryExists(initialSelectedCategory, allCategories)) { setUrlandReplaceHistory({ searchString: searchTerm, categoryId: '', subCategoryId: '' }); return null; @@ -155,8 +146,8 @@ export const AvailablePackages: React.FC<{ , ]; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/card_utils.test.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/card_utils.test.tsx index 327d550df5780..77cfdb3a9102e 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/card_utils.test.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/card_utils.test.tsx @@ -9,8 +9,9 @@ import React from 'react'; import { createIntegrationsTestRendererMock } from '../../../../../../mock'; import type { PackageListItem } from '../../../../types'; +import { ExperimentalFeaturesService } from '../../../../services'; -import { getIntegrationLabels } from './card_utils'; +import { getIntegrationLabels, mapToCard } from './card_utils'; function renderIntegrationLabels(item: Partial) { const renderer = createIntegrationsTestRendererMock(); @@ -18,7 +19,73 @@ function renderIntegrationLabels(item: Partial) { return renderer.render(<>{getIntegrationLabels(item as any)}); } +const addBasePath = (s: string) => s; +const getHref = (k: string) => k; + describe('Card utils', () => { + describe('mapToCard', () => { + beforeEach(() => { + ExperimentalFeaturesService.init({}); + }); + + it('should use the installed version if available, without prelease', () => { + const cardItem = mapToCard({ + item: { + id: 'test', + version: '2.0.0-preview-1', + installationInfo: { + version: '1.0.0', + }, + }, + addBasePath, + getHref, + } as any); + + expect(cardItem).toMatchObject({ + release: 'ga', + version: '1.0.0', + isUpdateAvailable: true, + extraLabelsBadges: undefined, + }); + }); + + it('should use the installed version if available, with prelease ', () => { + const cardItem = mapToCard({ + item: { + id: 'test', + version: '2.0.0', + installationInfo: { + version: '1.0.0-preview-1', + }, + }, + addBasePath, + getHref, + } as any); + + expect(cardItem).toMatchObject({ + release: 'preview', + version: '1.0.0-preview-1', + isUpdateAvailable: true, + }); + }); + + it('should use the registry version if no installation is available ', () => { + const cardItem = mapToCard({ + item: { + id: 'test', + version: '2.0.0-preview-1', + }, + addBasePath, + getHref, + } as any); + + expect(cardItem).toMatchObject({ + release: 'preview', + version: '2.0.0-preview-1', + isUpdateAvailable: false, + }); + }); + }); describe('getIntegrationLabels', () => { it('should return an empty list for an integration without errors', () => { const res = renderIntegrationLabels({ diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/card_utils.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/card_utils.tsx index 910b872bf5ceb..1caea49e2096e 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/card_utils.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/card_utils.tsx @@ -75,7 +75,7 @@ export const mapToCard = ({ let isUnverified = false; - const version = 'version' in item ? item.version || '' : ''; + let version = 'version' in item ? item.version || '' : ''; let isUpdateAvailable = false; let isReauthorizationRequired = false; @@ -84,9 +84,8 @@ export const mapToCard = ({ ? addBasePath(item.uiInternalPath) : item.uiExternalLink || getAbsolutePath(item.uiInternalPath); } else { - let urlVersion = item.version; if (item?.installationInfo?.version) { - urlVersion = item.installationInfo.version || item.version; + version = item.installationInfo.version || item.version; isUnverified = isPackageUnverified(item, packageVerificationKeyId); isUpdateAvailable = isPackageUpdatable(item); @@ -94,7 +93,7 @@ export const mapToCard = ({ } const url = getHref('integration_details_overview', { - pkgkey: `${item.name}-${urlVersion}`, + pkgkey: `${item.name}-${version}`, ...(item.integration ? { integration: item.integration } : {}), }); diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/hooks/use_available_packages.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/hooks/use_available_packages.tsx index ca66f0c2020d0..9aa242f8575b3 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/hooks/use_available_packages.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/hooks/use_available_packages.tsx @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useState, useMemo } from 'react'; +import { useState, useMemo } from 'react'; import { uniq } from 'lodash'; @@ -103,11 +103,13 @@ const packageListToIntegrationsList = (packages: PackageList): PackageList => { }, []); }; -export const useAvailablePackages = () => { +export const useAvailablePackages = ({ + prereleaseIntegrationsEnabled, +}: { + prereleaseIntegrationsEnabled: boolean; +}) => { const [preference, setPreference] = useState('recommended'); - const [prereleaseIntegrationsEnabled, setPrereleaseIntegrationsEnabled] = React.useState< - boolean | undefined - >(undefined); + const { showIntegrationsSubcategories } = ExperimentalFeaturesService.get(); const { @@ -245,6 +247,5 @@ export const useAvailablePackages = () => { eprPackageLoadingError, eprCategoryLoadingError, filteredCards, - setPrereleaseIntegrationsEnabled, }; }; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx index 81dec69d0d911..72e2d57412d41 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/home/index.tsx @@ -5,8 +5,9 @@ * 2.0. */ -import React, { useState, useMemo } from 'react'; +import React, { useMemo } from 'react'; import { Routes, Route } from '@kbn/shared-ux-router'; +import { EuiLoadingSpinner } from '@elastic/eui'; import { installationStatuses } from '../../../../../../../common/constants'; @@ -14,7 +15,7 @@ import { INTEGRATIONS_ROUTING_PATHS, INTEGRATIONS_SEARCH_QUERYPARAM } from '../. import { DefaultLayout } from '../../../../layouts'; import { isPackageUpdatable } from '../../../../services'; -import { useGetPackagesQuery } from '../../../../hooks'; +import { useAuthz, useGetPackagesQuery, useGetSettingsQuery } from '../../../../hooks'; import type { CategoryFacet, ExtendedIntegrationCategory } from './category_facets'; @@ -41,12 +42,23 @@ export const categoryExists = (category: string, categories: CategoryFacet[]) => }; export const EPMHomePage: React.FC = () => { - const [prereleaseEnabled, setPrereleaseEnabled] = useState(false); + const authz = useAuthz(); + const isAuthorizedToFetchSettings = authz.fleet.all; + const { data: settings, isFetchedAfterMount: isSettingsFetched } = useGetSettingsQuery({ + enabled: isAuthorizedToFetchSettings, + }); + const prereleaseIntegrationsEnabled = settings?.item.prerelease_integrations_enabled ?? false; + const shouldFetchPackages = !isAuthorizedToFetchSettings || isSettingsFetched; // loading packages to find installed ones - const { data: allPackages, isLoading } = useGetPackagesQuery({ - prerelease: prereleaseEnabled, - }); + const { data: allPackages, isLoading } = useGetPackagesQuery( + { + prerelease: prereleaseIntegrationsEnabled, + }, + { + enabled: shouldFetchPackages, + } + ); const installedPackages = useMemo( () => @@ -76,6 +88,11 @@ export const EPMHomePage: React.FC = () => { const notificationsBySection = { manage: unverifiedPackageCount + upgradeablePackageCount, }; + + if (!shouldFetchPackages) { + return ; + } + return ( @@ -85,7 +102,7 @@ export const EPMHomePage: React.FC = () => { - + diff --git a/x-pack/plugins/fleet/public/hooks/use_agent_version.test.ts b/x-pack/plugins/fleet/public/hooks/use_agent_version.test.ts index 6cb1c8ee42248..34a7afe33baaa 100644 --- a/x-pack/plugins/fleet/public/hooks/use_agent_version.test.ts +++ b/x-pack/plugins/fleet/public/hooks/use_agent_version.test.ts @@ -37,6 +37,24 @@ describe('useAgentVersion', () => { expect(result.current).toEqual(mockKibanaVersion); }); + it('should return agent version with newer patch than kibana', async () => { + const mockKibanaVersion = '8.8.1'; + const mockAvailableVersions = ['8.9.0', '8.8.2', '8.8.0', '8.7.0']; + + (useKibanaVersion as jest.Mock).mockReturnValue(mockKibanaVersion); + (sendGetAgentsAvailableVersions as jest.Mock).mockResolvedValue({ + data: { items: mockAvailableVersions }, + }); + + const { result, waitForNextUpdate } = renderHook(() => useAgentVersion()); + + expect(sendGetAgentsAvailableVersions).toHaveBeenCalled(); + + await waitForNextUpdate(); + + expect(result.current).toEqual('8.8.2'); + }); + it('should return the latest availeble agent version if a version that matches Kibana version is not released', async () => { const mockKibanaVersion = '8.11.0'; const mockAvailableVersions = ['8.8.0', '8.7.0', '8.9.2', '7.16.0']; @@ -122,4 +140,29 @@ describe('useAgentVersion', () => { expect(result.current).toEqual(mockKibanaVersion); }); + + it('should return the latest availeble agent version if has build suffix', async () => { + const mockKibanaVersion = '8.11.0'; + const mockAvailableVersions = [ + '8.12.0', + '8.11.1+build123456789', + '8.8.0', + '8.7.0', + '8.9.2', + '7.16.0', + ]; + + (useKibanaVersion as jest.Mock).mockReturnValue(mockKibanaVersion); + (sendGetAgentsAvailableVersions as jest.Mock).mockResolvedValue({ + data: { items: mockAvailableVersions }, + }); + + const { result, waitForNextUpdate } = renderHook(() => useAgentVersion()); + + expect(sendGetAgentsAvailableVersions).toHaveBeenCalled(); + + await waitForNextUpdate(); + + expect(result.current).toEqual('8.11.1+build123456789'); + }); }); diff --git a/x-pack/plugins/fleet/public/hooks/use_agent_version.ts b/x-pack/plugins/fleet/public/hooks/use_agent_version.ts index 8c198dbc7773e..85409759cd88a 100644 --- a/x-pack/plugins/fleet/public/hooks/use_agent_version.ts +++ b/x-pack/plugins/fleet/public/hooks/use_agent_version.ts @@ -25,15 +25,15 @@ export const useAgentVersion = (): string | undefined => { const availableVersions = res?.data?.items; let agentVersionToUse; + availableVersions?.sort(semverRcompare); if ( availableVersions && availableVersions.length > 0 && - availableVersions.indexOf(kibanaVersion) === -1 + availableVersions.indexOf(kibanaVersion) !== 0 ) { - availableVersions.sort(semverRcompare); agentVersionToUse = availableVersions.find((version) => { - return semverLt(version, kibanaVersion); + return semverLt(version, kibanaVersion) || differsOnlyInPatch(version, kibanaVersion); }) || availableVersions[0]; } else { agentVersionToUse = kibanaVersion; @@ -50,3 +50,16 @@ export const useAgentVersion = (): string | undefined => { return agentVersion; }; + +export const differsOnlyInPatch = ( + versionA: string, + versionB: string, + allowEqualPatch: boolean = true +): boolean => { + const [majorA, minorA, patchA] = versionA.split('.'); + const [majorB, minorB, patchB] = versionB.split('.'); + + return ( + majorA === majorB && minorA === minorB && (allowEqualPatch ? patchA >= patchB : patchA > patchB) + ); +}; diff --git a/x-pack/plugins/fleet/public/hooks/use_request/epm.ts b/x-pack/plugins/fleet/public/hooks/use_request/epm.ts index a09e975d57e16..03bf36da75763 100644 --- a/x-pack/plugins/fleet/public/hooks/use_request/epm.ts +++ b/x-pack/plugins/fleet/public/hooks/use_request/epm.ts @@ -82,15 +82,21 @@ export const useGetPackages = (query: GetPackagesRequest['query'] = {}) => { }); }; -export const useGetPackagesQuery = (query: GetPackagesRequest['query']) => { - return useQuery(['get-packages', query], () => - sendRequestForRq({ - path: epmRouteService.getListPath(), - method: 'get', - version: API_VERSIONS.public.v1, - query, - }) - ); +export const useGetPackagesQuery = ( + query: GetPackagesRequest['query'], + options?: { enabled?: boolean } +) => { + return useQuery({ + queryKey: ['get-packages', query], + queryFn: () => + sendRequestForRq({ + path: epmRouteService.getListPath(), + method: 'get', + version: API_VERSIONS.public.v1, + query, + }), + enabled: options?.enabled, + }); }; export const sendGetPackages = (query: GetPackagesRequest['query'] = {}) => { diff --git a/x-pack/plugins/fleet/public/hooks/use_request/settings.ts b/x-pack/plugins/fleet/public/hooks/use_request/settings.ts index d074644a9d26a..7cb6415df1923 100644 --- a/x-pack/plugins/fleet/public/hooks/use_request/settings.ts +++ b/x-pack/plugins/fleet/public/hooks/use_request/settings.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { useQuery } from '@tanstack/react-query'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { settingsRoutesService } from '../../services'; import type { PutSettingsResponse, PutSettingsRequest, GetSettingsResponse } from '../../types'; @@ -15,14 +15,17 @@ import { API_VERSIONS } from '../../../common/constants'; import type { RequestError } from './use_request'; import { sendRequest, sendRequestForRq, useRequest } from './use_request'; -export function useGetSettingsQuery() { - return useQuery(['settings'], () => - sendRequestForRq({ - method: 'get', - path: settingsRoutesService.getInfoPath(), - version: API_VERSIONS.public.v1, - }) - ); +export function useGetSettingsQuery(options?: { enabled?: boolean }) { + return useQuery({ + queryKey: ['settings'], + enabled: options?.enabled, + queryFn: () => + sendRequestForRq({ + method: 'get', + path: settingsRoutesService.getInfoPath(), + version: API_VERSIONS.public.v1, + }), + }); } export function useGetSettings() { @@ -41,6 +44,17 @@ export function sendGetSettings() { }); } +export function usePutSettingsMutation() { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: sendPutSettings, + onSuccess: () => { + queryClient.invalidateQueries(['settings']); + }, + }); +} + export function sendPutSettings(body: PutSettingsRequest['body']) { return sendRequest({ method: 'put', diff --git a/x-pack/plugins/fleet/public/index.ts b/x-pack/plugins/fleet/public/index.ts index 30ccf9c9c657d..1efc18a53e1e8 100644 --- a/x-pack/plugins/fleet/public/index.ts +++ b/x-pack/plugins/fleet/public/index.ts @@ -16,6 +16,7 @@ export const plugin = (initializerContext: PluginInitializerContext) => { }; export type { NewPackagePolicy, KibanaSavedObjectType } from './types'; +export { SetupTechnology } from './types'; export type { AgentDetailsReassignPolicyAction, AgentPolicyDetailsDeployAgentAction, diff --git a/x-pack/plugins/fleet/public/types/index.ts b/x-pack/plugins/fleet/public/types/index.ts index 63837dc809559..fe1c49af887d9 100644 --- a/x-pack/plugins/fleet/public/types/index.ts +++ b/x-pack/plugins/fleet/public/types/index.ts @@ -144,6 +144,7 @@ export { ElasticsearchAssetType, KibanaAssetType, InstallStatus, + SetupTechnology, } from '../../common/types'; export * from './intra_app_route_state'; diff --git a/x-pack/plugins/fleet/public/types/ui_extensions.ts b/x-pack/plugins/fleet/public/types/ui_extensions.ts index 53ae5322f0d9d..b5ae9dacf54bc 100644 --- a/x-pack/plugins/fleet/public/types/ui_extensions.ts +++ b/x-pack/plugins/fleet/public/types/ui_extensions.ts @@ -36,6 +36,8 @@ export type PackagePolicyReplaceDefineStepExtensionComponentProps = ( validationResults?: PackagePolicyValidationResults; agentPolicy?: AgentPolicy; packageInfo: PackageInfo; + agentlessPolicy?: AgentPolicy; + handleSetupTechnologyChange?: (setupTechnology: string) => void; }; /** diff --git a/x-pack/plugins/fleet/scripts/verify_test_packages/verify_test_packages.test.ts b/x-pack/plugins/fleet/scripts/verify_test_packages/verify_test_packages.test.ts index 219d101d6e1ad..b8d1ba7e9bec0 100644 --- a/x-pack/plugins/fleet/scripts/verify_test_packages/verify_test_packages.test.ts +++ b/x-pack/plugins/fleet/scripts/verify_test_packages/verify_test_packages.test.ts @@ -5,9 +5,30 @@ * 2.0. */ +import { securityMock } from '@kbn/security-plugin/server/mocks'; +import { loggerMock } from '@kbn/logging-mocks'; + +import type { Logger } from '@kbn/core/server'; + +import { appContextService } from '../../server/services/app_context'; + import { verifyAllTestPackages } from './verify_test_packages'; +jest.mock('../../server/services/app_context'); + +const mockedAppContextService = appContextService as jest.Mocked; +mockedAppContextService.getSecuritySetup.mockImplementation(() => ({ + ...securityMock.createSetup(), +})); + +let mockedLogger: jest.Mocked; + describe('Test packages', () => { + beforeEach(() => { + mockedLogger = loggerMock.create(); + mockedAppContextService.getLogger.mockReturnValue(mockedLogger); + }); + test('All test packages should be valid (node scripts/verify_test_packages) ', async () => { const { errors } = await verifyAllTestPackages(); expect(errors).toEqual([]); diff --git a/x-pack/plugins/fleet/server/collectors/agent_collectors.ts b/x-pack/plugins/fleet/server/collectors/agent_collectors.ts index 09a4986e0e6f0..391114aa992b3 100644 --- a/x-pack/plugins/fleet/server/collectors/agent_collectors.ts +++ b/x-pack/plugins/fleet/server/collectors/agent_collectors.ts @@ -75,6 +75,12 @@ export interface AgentData { version: string; count: number; }>; + upgrade_details: Array<{ + target_version: string; + state: string; + error_msg: string; + agent_count: number; + }>; } const DEFAULT_AGENT_DATA = { @@ -82,6 +88,7 @@ const DEFAULT_AGENT_DATA = { agents_per_policy: [], agents_per_version: [], agents_per_os: [], + upgrade_details: [], }; export const getAgentData = async ( @@ -135,6 +142,23 @@ export const getAgentData = async ( ], }, }, + upgrade_details: { + multi_terms: { + size: 1000, + terms: [ + { + field: 'upgrade_details.target_version.keyword', + }, + { + field: 'upgrade_details.state', + }, + { + field: 'upgrade_details.metadata.error_msg.keyword', + missing: '', + }, + ], + }, + }, }, }, { signal: abortController.signal } @@ -190,11 +214,21 @@ export const getAgentData = async ( count: bucket.doc_count, })); + const upgradeDetails = ((response?.aggregations?.upgrade_details as any).buckets ?? []).map( + (bucket: any) => ({ + target_version: bucket.key[0], + state: bucket.key[1], + error_msg: bucket.key[2], + agent_count: bucket.doc_count, + }) + ); + return { agent_checkin_status: statuses, agents_per_policy: agentsPerPolicy, agents_per_version: agentsPerVersion, agents_per_os: agentsPerOS, + upgrade_details: upgradeDetails, }; } catch (error) { if (error.statusCode === 404) { diff --git a/x-pack/plugins/fleet/server/collectors/agents_per_output.test.ts b/x-pack/plugins/fleet/server/collectors/agents_per_output.test.ts index 1202876c754a8..f68a700865708 100644 --- a/x-pack/plugins/fleet/server/collectors/agents_per_output.test.ts +++ b/x-pack/plugins/fleet/server/collectors/agents_per_output.test.ts @@ -19,11 +19,13 @@ jest.mock('../services', () => { { agents: 1, data_output_id: 'logstash1' }, { agents: 1, monitoring_output_id: 'kafka1' }, { agents: 1, data_output_id: 'elasticsearch2', monitoring_output_id: 'elasticsearch2' }, + { agents: 1, data_output_id: 'elasticsearch3', monitoring_output_id: 'elasticsearch3' }, { agents: 1, data_output_id: 'es-containerhost', monitoring_output_id: 'es-containerhost', }, + { agents: 1, data_output_id: 'remote-es', monitoring_output_id: 'remote-es' }, ], }), }, @@ -35,11 +37,14 @@ jest.mock('../services', () => { is_default: true, is_default_monitoring: true, type: 'elasticsearch', + preset: 'balanced', }, { id: 'logstash1', type: 'logstash' }, { id: 'kafka1', type: 'kafka' }, - { id: 'elasticsearch2', type: 'elasticsearch' }, - { id: 'es-containerhost', type: 'elasticsearch' }, + { id: 'elasticsearch2', type: 'elasticsearch', preset: 'custom' }, + { id: 'elasticsearch3', type: 'elasticsearch', preset: 'balanced' }, + { id: 'es-containerhost', type: 'elasticsearch', preset: 'throughput' }, + { id: 'remote-es', type: 'remote_elasticsearch', preset: 'scale' }, ], }), }, @@ -52,9 +57,32 @@ describe('agents_per_output', () => { it('should return agent count by output type', async () => { const res = await getAgentsPerOutput(soClientMock, {} as unknown as ElasticsearchClient); expect(res).toEqual([ - { output_type: 'elasticsearch', count_as_data: 4, count_as_monitoring: 4 }, + { + output_type: 'elasticsearch', + count_as_data: 5, + count_as_monitoring: 5, + preset_counts: { + custom: 1, + balanced: 2, + throughput: 1, + scale: 0, + latency: 0, + }, + }, { output_type: 'logstash', count_as_data: 1, count_as_monitoring: 0 }, { output_type: 'kafka', count_as_data: 0, count_as_monitoring: 1 }, + { + output_type: 'remote_elasticsearch', + count_as_data: 1, + count_as_monitoring: 1, + preset_counts: { + custom: 0, + balanced: 0, + throughput: 0, + scale: 1, + latency: 0, + }, + }, ]); }); }); diff --git a/x-pack/plugins/fleet/server/collectors/agents_per_output.ts b/x-pack/plugins/fleet/server/collectors/agents_per_output.ts index 3ad09bcb51177..5090b1530bc02 100644 --- a/x-pack/plugins/fleet/server/collectors/agents_per_output.ts +++ b/x-pack/plugins/fleet/server/collectors/agents_per_output.ts @@ -6,8 +6,12 @@ */ import type { ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/server'; + import _ from 'lodash'; +import { outputTypeSupportPresets } from '../../common/services/output_helpers'; +import type { AgentPolicy } from '../../common/types'; + import { SO_SEARCH_LIMIT } from '../../common'; import { agentPolicyService, outputService } from '../services'; @@ -15,6 +19,13 @@ export interface AgentsPerOutputType { output_type: string; count_as_data: number; count_as_monitoring: number; + preset_counts?: { + balanced: number; + custom: number; + latency: number; + scale: number; + throughput: number; + }; } export async function getAgentsPerOutput( @@ -28,38 +39,71 @@ export async function getAgentsPerOutput( outputs.find((output) => output.is_default_monitoring)?.id || ''; const outputsById = _.keyBy(outputs, 'id'); - const getOutputTypeById = (outputId: string): string => outputsById[outputId]?.type ?? ''; + const getDataOutputForAgentPolicy = (agentPolicy: AgentPolicy) => + outputsById[agentPolicy.data_output_id || defaultOutputId]; + const getMonitoringOutputForAgentPolicy = (agentPolicy: AgentPolicy) => + outputsById[agentPolicy.monitoring_output_id || defaultMonitoringOutputId]; - const { items } = await agentPolicyService.list(soClient, { + const { items: agentPolicies } = await agentPolicyService.list(soClient, { esClient, withAgentCount: true, page: 1, perPage: SO_SEARCH_LIMIT, }); + const outputTypes: { [key: string]: AgentsPerOutputType } = {}; - items - .filter((item) => (item.agents ?? 0) > 0) - .forEach((item) => { - const dataOutputType = getOutputTypeById(item.data_output_id || defaultOutputId); - if (!outputTypes[dataOutputType]) { - outputTypes[dataOutputType] = { - output_type: dataOutputType, + + agentPolicies + .filter((agentPolicy) => (agentPolicy.agents ?? 0) > 0) + .forEach((agentPolicy) => { + const dataOutput = getDataOutputForAgentPolicy(agentPolicy); + const monitoringOutput = getMonitoringOutputForAgentPolicy(agentPolicy); + + if (!outputTypes[dataOutput.type]) { + outputTypes[dataOutput.type] = { + output_type: dataOutput.type, count_as_data: 0, count_as_monitoring: 0, }; } - outputTypes[dataOutputType].count_as_data += item.agents ?? 0; - const monitoringOutputType = getOutputTypeById( - item.monitoring_output_id || defaultMonitoringOutputId - ); - if (!outputTypes[monitoringOutputType]) { - outputTypes[monitoringOutputType] = { - output_type: monitoringOutputType, + + outputTypes[dataOutput.type].count_as_data += agentPolicy.agents ?? 0; + + if (!outputTypes[monitoringOutput.type]) { + outputTypes[monitoringOutput.type] = { + output_type: monitoringOutput.type, count_as_data: 0, count_as_monitoring: 0, }; } - outputTypes[monitoringOutputType].count_as_monitoring += item.agents ?? 0; + outputTypes[monitoringOutput.type].count_as_monitoring += agentPolicy.agents ?? 0; }); + + outputs.forEach((output) => { + if (!outputTypeSupportPresets(output.type)) { + return; + } + if (!outputTypes[output.type]) { + return; + } + const outputTelemetryRecord = outputTypes[output.type]; + + if (!outputTelemetryRecord.preset_counts) { + outputTelemetryRecord.preset_counts = { + balanced: 0, + custom: 0, + latency: 0, + scale: 0, + throughput: 0, + }; + } + + if (output.preset && output.preset in outputTelemetryRecord.preset_counts) { + outputTelemetryRecord.preset_counts[ + output.preset as keyof typeof outputTelemetryRecord.preset_counts + ] += 1; + } + }); + return Object.values(outputTypes); } diff --git a/x-pack/plugins/fleet/server/config.ts b/x-pack/plugins/fleet/server/config.ts index 87212ab025c25..0a817e0ccbfed 100644 --- a/x-pack/plugins/fleet/server/config.ts +++ b/x-pack/plugins/fleet/server/config.ts @@ -157,6 +157,9 @@ export const config: PluginConfigDescriptor = { disableRegistryVersionCheck: schema.boolean({ defaultValue: false }), allowAgentUpgradeSourceUri: schema.boolean({ defaultValue: false }), bundledPackageLocation: schema.string({ defaultValue: DEFAULT_BUNDLED_PACKAGE_LOCATION }), + disableBundledPackagesCache: schema.boolean({ + defaultValue: false, + }), }), packageVerification: schema.object({ gpgKeyPath: schema.string({ defaultValue: DEFAULT_GPG_KEY_PATH }), diff --git a/x-pack/plugins/fleet/server/errors/handlers.ts b/x-pack/plugins/fleet/server/errors/handlers.ts index 7bd380af258e2..c85bfeced9db1 100644 --- a/x-pack/plugins/fleet/server/errors/handlers.ts +++ b/x-pack/plugins/fleet/server/errors/handlers.ts @@ -20,18 +20,14 @@ import { UninstallTokenError } from '../../common/errors'; import { appContextService } from '../services'; import { - AgentNotFoundError, - AgentActionNotFoundError, AgentPolicyNameExistsError, ConcurrentInstallOperationError, FleetError, - PackageNotFoundError, PackageUnsupportedMediaTypeError, RegistryConnectionError, RegistryError, RegistryResponseError, PackageFailedVerificationError, - PackagePolicyNotFoundError, FleetUnauthorizedError, PackagePolicyNameExistsError, PackageOutdatedError, @@ -41,6 +37,11 @@ import { PackageESError, KibanaSOReferenceError, PackageAlreadyInstalledError, + AgentPolicyInvalidError, + EnrollmentKeyNameExistsError, + AgentRequestInvalidError, + PackagePolicyRequestError, + FleetNotFoundError, } from '.'; type IngestErrorHandler = ( @@ -71,24 +72,31 @@ const getHTTPResponseCode = (error: FleetError): number => { if (error instanceof KibanaSOReferenceError) { return 400; } + if (error instanceof AgentPolicyInvalidError) { + return 400; + } + if (error instanceof AgentRequestInvalidError) { + return 400; + } + if (error instanceof PackagePolicyRequestError) { + return 400; + } // Unauthorized if (error instanceof FleetUnauthorizedError) { return 403; } // Not Found - if (error instanceof PackageNotFoundError || error instanceof PackagePolicyNotFoundError) { - return 404; - } - if (error instanceof AgentNotFoundError) { - return 404; - } - if (error instanceof AgentActionNotFoundError) { + if (error instanceof FleetNotFoundError) { return 404; } + // Conflict if (error instanceof AgentPolicyNameExistsError) { return 409; } + if (error instanceof EnrollmentKeyNameExistsError) { + return 409; + } if (error instanceof ConcurrentInstallOperationError) { return 409; } @@ -121,6 +129,7 @@ const getHTTPResponseCode = (error: FleetError): number => { // Connection errors (ie. RegistryConnectionError) / fallback (RegistryError) from EPR return 502; } + return 400; // Bad Request }; diff --git a/x-pack/plugins/fleet/server/errors/index.ts b/x-pack/plugins/fleet/server/errors/index.ts index 7f607f4692774..ce7245672e623 100644 --- a/x-pack/plugins/fleet/server/errors/index.ts +++ b/x-pack/plugins/fleet/server/errors/index.ts @@ -28,7 +28,7 @@ export class RegistryResponseError extends RegistryError { } // Package errors -export class PackageNotFoundError extends FleetError {} + export class PackageOutdatedError extends FleetError {} export class PackageFailedVerificationError extends FleetError { constructor(pkgName: string, pkgVersion: string) { @@ -43,20 +43,24 @@ export class PackageInvalidArchiveError extends FleetError {} export class PackageRemovalError extends FleetError {} export class PackageESError extends FleetError {} export class ConcurrentInstallOperationError extends FleetError {} -export class BundledPackageLocationNotFoundError extends FleetError {} + export class KibanaSOReferenceError extends FleetError {} export class PackageAlreadyInstalledError extends FleetError {} export class AgentPolicyError extends FleetError {} -export class AgentPolicyNotFoundError extends FleetError {} -export class AgentNotFoundError extends FleetError {} -export class AgentActionNotFoundError extends FleetError {} +export class AgentRequestInvalidError extends FleetError {} +export class AgentPolicyInvalidError extends FleetError {} + export class AgentPolicyNameExistsError extends AgentPolicyError {} export class AgentReassignmentError extends FleetError {} export class PackagePolicyIneligibleForUpgradeError extends FleetError {} export class PackagePolicyValidationError extends FleetError {} export class PackagePolicyNameExistsError extends FleetError {} -export class PackagePolicyNotFoundError extends FleetError {} +export class BundledPackageLocationNotFoundError extends FleetError {} + +export class PackagePolicyRequestError extends FleetError {} + +export class EnrollmentKeyNameExistsError extends FleetError {} export class HostedAgentPolicyRestrictionRelatedError extends FleetError { constructor(message = 'Cannot perform that action') { super( @@ -75,12 +79,27 @@ export class FleetEncryptedSavedObjectEncryptionKeyRequired extends FleetError { export class FleetSetupError extends FleetError {} export class GenerateServiceTokenError extends FleetError {} export class FleetUnauthorizedError extends FleetError {} +export class FleetNotFoundError extends FleetError {} export class OutputUnauthorizedError extends FleetError {} export class OutputInvalidError extends FleetError {} export class OutputLicenceError extends FleetError {} export class DownloadSourceError extends FleetError {} +// Not found errors +export class AgentNotFoundError extends FleetNotFoundError {} +export class AgentPolicyNotFoundError extends FleetNotFoundError {} +export class AgentActionNotFoundError extends FleetNotFoundError {} +export class DownloadSourceNotFound extends FleetNotFoundError {} +export class EnrollmentKeyNotFoundError extends FleetNotFoundError {} +export class FleetServerHostNotFoundError extends FleetNotFoundError {} +export class SigningServiceNotFoundError extends FleetNotFoundError {} +export class InputNotFoundError extends FleetNotFoundError {} +export class OutputNotFoundError extends FleetNotFoundError {} +export class PackageNotFoundError extends FleetNotFoundError {} +export class PackagePolicyNotFoundError extends FleetNotFoundError {} +export class StreamNotFoundError extends FleetNotFoundError {} + export class FleetServerHostUnauthorizedError extends FleetUnauthorizedError {} export class FleetProxyUnauthorizedError extends FleetUnauthorizedError {} diff --git a/x-pack/plugins/fleet/server/integration_tests/cloud_preconfiguration.test.ts b/x-pack/plugins/fleet/server/integration_tests/cloud_preconfiguration.test.ts index c080aefacd12e..72335f2c94f31 100644 --- a/x-pack/plugins/fleet/server/integration_tests/cloud_preconfiguration.test.ts +++ b/x-pack/plugins/fleet/server/integration_tests/cloud_preconfiguration.test.ts @@ -333,6 +333,7 @@ describe('Fleet cloud preconfiguration', () => { 'es-containerhost': { hosts: ['https://cloudinternales:9200'], type: 'elasticsearch', + preset: 'balanced', }, }, revision: 5, diff --git a/x-pack/plugins/fleet/server/integration_tests/fleet_usage_telemetry.test.ts b/x-pack/plugins/fleet/server/integration_tests/fleet_usage_telemetry.test.ts index e2e7e9f7887e6..430dc6f745ad9 100644 --- a/x-pack/plugins/fleet/server/integration_tests/fleet_usage_telemetry.test.ts +++ b/x-pack/plugins/fleet/server/integration_tests/fleet_usage_telemetry.test.ts @@ -146,6 +146,13 @@ describe('fleet usage telemetry', () => { status: 'HEALTHY', }, ], + upgrade_details: { + target_version: '8.12.0', + state: 'UPG_FAILED', + metadata: { + error_msg: 'Download failed', + }, + }, }, { create: { @@ -176,6 +183,13 @@ describe('fleet usage telemetry', () => { status: 'HEALTHY', }, ], + upgrade_details: { + target_version: '8.12.0', + state: 'UPG_FAILED', + metadata: { + error_msg: 'Agent crash detected', + }, + }, }, { create: { @@ -220,6 +234,25 @@ describe('fleet usage telemetry', () => { last_checkin: new Date(Date.now() - 1000 * 60 * 6).toISOString(), active: true, policy_id: 'policy2', + upgrade_details: { + target_version: '8.11.0', + state: 'UPG_ROLLBACK', + metadata: {}, + }, + }, + { + create: { + _id: 'agent4', + }, + }, + { + agent: { + version: '8.6.0', + }, + last_checkin_status: 'online', + last_checkin: new Date(Date.now() - 1000 * 60 * 6).toISOString(), + active: true, + policy_id: 'policy3', }, ], refresh: 'wait_for', @@ -361,6 +394,21 @@ describe('fleet usage telemetry', () => { }, { id: 'output3' } ); + await soClient.create( + 'ingest-outputs', + { + name: 'output4', + type: 'elasticsearch', + hosts: ['http://localhost:9200'], + is_default: false, + is_default_monitoring: false, + config_yaml: '', + ca_trusted_fingerprint: '', + proxy_id: null, + preset: 'balanced', + }, + { id: 'output4' } + ); await soClient.create( 'ingest-agent-policies', @@ -380,6 +428,24 @@ describe('fleet usage telemetry', () => { }, { id: 'policy2' } ); + await soClient.create( + 'ingest-agent-policies', + { + namespace: 'default', + monitoring_enabled: ['logs', 'metrics'], + name: 'Yet another policy', + description: 'Policy 3', + inactivity_timeout: 1209600, + status: 'active', + is_managed: false, + revision: 2, + updated_by: 'system', + schema_version: '1.0.0', + data_output_id: 'output4', + monitoring_output_id: 'output4', + }, + { id: 'policy3' } + ); }); afterAll(async () => { @@ -397,13 +463,13 @@ describe('fleet usage telemetry', () => { expect.objectContaining({ agents_enabled: true, agents: { - total_enrolled: 3, + total_enrolled: 4, healthy: 0, unhealthy: 0, inactive: 0, unenrolled: 1, - offline: 3, - total_all_statuses: 4, + offline: 4, + total_all_statuses: 5, updating: 0, }, fleet_server: { @@ -419,10 +485,10 @@ describe('fleet usage telemetry', () => { agents_per_version: [ { version: '8.6.0', - count: 2, + count: 3, healthy: 0, inactive: 0, - offline: 2, + offline: 3, unenrolled: 0, unhealthy: 0, updating: 0, @@ -439,7 +505,7 @@ describe('fleet usage telemetry', () => { }, ], agent_checkin_status: { error: 1, degraded: 1 }, - agents_per_policy: [2, 1], + agents_per_policy: [2, 1, 1], agents_per_os: [ { name: 'Ubuntu', @@ -463,6 +529,18 @@ describe('fleet usage telemetry', () => { count_as_monitoring: 1, output_type: 'logstash', }, + { + count_as_data: 1, + count_as_monitoring: 1, + output_type: 'elasticsearch', + preset_counts: { + balanced: 2, + custom: 0, + latency: 0, + scale: 0, + throughput: 0, + }, + }, ], fleet_server_config: { policies: [ @@ -477,7 +555,7 @@ describe('fleet usage telemetry', () => { ], }, agent_policies: { - count: 2, + count: 3, output_types: expect.arrayContaining(['elasticsearch', 'logstash', 'third_type']), }, agent_logs_panics_last_hour: [ @@ -498,5 +576,24 @@ describe('fleet usage telemetry', () => { fleet_server_logs_top_errors: ['failed to unenroll offline agents'], }) ); + expect(usage?.upgrade_details.length).toBe(3); + expect(usage?.upgrade_details).toContainEqual({ + target_version: '8.12.0', + state: 'UPG_FAILED', + error_msg: 'Download failed', + agent_count: 1, + }); + expect(usage?.upgrade_details).toContainEqual({ + target_version: '8.12.0', + state: 'UPG_FAILED', + error_msg: 'Agent crash detected', + agent_count: 1, + }); + expect(usage?.upgrade_details).toContainEqual({ + target_version: '8.11.0', + state: 'UPG_ROLLBACK', + error_msg: '', + agent_count: 1, + }); }); }); diff --git a/x-pack/plugins/fleet/server/integration_tests/helpers/index.ts b/x-pack/plugins/fleet/server/integration_tests/helpers/index.ts index 23cdc80b8d65d..961f3c90fb549 100644 --- a/x-pack/plugins/fleet/server/integration_tests/helpers/index.ts +++ b/x-pack/plugins/fleet/server/integration_tests/helpers/index.ts @@ -8,6 +8,8 @@ import { adminTestUser } from '@kbn/test'; import { getSupertest, type createRoot, type HttpMethod } from '@kbn/core-test-helpers-kbn-server'; +import { FleetError } from '../../errors'; + type Root = ReturnType; export * from './docker_registry_helper'; @@ -18,7 +20,7 @@ export const waitForFleetSetup = async (root: Root) => { const resp = await statusApi.send(); const fleetStatus = resp.body?.status?.plugins?.fleet; if (fleetStatus?.meta?.error) { - throw new Error(`Setup failed: ${JSON.stringify(fleetStatus)}`); + throw new FleetError(`Setup failed: ${JSON.stringify(fleetStatus)}`); } return !fleetStatus || fleetStatus?.summary === 'Fleet is setting up'; diff --git a/x-pack/plugins/fleet/server/routes/agent/source_uri_utils.ts b/x-pack/plugins/fleet/server/routes/agent/source_uri_utils.ts index 3f571fdcb09cc..efedf164f6531 100644 --- a/x-pack/plugins/fleet/server/routes/agent/source_uri_utils.ts +++ b/x-pack/plugins/fleet/server/routes/agent/source_uri_utils.ts @@ -9,6 +9,7 @@ import type { SavedObjectsClientContract } from '@kbn/core/server'; import { downloadSourceService } from '../../services'; import type { AgentPolicy } from '../../types'; +import { FleetError, DownloadSourceNotFound } from '../../errors'; export const getSourceUriForAgentPolicy = async ( soClient: SavedObjectsClientContract, @@ -17,12 +18,12 @@ export const getSourceUriForAgentPolicy = async ( const defaultDownloadSourceId = await downloadSourceService.getDefaultDownloadSourceId(soClient); if (!defaultDownloadSourceId) { - throw new Error('Default download source host is not setup'); + throw new FleetError('Default download source host is not setup'); } const downloadSourceId: string = agentPolicy.download_source_id || defaultDownloadSourceId; const downloadSource = await downloadSourceService.get(soClient, downloadSourceId); if (!downloadSource) { - throw new Error(`Download source host not found ${downloadSourceId}`); + throw new DownloadSourceNotFound(`Download source host not found ${downloadSourceId}`); } return { host: downloadSource.host, proxy_id: downloadSource.proxy_id }; }; diff --git a/x-pack/plugins/fleet/server/routes/agent/upgrade_handler.test.ts b/x-pack/plugins/fleet/server/routes/agent/upgrade_handler.test.ts index aefcbfc5cd87f..62f34559c79ee 100644 --- a/x-pack/plugins/fleet/server/routes/agent/upgrade_handler.test.ts +++ b/x-pack/plugins/fleet/server/routes/agent/upgrade_handler.test.ts @@ -15,7 +15,7 @@ describe('upgrade handler', () => { it('should throw if upgrade version is higher than kibana version', () => { expect(() => checkKibanaVersion('8.5.0', '8.4.0')).toThrowError( - 'cannot upgrade agent to 8.5.0 because it is higher than the installed kibana version 8.4.0' + 'Cannot upgrade agent to 8.5.0 because it is higher than the installed kibana version 8.4.0' ); }); diff --git a/x-pack/plugins/fleet/server/routes/agent/upgrade_handler.ts b/x-pack/plugins/fleet/server/routes/agent/upgrade_handler.ts index 547fda566a95f..3b1cd89ff1364 100644 --- a/x-pack/plugins/fleet/server/routes/agent/upgrade_handler.ts +++ b/x-pack/plugins/fleet/server/routes/agent/upgrade_handler.ts @@ -19,12 +19,13 @@ import type { PostAgentUpgradeResponse } from '../../../common/types'; import type { PostAgentUpgradeRequestSchema, PostBulkAgentUpgradeRequestSchema } from '../../types'; import * as AgentService from '../../services/agents'; import { appContextService } from '../../services'; -import { defaultFleetErrorHandler } from '../../errors'; +import { defaultFleetErrorHandler, AgentRequestInvalidError } from '../../errors'; import { getRecentUpgradeInfoForAgent, isAgentUpgradeable, AGENT_UPGRADE_COOLDOWN_IN_MIN, isAgentUpgrading, + getNotUpgradeableMessage, } from '../../../common/services'; import { getMaxVersion } from '../../../common/services/get_min_max_version'; import { getAgentById } from '../../services/agents'; @@ -114,7 +115,11 @@ export const postAgentUpgradeHandler: RequestHandler< return response.customError({ statusCode: 400, body: { - message: `agent ${request.params.agentId} is not upgradeable`, + message: `Agent ${request.params.agentId} is not upgradeable: ${getNotUpgradeableMessage( + agent, + latestAgentVersion, + version + )}`, }, }); } @@ -187,14 +192,15 @@ export const postBulkAgentsUpgradeHandler: RequestHandler< export const checkKibanaVersion = (version: string, kibanaVersion: string, force = false) => { // get version number only in case "-SNAPSHOT" is in it const kibanaVersionNumber = semverCoerce(kibanaVersion)?.version; - if (!kibanaVersionNumber) throw new Error(`kibanaVersion ${kibanaVersionNumber} is not valid`); + if (!kibanaVersionNumber) + throw new AgentRequestInvalidError(`KibanaVersion ${kibanaVersionNumber} is not valid`); const versionToUpgradeNumber = semverCoerce(version)?.version; if (!versionToUpgradeNumber) - throw new Error(`version to upgrade ${versionToUpgradeNumber} is not valid`); + throw new AgentRequestInvalidError(`Version to upgrade ${versionToUpgradeNumber} is not valid`); if (!force && semverGt(versionToUpgradeNumber, kibanaVersionNumber)) { - throw new Error( - `cannot upgrade agent to ${versionToUpgradeNumber} because it is higher than the installed kibana version ${kibanaVersionNumber}` + throw new AgentRequestInvalidError( + `Cannot upgrade agent to ${versionToUpgradeNumber} because it is higher than the installed kibana version ${kibanaVersionNumber}` ); } @@ -205,8 +211,8 @@ export const checkKibanaVersion = (version: string, kibanaVersion: string, force // When force is enabled, only the major and minor versions are checked if (force && !(kibanaMajorGt || kibanaMajorEqMinorGte)) { - throw new Error( - `cannot force upgrade agent to ${versionToUpgradeNumber} because it does not satisfy the major and minor of the installed kibana version ${kibanaVersionNumber}` + throw new AgentRequestInvalidError( + `Cannot force upgrade agent to ${versionToUpgradeNumber} because it does not satisfy the major and minor of the installed kibana version ${kibanaVersionNumber}` ); } }; @@ -228,8 +234,8 @@ const checkFleetServerVersion = ( } if (!force && semverGt(versionToUpgradeNumber, maxFleetServerVersion)) { - throw new Error( - `cannot upgrade agent to ${versionToUpgradeNumber} because it is higher than the latest fleet server version ${maxFleetServerVersion}` + throw new AgentRequestInvalidError( + `Cannot upgrade agent to ${versionToUpgradeNumber} because it is higher than the latest fleet server version ${maxFleetServerVersion}` ); } @@ -241,8 +247,8 @@ const checkFleetServerVersion = ( // When force is enabled, only the major and minor versions are checked if (force && !(fleetServerMajorGt || fleetServerMajorEqMinorGte)) { - throw new Error( - `cannot force upgrade agent to ${versionToUpgradeNumber} because it does not satisfy the major and minor of the latest fleet server version ${maxFleetServerVersion}` + throw new AgentRequestInvalidError( + `Cannot force upgrade agent to ${versionToUpgradeNumber} because it does not satisfy the major and minor of the latest fleet server version ${maxFleetServerVersion}` ); } }; diff --git a/x-pack/plugins/fleet/server/routes/epm/file_handler.test.ts b/x-pack/plugins/fleet/server/routes/epm/file_handler.test.ts new file mode 100644 index 0000000000000..56d6d8c127bc2 --- /dev/null +++ b/x-pack/plugins/fleet/server/routes/epm/file_handler.test.ts @@ -0,0 +1,245 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; +import { httpServerMock } from '@kbn/core-http-server-mocks'; +import { elasticsearchServiceMock } from '@kbn/core-elasticsearch-server-mocks'; +import { savedObjectsClientMock } from '@kbn/core-saved-objects-api-server-mocks'; +import { Headers } from 'node-fetch'; + +import { getBundledPackageByPkgKey } from '../../services/epm/packages/bundled_packages'; +import { getFile, getInstallation } from '../../services/epm/packages/get'; +import type { FleetRequestHandlerContext } from '../..'; +import { appContextService } from '../../services'; +import { unpackBufferEntries, getArchiveEntry } from '../../services/epm/archive'; +import { getAsset } from '../../services/epm/archive/storage'; + +import { getFileHandler } from './file_handler'; + +jest.mock('../../services/app_context'); +jest.mock('../../services/epm/archive'); +jest.mock('../../services/epm/archive/storage'); +jest.mock('../../services/epm/packages/bundled_packages'); +jest.mock('../../services/epm/packages/get'); + +const mockedGetBundledPackageByPkgKey = jest.mocked(getBundledPackageByPkgKey); +const mockedGetInstallation = jest.mocked(getInstallation); +const mockedGetFile = jest.mocked(getFile); +const mockedGetArchiveEntry = jest.mocked(getArchiveEntry); +const mockedUnpackBufferEntries = jest.mocked(unpackBufferEntries); +const mockedGetAsset = jest.mocked(getAsset); + +function mockContext() { + const mockSavedObjectsClient = savedObjectsClientMock.create(); + const mockElasticsearchClient = elasticsearchServiceMock.createClusterClient().asInternalUser; + return { + fleet: { + internalSOClient: async () => mockSavedObjectsClient, + }, + core: { + savedObjects: { + client: mockSavedObjectsClient, + }, + elasticsearch: { + client: { + asInternalUser: mockElasticsearchClient, + }, + }, + }, + } as unknown as FleetRequestHandlerContext; +} + +describe('getFileHandler', () => { + beforeEach(() => { + const logger = loggingSystemMock.createLogger(); + jest.mocked(appContextService).getLogger.mockReturnValue(logger); + mockedGetBundledPackageByPkgKey.mockReset(); + mockedUnpackBufferEntries.mockReset(); + mockedGetFile.mockReset(); + mockedGetInstallation.mockReset(); + mockedGetArchiveEntry.mockReset(); + mockedGetAsset.mockReset(); + }); + + it('should return the file for bundled package and an existing file', async () => { + mockedGetBundledPackageByPkgKey.mockResolvedValue({ + getBuffer: () => Promise.resolve(), + } as any); + const request = httpServerMock.createKibanaRequest({ + params: { + pkgName: 'test', + pkgVersion: '1.0.0', + filePath: 'README.md', + }, + }); + const buffer = Buffer.from(`TEST`); + mockedUnpackBufferEntries.mockResolvedValue([ + { + path: 'test-1.0.0/README.md', + buffer, + }, + ]); + const response = httpServerMock.createResponseFactory(); + const context = mockContext(); + await getFileHandler(context, request, response); + + expect(response.custom).toBeCalledWith( + expect.objectContaining({ + statusCode: 200, + body: buffer, + headers: expect.objectContaining({ + 'content-type': 'text/markdown; charset=utf-8', + }), + }) + ); + }); + + it('should a 404 for bundled package with a non existing file', async () => { + mockedGetBundledPackageByPkgKey.mockResolvedValue({ + getBuffer: () => Promise.resolve(), + } as any); + const request = httpServerMock.createKibanaRequest({ + params: { + pkgName: 'test', + pkgVersion: '1.0.0', + filePath: 'idonotexists.md', + }, + }); + mockedUnpackBufferEntries.mockResolvedValue([ + { + path: 'test-1.0.0/README.md', + buffer: Buffer.from(`TEST`), + }, + ]); + const response = httpServerMock.createResponseFactory(); + const context = mockContext(); + await getFileHandler(context, request, response); + + expect(response.custom).toBeCalledWith( + expect.objectContaining({ + statusCode: 404, + body: 'bundled package file not found: idonotexists.md', + }) + ); + }); + + it('should proxy registry 200 for non bundled and non installed package', async () => { + const request = httpServerMock.createKibanaRequest({ + params: { + pkgName: 'test', + pkgVersion: '1.0.0', + filePath: 'idonotexists.md', + }, + }); + const response = httpServerMock.createResponseFactory(); + const context = mockContext(); + + mockedGetFile.mockResolvedValue({ + status: 200, + // @ts-expect-error + body: 'test', + headers: new Headers({ + raw: '', + 'content-type': 'text/markdown', + }), + }); + + await getFileHandler(context, request, response); + + expect(response.custom).toBeCalledWith( + expect.objectContaining({ + statusCode: 200, + body: 'test', + headers: expect.objectContaining({ + 'content-type': 'text/markdown', + }), + }) + ); + }); + + it('should proxy registry 404 for non bundled and non installed package', async () => { + const request = httpServerMock.createKibanaRequest({ + params: { + pkgName: 'test', + pkgVersion: '1.0.0', + filePath: 'idonotexists.md', + }, + }); + const response = httpServerMock.createResponseFactory(); + const context = mockContext(); + + mockedGetFile.mockResolvedValue({ + status: 404, + // @ts-expect-error + body: 'not found', + headers: new Headers({ + raw: '', + 'content-type': 'text', + }), + }); + + await getFileHandler(context, request, response); + + expect(response.custom).toBeCalledWith( + expect.objectContaining({ + statusCode: 404, + body: 'not found', + headers: expect.objectContaining({ + 'content-type': 'text', + }), + }) + ); + }); + + it('should return the file from installation for installed package', async () => { + const request = httpServerMock.createKibanaRequest({ + params: { + pkgName: 'test', + pkgVersion: '1.0.0', + filePath: 'README.md', + }, + }); + const response = httpServerMock.createResponseFactory(); + const context = mockContext(); + + mockedGetInstallation.mockResolvedValue({ version: '1.0.0' } as any); + mockedGetArchiveEntry.mockReturnValue(Buffer.from('test')); + + await getFileHandler(context, request, response); + + expect(response.custom).toBeCalledWith( + expect.objectContaining({ + statusCode: 200, + headers: expect.objectContaining({ + 'content-type': 'text/markdown; charset=utf-8', + }), + }) + ); + }); + + it('should a 404 if the file from installation do not exists for installed package', async () => { + const request = httpServerMock.createKibanaRequest({ + params: { + pkgName: 'test', + pkgVersion: '1.0.0', + filePath: 'README.md', + }, + }); + const response = httpServerMock.createResponseFactory(); + const context = mockContext(); + + mockedGetInstallation.mockResolvedValue({ version: '1.0.0' } as any); + await getFileHandler(context, request, response); + + expect(response.custom).toBeCalledWith( + expect.objectContaining({ + statusCode: 404, + body: 'installed package file not found: README.md', + }) + ); + }); +}); diff --git a/x-pack/plugins/fleet/server/routes/epm/file_handler.ts b/x-pack/plugins/fleet/server/routes/epm/file_handler.ts new file mode 100644 index 0000000000000..4b6b74628aa4e --- /dev/null +++ b/x-pack/plugins/fleet/server/routes/epm/file_handler.ts @@ -0,0 +1,141 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import path from 'path'; + +import type { TypeOf } from '@kbn/config-schema'; +import mime from 'mime-types'; +import type { ResponseHeaders, KnownHeaders, HttpResponseOptions } from '@kbn/core/server'; + +import type { GetFileRequestSchema, FleetRequestHandler } from '../../types'; +import { getFile, getInstallation } from '../../services/epm/packages'; +import { defaultFleetErrorHandler } from '../../errors'; +import { getArchiveEntry } from '../../services/epm/archive'; +import { getAsset } from '../../services/epm/archive/storage'; +import { getBundledPackageByPkgKey } from '../../services/epm/packages/bundled_packages'; +import { pkgToPkgKey } from '../../services/epm/registry'; +import { unpackBufferEntries } from '../../services/epm/archive'; + +const CACHE_CONTROL_10_MINUTES_HEADER: HttpResponseOptions['headers'] = { + 'cache-control': 'max-age=600', +}; +export const getFileHandler: FleetRequestHandler< + TypeOf +> = async (context, request, response) => { + try { + const { pkgName, pkgVersion, filePath } = request.params; + const savedObjectsClient = (await context.fleet).internalSoClient; + + const installation = await getInstallation({ savedObjectsClient, pkgName }); + const useLocalFile = pkgVersion === installation?.version; + const assetPath = `${pkgName}-${pkgVersion}/${filePath}`; + + if (useLocalFile) { + const fileBuffer = getArchiveEntry(assetPath); + // only pull local installation if we don't have it cached + const storedAsset = !fileBuffer && (await getAsset({ savedObjectsClient, path: assetPath })); + + // error, if neither is available + if (!fileBuffer && !storedAsset) { + return response.custom({ + body: `installed package file not found: ${filePath}`, + statusCode: 404, + }); + } + + // if storedAsset is not available, fileBuffer *must* be + // b/c we error if we don't have at least one, and storedAsset is the least likely + const { buffer, contentType } = storedAsset + ? { + contentType: storedAsset.media_type, + buffer: storedAsset.data_utf8 + ? Buffer.from(storedAsset.data_utf8, 'utf8') + : Buffer.from(storedAsset.data_base64, 'base64'), + } + : { + contentType: mime.contentType(path.extname(assetPath)), + buffer: fileBuffer, + }; + + if (!contentType) { + return response.custom({ + body: `unknown content type for file: ${filePath}`, + statusCode: 400, + }); + } + + return response.custom({ + body: buffer, + statusCode: 200, + headers: { + ...CACHE_CONTROL_10_MINUTES_HEADER, + 'content-type': contentType, + }, + }); + } + + const bundledPackage = await getBundledPackageByPkgKey( + pkgToPkgKey({ name: pkgName, version: pkgVersion }) + ); + if (bundledPackage) { + const bufferEntries = await unpackBufferEntries( + await bundledPackage.getBuffer(), + 'application/zip' + ); + + const fileBuffer = bufferEntries.find((entry) => entry.path === assetPath)?.buffer; + + if (!fileBuffer) { + return response.custom({ + body: `bundled package file not found: ${filePath}`, + statusCode: 404, + }); + } + + // if storedAsset is not available, fileBuffer *must* be + // b/c we error if we don't have at least one, and storedAsset is the least likely + const { buffer, contentType } = { + contentType: mime.contentType(path.extname(assetPath)), + buffer: fileBuffer, + }; + + if (!contentType) { + return response.custom({ + body: `unknown content type for file: ${filePath}`, + statusCode: 400, + }); + } + + return response.custom({ + body: buffer, + statusCode: 200, + headers: { + ...CACHE_CONTROL_10_MINUTES_HEADER, + 'content-type': contentType, + }, + }); + } else { + const registryResponse = await getFile(pkgName, pkgVersion, filePath); + const headersToProxy: KnownHeaders[] = ['content-type']; + const proxiedHeaders = headersToProxy.reduce((headers, knownHeader) => { + const value = registryResponse.headers.get(knownHeader); + if (value !== null) { + headers[knownHeader] = value; + } + return headers; + }, {} as ResponseHeaders); + + return response.custom({ + body: registryResponse.body, + statusCode: registryResponse.status, + headers: { ...CACHE_CONTROL_10_MINUTES_HEADER, ...proxiedHeaders }, + }); + } + } catch (error) { + return defaultFleetErrorHandler({ error, response }); + } +}; diff --git a/x-pack/plugins/fleet/server/routes/epm/handlers.ts b/x-pack/plugins/fleet/server/routes/epm/handlers.ts index c03272119dd16..6fadeff5180c2 100644 --- a/x-pack/plugins/fleet/server/routes/epm/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/epm/handlers.ts @@ -5,12 +5,9 @@ * 2.0. */ -import path from 'path'; - import type { TypeOf } from '@kbn/config-schema'; -import mime from 'mime-types'; import semverValid from 'semver/functions/valid'; -import type { ResponseHeaders, KnownHeaders, HttpResponseOptions } from '@kbn/core/server'; +import type { HttpResponseOptions } from '@kbn/core/server'; import { pick } from 'lodash'; @@ -41,7 +38,6 @@ import type { GetPackagesRequestSchema, GetInstalledPackagesRequestSchema, GetDataStreamsRequestSchema, - GetFileRequestSchema, GetInfoRequestSchema, InstallPackageFromRegistryRequestSchema, InstallPackageByUploadRequestSchema, @@ -60,21 +56,17 @@ import { getCategories, getPackages, getInstalledPackages, - getFile, getPackageInfo, isBulkInstallError, installPackage, removeInstallation, getLimitedPackages, - getInstallation, getBulkAssets, getTemplateInputs, } from '../../services/epm/packages'; import type { BulkInstallResponse } from '../../services/epm/packages'; import { defaultFleetErrorHandler, fleetErrorToResponseOptions, FleetError } from '../../errors'; import { appContextService, checkAllowedPackages } from '../../services'; -import { getArchiveEntry } from '../../services/epm/archive/cache'; -import { getAsset } from '../../services/epm/archive/storage'; import { getPackageUsageStats } from '../../services/epm/packages/get'; import { updatePackage } from '../../services/epm/packages/update'; import { getGpgKeyIdOrUndefined } from '../../services/epm/packages/package_verification'; @@ -206,80 +198,6 @@ export const getLimitedListHandler: FleetRequestHandler< } }; -export const getFileHandler: FleetRequestHandler< - TypeOf -> = async (context, request, response) => { - try { - const { pkgName, pkgVersion, filePath } = request.params; - const savedObjectsClient = (await context.fleet).internalSoClient; - const installation = await getInstallation({ savedObjectsClient, pkgName }); - const useLocalFile = pkgVersion === installation?.version; - - if (useLocalFile) { - const assetPath = `${pkgName}-${pkgVersion}/${filePath}`; - const fileBuffer = getArchiveEntry(assetPath); - // only pull local installation if we don't have it cached - const storedAsset = !fileBuffer && (await getAsset({ savedObjectsClient, path: assetPath })); - - // error, if neither is available - if (!fileBuffer && !storedAsset) { - return response.custom({ - body: `installed package file not found: ${filePath}`, - statusCode: 404, - }); - } - - // if storedAsset is not available, fileBuffer *must* be - // b/c we error if we don't have at least one, and storedAsset is the least likely - const { buffer, contentType } = storedAsset - ? { - contentType: storedAsset.media_type, - buffer: storedAsset.data_utf8 - ? Buffer.from(storedAsset.data_utf8, 'utf8') - : Buffer.from(storedAsset.data_base64, 'base64'), - } - : { - contentType: mime.contentType(path.extname(assetPath)), - buffer: fileBuffer, - }; - - if (!contentType) { - return response.custom({ - body: `unknown content type for file: ${filePath}`, - statusCode: 400, - }); - } - - return response.custom({ - body: buffer, - statusCode: 200, - headers: { - ...CACHE_CONTROL_10_MINUTES_HEADER, - 'content-type': contentType, - }, - }); - } else { - const registryResponse = await getFile(pkgName, pkgVersion, filePath); - const headersToProxy: KnownHeaders[] = ['content-type']; - const proxiedHeaders = headersToProxy.reduce((headers, knownHeader) => { - const value = registryResponse.headers.get(knownHeader); - if (value !== null) { - headers[knownHeader] = value; - } - return headers; - }, {} as ResponseHeaders); - - return response.custom({ - body: registryResponse.body, - statusCode: registryResponse.status, - headers: { ...CACHE_CONTROL_10_MINUTES_HEADER, ...proxiedHeaders }, - }); - } - } catch (error) { - return defaultFleetErrorHandler({ error, response }); - } -}; - export const getInfoHandler: FleetRequestHandler< TypeOf, TypeOf diff --git a/x-pack/plugins/fleet/server/routes/epm/index.ts b/x-pack/plugins/fleet/server/routes/epm/index.ts index 6e0000bf4ccbf..5245381a409da 100644 --- a/x-pack/plugins/fleet/server/routes/epm/index.ts +++ b/x-pack/plugins/fleet/server/routes/epm/index.ts @@ -55,7 +55,6 @@ import { getListHandler, getInstalledListHandler, getLimitedListHandler, - getFileHandler, getInfoHandler, getBulkAssetsHandler, installPackageFromRegistryHandler, @@ -70,6 +69,7 @@ import { createCustomIntegrationHandler, getInputsHandler, } from './handlers'; +import { getFileHandler } from './file_handler'; const MAX_FILE_SIZE_BYTES = 104857600; // 100MB diff --git a/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts b/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts index cbc560cc72dc8..a16fd37c9ac15 100644 --- a/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/package_policy/handlers.ts @@ -6,7 +6,6 @@ */ import type { TypeOf } from '@kbn/config-schema'; -import Boom from '@hapi/boom'; import { SavedObjectsErrorHelpers } from '@kbn/core/server'; import type { RequestHandler } from '@kbn/core/server'; @@ -43,7 +42,11 @@ import type { UpgradePackagePolicyResponse, } from '../../../common/types'; import { installationStatuses, inputsFormat } from '../../../common/constants'; -import { defaultFleetErrorHandler, PackagePolicyNotFoundError } from '../../errors'; +import { + defaultFleetErrorHandler, + PackagePolicyNotFoundError, + PackagePolicyRequestError, +} from '../../errors'; import { getInstallations, getPackageInfo } from '../../services/epm/packages'; import { PACKAGES_SAVED_OBJECT_TYPE, SO_SEARCH_LIMIT } from '../../constants'; import { @@ -244,7 +247,7 @@ export const createPackagePolicyHandler: FleetRequestHandler< let newPackagePolicy: NewPackagePolicy; if (isSimplifiedCreatePackagePolicyRequest(newPolicy)) { if (!pkg) { - throw new Error('Package is required'); + throw new PackagePolicyRequestError('Package is required'); } const pkgInfo = await getPackageInfo({ savedObjectsClient: soClient, @@ -311,7 +314,7 @@ export const updatePackagePolicyHandler: FleetRequestHandler< const packagePolicy = await packagePolicyService.get(soClient, request.params.packagePolicyId); if (!packagePolicy) { - throw Boom.notFound('Package policy not found'); + throw new PackagePolicyNotFoundError('Package policy not found'); } if (limitedToPackages && limitedToPackages.length) { @@ -337,7 +340,7 @@ export const updatePackagePolicyHandler: FleetRequestHandler< isSimplifiedCreatePackagePolicyRequest(body as unknown as SimplifiedPackagePolicy) ) { if (!pkg) { - throw new Error('package is required'); + throw new PackagePolicyRequestError('Package is required'); } const pkgInfo = await getPackageInfo({ savedObjectsClient: soClient, diff --git a/x-pack/plugins/fleet/server/routes/uninstall_token/handlers.ts b/x-pack/plugins/fleet/server/routes/uninstall_token/handlers.ts index 804b90ab1aa2f..50dc1263ddf07 100644 --- a/x-pack/plugins/fleet/server/routes/uninstall_token/handlers.ts +++ b/x-pack/plugins/fleet/server/routes/uninstall_token/handlers.ts @@ -34,7 +34,12 @@ export const getUninstallTokensMetadataHandler: FleetRequestHandler< try { const { page = 1, perPage = 20, policyId } = request.query; - const body = await uninstallTokenService.getTokenMetadata(policyId?.trim(), page, perPage); + const body = await uninstallTokenService.getTokenMetadata( + policyId?.trim(), + page, + perPage, + 'policy-elastic-agent-on-cloud' + ); return response.ok({ body }); } catch (error) { diff --git a/x-pack/plugins/fleet/server/saved_objects/index.ts b/x-pack/plugins/fleet/server/saved_objects/index.ts index b1fa2c0e462ee..ff569adfd95b5 100644 --- a/x-pack/plugins/fleet/server/saved_objects/index.ts +++ b/x-pack/plugins/fleet/server/saved_objects/index.ts @@ -278,6 +278,10 @@ const getSavedObjectTypes = (): { [key: string]: SavedObjectsType } => ({ }, }, }, + preset: { + type: 'keyword', + index: false, + }, }, }, modelVersions: { @@ -329,6 +333,19 @@ const getSavedObjectTypes = (): { [key: string]: SavedObjectsType } => ({ }, ], }, + '4': { + changes: [ + { + type: 'mappings_addition', + addedMappings: { + preset: { + type: 'keyword', + index: false, + }, + }, + }, + ], + }, }, migrations: { '7.13.0': migrateOutputToV7130, @@ -697,6 +714,7 @@ export function registerEncryptedSavedObjects( 'timeout', 'broker_timeout', 'required_acks', + 'preset', 'secrets', ]), }); diff --git a/x-pack/plugins/fleet/server/services/agent_policies/__snapshots__/full_agent_policy.test.ts.snap b/x-pack/plugins/fleet/server/services/agent_policies/__snapshots__/full_agent_policy.test.ts.snap index df17382b1cd3f..86f8126c1c45a 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/__snapshots__/full_agent_policy.test.ts.snap +++ b/x-pack/plugins/fleet/server/services/agent_policies/__snapshots__/full_agent_policy.test.ts.snap @@ -58,12 +58,14 @@ Object { "hosts": Array [ "http://es-data.co:9201", ], + "preset": "balanced", "type": "elasticsearch", }, "default": Object { "hosts": Array [ "http://127.0.0.1:9201", ], + "preset": "balanced", "type": "elasticsearch", }, }, @@ -134,12 +136,14 @@ Object { "hosts": Array [ "http://127.0.0.1:9201", ], + "preset": "balanced", "type": "elasticsearch", }, "monitoring-output-id": Object { "hosts": Array [ "http://es-monitoring.co:9201", ], + "preset": "balanced", "type": "elasticsearch", }, }, @@ -210,12 +214,14 @@ Object { "hosts": Array [ "http://es-data.co:9201", ], + "preset": "balanced", "type": "elasticsearch", }, "monitoring-output-id": Object { "hosts": Array [ "http://es-monitoring.co:9201", ], + "preset": "balanced", "type": "elasticsearch", }, }, diff --git a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts index d9bf271210bc9..3250c55d5f6e7 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts +++ b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.test.ts @@ -485,6 +485,7 @@ describe('transformOutputToFullPolicyOutput', () => { "hosts": Array [ "http://host.fr", ], + "preset": "balanced", "type": "elasticsearch", } `); @@ -509,6 +510,7 @@ ssl.test: 123 "hosts": Array [ "http://host.fr", ], + "preset": "balanced", "ssl.ca_trusted_fingerprint": "fingerprint123", "ssl.test": 123, "test": 1234, @@ -541,6 +543,7 @@ ssl.test: 123 "hosts": Array [ "http://host.fr", ], + "preset": "balanced", "proxy_url": "https://proxy1.fr", "type": "elasticsearch", } @@ -567,6 +570,7 @@ ssl.test: 123 "http://host.fr", ], "password": "\${ES_PASSWORD}", + "preset": "balanced", "type": "elasticsearch", "username": "\${ES_USERNAME}", } diff --git a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts index dcb9ac0d0b89f..154614d2aa297 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/agent_policies/full_agent_policy.ts @@ -5,10 +5,17 @@ * 2.0. */ +/* eslint-disable @typescript-eslint/naming-convention */ + import type { SavedObjectsClientContract } from '@kbn/core/server'; import { safeLoad } from 'js-yaml'; import deepMerge from 'deepmerge'; +import { + getDefaultPresetForEsOutput, + outputTypeSupportPresets, +} from '../../../common/services/output_helpers'; + import type { FullAgentPolicy, PackagePolicy, @@ -311,9 +318,17 @@ export function transformOutputToFullPolicyOutput( proxy?: FleetProxy, standalone = false ): FullAgentPolicyOutput { - // eslint-disable-next-line @typescript-eslint/naming-convention - const { config_yaml, type, hosts, ca_sha256, ca_trusted_fingerprint, ssl, shipper, secrets } = - output; + const { + config_yaml, + type, + hosts, + ca_sha256, + ca_trusted_fingerprint, + ssl, + shipper, + secrets, + preset, + } = output; const configJs = config_yaml ? safeLoad(config_yaml) : {}; @@ -324,7 +339,6 @@ export function transformOutputToFullPolicyOutput( let kafkaData = {}; if (type === outputType.Kafka) { - /* eslint-disable @typescript-eslint/naming-convention */ const { client_id, version, @@ -482,6 +496,10 @@ export function transformOutputToFullPolicyOutput( newOutput.service_token = output.service_token; } + if (outputTypeSupportPresets(output.type)) { + newOutput.preset = preset ?? getDefaultPresetForEsOutput(config_yaml ?? '', safeLoad); + } + return newOutput; } diff --git a/x-pack/plugins/fleet/server/services/agent_policies/outputs_helpers.ts b/x-pack/plugins/fleet/server/services/agent_policies/outputs_helpers.ts index bbe00c49b414f..c7c02d8a53fb5 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/outputs_helpers.ts +++ b/x-pack/plugins/fleet/server/services/agent_policies/outputs_helpers.ts @@ -12,7 +12,7 @@ import { LICENCE_FOR_PER_POLICY_OUTPUT, outputType } from '../../../common/const import { policyHasFleetServer, policyHasSyntheticsIntegration } from '../../../common/services'; import { appContextService } from '..'; import { outputService } from '../output'; -import { OutputInvalidError, OutputLicenceError } from '../../errors'; +import { OutputInvalidError, OutputLicenceError, OutputNotFoundError } from '../../errors'; /** * Get the data output for a given agent policy @@ -28,7 +28,7 @@ export async function getDataOutputForAgentPolicy( agentPolicy.data_output_id || (await outputService.getDefaultDataOutputId(soClient)); if (!dataOutputId) { - throw new Error('No default data output found.'); + throw new OutputNotFoundError('No default data output found.'); } return outputService.get(soClient, dataOutputId); diff --git a/x-pack/plugins/fleet/server/services/agent_policies/package_policies_to_agent_permissions.ts b/x-pack/plugins/fleet/server/services/agent_policies/package_policies_to_agent_permissions.ts index 4445ebbe84769..ec36c7575937e 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/package_policies_to_agent_permissions.ts +++ b/x-pack/plugins/fleet/server/services/agent_policies/package_policies_to_agent_permissions.ts @@ -24,6 +24,7 @@ import type { RegistryDataStreamPrivileges, } from '../../../common/types'; import { PACKAGE_POLICY_DEFAULT_INDEX_PRIVILEGES } from '../../constants'; +import { PackagePolicyRequestError } from '../../errors'; import type { PackagePolicy } from '../../types'; import { pkgToPkgKey } from '../epm/registry'; @@ -46,7 +47,7 @@ export function storedPackagePoliciesToAgentPermissions( ): FullAgentPolicyOutputPermissions | undefined { // I'm not sure what permissions to return for this case, so let's return the defaults if (!packagePolicies) { - throw new Error( + throw new PackagePolicyRequestError( 'storedPackagePoliciesToAgentPermissions should be called with a PackagePolicy' ); } @@ -57,7 +58,9 @@ export function storedPackagePoliciesToAgentPermissions( const permissionEntries = packagePolicies.map((packagePolicy) => { if (!packagePolicy.package) { - throw new Error(`No package for package policy ${packagePolicy.name ?? packagePolicy.id}`); + throw new PackagePolicyRequestError( + `No package for package policy ${packagePolicy.name ?? packagePolicy.id}` + ); } const pkg = packageInfoCache.get(pkgToPkgKey(packagePolicy.package))!; diff --git a/x-pack/plugins/fleet/server/services/agent_policies/related_saved_objects.ts b/x-pack/plugins/fleet/server/services/agent_policies/related_saved_objects.ts index b614b9c2dd9e4..0108e9cd97721 100644 --- a/x-pack/plugins/fleet/server/services/agent_policies/related_saved_objects.ts +++ b/x-pack/plugins/fleet/server/services/agent_policies/related_saved_objects.ts @@ -16,6 +16,7 @@ import { getSourceUriForAgentPolicy } from '../../routes/agent/source_uri_utils' import { getFleetServerHostsForAgentPolicy } from '../fleet_server_host'; import { appContextService } from '../app_context'; import { bulkGetFleetProxies } from '../fleet_proxies'; +import { OutputNotFoundError } from '../../errors'; export async function fetchRelatedSavedObjects( soClient: SavedObjectsClientContract, @@ -27,7 +28,7 @@ export async function fetchRelatedSavedObjects( ]); if (!defaultDataOutputId) { - throw new Error('Default output is not setup'); + throw new OutputNotFoundError('Default output is not setup'); } const dataOutputId = agentPolicy.data_output_id || defaultDataOutputId; @@ -51,11 +52,11 @@ export async function fetchRelatedSavedObjects( const dataOutput = outputs.find((output) => output.id === dataOutputId); if (!dataOutput) { - throw new Error(`Data output not found ${dataOutputId}`); + throw new OutputNotFoundError(`Data output not found ${dataOutputId}`); } const monitoringOutput = outputs.find((output) => output.id === monitoringOutputId); if (!monitoringOutput) { - throw new Error(`Monitoring output not found ${monitoringOutputId}`); + throw new OutputNotFoundError(`Monitoring output not found ${monitoringOutputId}`); } const proxyIds = uniq( diff --git a/x-pack/plugins/fleet/server/services/agent_policy.test.ts b/x-pack/plugins/fleet/server/services/agent_policy.test.ts index 931168f545b55..3e97594ee959f 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy.test.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy.test.ts @@ -8,6 +8,8 @@ import { elasticsearchServiceMock, savedObjectsClientMock } from '@kbn/core/server/mocks'; import { SavedObjectsErrorHelpers } from '@kbn/core/server'; import { securityMock } from '@kbn/security-plugin/server/mocks'; +import { loggerMock } from '@kbn/logging-mocks'; +import type { Logger } from '@kbn/core/server'; import { PackagePolicyRestrictionRelatedError, FleetUnauthorizedError } from '../errors'; import type { @@ -105,8 +107,13 @@ function getAgentPolicyCreateMock() { }); return soClient; } - +let mockedLogger: jest.Mocked; describe('agent policy', () => { + beforeEach(() => { + mockedLogger = loggerMock.create(); + mockedAppContextService.getLogger.mockReturnValue(mockedLogger); + }); + afterEach(() => { jest.resetAllMocks(); }); diff --git a/x-pack/plugins/fleet/server/services/agent_policy.ts b/x-pack/plugins/fleet/server/services/agent_policy.ts index 568829fda978e..b44e0616b7b69 100644 --- a/x-pack/plugins/fleet/server/services/agent_policy.ts +++ b/x-pack/plugins/fleet/server/services/agent_policy.ts @@ -71,6 +71,7 @@ import { AgentPolicyNotFoundError, PackagePolicyRestrictionRelatedError, FleetUnauthorizedError, + FleetError, } from '../errors'; import type { FullAgentConfigMap } from '../../common/types/models/agent_cm'; @@ -125,24 +126,24 @@ class AgentPolicyService { id, savedObjectType: AGENT_POLICY_SAVED_OBJECT_TYPE, }); + const logger = appContextService.getLogger(); + logger.debug(`Starting update of agent policy ${id}`); const existingAgentPolicy = await this.get(soClient, id, true); if (!existingAgentPolicy) { - throw new Error('Agent policy not found'); + throw new AgentPolicyNotFoundError('Agent policy not found'); } if ( existingAgentPolicy.status === agentPolicyStatuses.Inactive && agentPolicy.status !== agentPolicyStatuses.Active ) { - throw new Error( + throw new FleetError( `Agent policy ${id} cannot be updated because it is ${existingAgentPolicy.status}` ); } - const logger = appContextService.getLogger(); - if (options.removeProtection) { logger.warn(`Setting tamper protection for Agent Policy ${id} to false`); } @@ -166,7 +167,7 @@ class AgentPolicyService { if (options.bumpRevision || options.removeProtection) { await this.triggerAgentPolicyUpdatedEvent(soClient, esClient, 'updated', id); } - + logger.debug(`Agent policy ${id} update completed`); return (await this.get(soClient, id)) as AgentPolicy; } @@ -190,7 +191,7 @@ class AgentPolicyService { is_preconfigured: true, }; - if (!id) throw new Error('Missing ID'); + if (!id) throw new AgentPolicyNotFoundError('Missing ID'); return await this.ensureAgentPolicy(soClient, esClient, newAgentPolicy, id as string); } @@ -254,6 +255,7 @@ class AgentPolicyService { this.checkTamperProtectionLicense(agentPolicy); const logger = appContextService.getLogger(); + logger.debug(`Creating new agent policy`); if (agentPolicy?.is_protected) { logger.warn( @@ -282,7 +284,7 @@ class AgentPolicyService { await appContextService.getUninstallTokenService()?.generateTokenForPolicyId(newSo.id); await this.triggerAgentPolicyUpdatedEvent(soClient, esClient, 'created', newSo.id); - + logger.debug(`Created new agent policy with id ${newSo.id}`); return { id: newSo.id, ...newSo.attributes }; } @@ -320,7 +322,7 @@ class AgentPolicyService { } if (agentPolicySO.error) { - throw new Error(agentPolicySO.error.message); + throw new FleetError(agentPolicySO.error.message); } const agentPolicy = { id: agentPolicySO.id, ...agentPolicySO.attributes }; @@ -356,7 +358,7 @@ class AgentPolicyService { } else if (agentPolicySO.error.statusCode === 404) { throw new AgentPolicyNotFoundError(`Agent policy ${agentPolicySO.id} not found`); } else { - throw new Error(agentPolicySO.error.message); + throw new FleetError(agentPolicySO.error.message); } } @@ -498,6 +500,9 @@ class AgentPolicyService { authorizationHeader?: HTTPAuthorizationHeader | null; } ): Promise { + const logger = appContextService.getLogger(); + logger.debug(`Starting update of agent policy ${id}`); + if (agentPolicy.name) { await this.requireUniqueName(soClient, { id, @@ -508,14 +513,12 @@ class AgentPolicyService { const existingAgentPolicy = await this.get(soClient, id, true); if (!existingAgentPolicy) { - throw new Error('Agent policy not found'); + throw new AgentPolicyNotFoundError('Agent policy not found'); } this.checkTamperProtectionLicense(agentPolicy); await this.checkForValidUninstallToken(agentPolicy, id); - const logger = appContextService.getLogger(); - if (agentPolicy?.is_protected && !policyHasEndpointSecurity(existingAgentPolicy)) { logger.warn( 'Agent policy requires Elastic Defend integration to set tamper protection to true' @@ -558,10 +561,13 @@ class AgentPolicyService { newAgentPolicyProps: Pick, options?: { user?: AuthenticatedUser } ): Promise { + const logger = appContextService.getLogger(); + logger.debug(`Starting copy of agent policy ${id}`); + // Copy base agent policy const baseAgentPolicy = await this.get(soClient, id, true); if (!baseAgentPolicy) { - throw new Error('Agent policy not found'); + throw new AgentPolicyNotFoundError('Agent policy not found'); } const newAgentPolicy = await this.create( soClient, @@ -631,11 +637,11 @@ class AgentPolicyService { // Get updated agent policy with package policies and adjusted tamper protection const updatedAgentPolicy = await this.get(soClient, newAgentPolicy.id, true); if (!updatedAgentPolicy) { - throw new Error('Copied agent policy not found'); + throw new AgentPolicyNotFoundError('Copied agent policy not found'); } await this.deployPolicy(soClient, newAgentPolicy.id); - + logger.debug(`Completed copy of agent policy ${id}`); return updatedAgentPolicy; } @@ -799,6 +805,9 @@ class AgentPolicyService { id: string, options?: { force?: boolean; removeFleetServerDocuments?: boolean; user?: AuthenticatedUser } ): Promise { + const logger = appContextService.getLogger(); + logger.debug(`Deleting agent policy ${id}`); + auditLoggingService.writeCustomSoAuditLog({ action: 'delete', id, @@ -807,7 +816,7 @@ class AgentPolicyService { const agentPolicy = await this.get(soClient, id, false); if (!agentPolicy) { - throw new Error('Agent policy not found'); + throw new AgentPolicyNotFoundError('Agent policy not found'); } if (agentPolicy.is_managed && !options?.force) { @@ -822,7 +831,7 @@ class AgentPolicyService { }); if (total > 0) { - throw new Error('Cannot delete agent policy that is assigned to agent(s)'); + throw new FleetError('Cannot delete agent policy that is assigned to agent(s)'); } const packagePolicies = await packagePolicyService.findAllForAgentPolicy(soClient, id); @@ -860,7 +869,7 @@ class AgentPolicyService { if (options?.removeFleetServerDocuments) { await this.deleteFleetServerPoliciesForPolicyId(esClient, id); } - + logger.debug(`Deleted agent policy ${id}`); return { id, name: agentPolicy.name, @@ -954,7 +963,7 @@ class AgentPolicyService { return acc; }, [] as BulkResponseItem[]); - logger.debug( + logger.warn( `Failed to index documents during policy deployment: ${JSON.stringify(erroredDocuments)}` ); } @@ -1225,7 +1234,7 @@ class AgentPolicyService { ); if (uninstallTokenError) { - throw new Error( + throw new FleetError( `Cannot enable Agent Tamper Protection: ${uninstallTokenError.error.message}` ); } diff --git a/x-pack/plugins/fleet/server/services/agents/crud.ts b/x-pack/plugins/fleet/server/services/agents/crud.ts index 7ad50c8d962c1..b8eb0f7d0ca14 100644 --- a/x-pack/plugins/fleet/server/services/agents/crud.ts +++ b/x-pack/plugins/fleet/server/services/agents/crud.ts @@ -4,7 +4,6 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import Boom from '@hapi/boom'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { SortResults } from '@elastic/elasticsearch/lib/api/types'; import type { SavedObjectsClientContract, ElasticsearchClient } from '@kbn/core/server'; @@ -19,7 +18,12 @@ import type { AgentStatus, FleetServerAgent } from '../../../common/types'; import { SO_SEARCH_LIMIT } from '../../../common/constants'; import { isAgentUpgradeable } from '../../../common/services'; import { AGENTS_INDEX } from '../../constants'; -import { FleetError, isESClientError, AgentNotFoundError } from '../../errors'; +import { + FleetError, + isESClientError, + AgentNotFoundError, + FleetUnauthorizedError, +} from '../../errors'; import { auditLoggingService } from '../audit_logging'; @@ -548,10 +552,10 @@ export async function getAgentByAccessAPIKeyId( throw new AgentNotFoundError('Agent not found'); } if (agent.access_api_key_id !== accessAPIKeyId) { - throw new Error('Agent api key id is not matching'); + throw new FleetError('Agent api key id is not matching'); } if (!agent.active) { - throw Boom.forbidden('Agent inactive'); + throw new FleetUnauthorizedError('Agent inactive'); } return agent; diff --git a/x-pack/plugins/fleet/server/services/agents/reassign.ts b/x-pack/plugins/fleet/server/services/agents/reassign.ts index 86d368d399310..0a5c6f9b51ee0 100644 --- a/x-pack/plugins/fleet/server/services/agents/reassign.ts +++ b/x-pack/plugins/fleet/server/services/agents/reassign.ts @@ -5,11 +5,14 @@ * 2.0. */ import type { SavedObjectsClientContract, ElasticsearchClient } from '@kbn/core/server'; -import Boom from '@hapi/boom'; import type { Agent } from '../../types'; import { agentPolicyService } from '../agent_policy'; -import { AgentReassignmentError, HostedAgentPolicyRestrictionRelatedError } from '../../errors'; +import { + AgentReassignmentError, + HostedAgentPolicyRestrictionRelatedError, + AgentPolicyNotFoundError, +} from '../../errors'; import { SO_SEARCH_LIMIT } from '../../constants'; @@ -33,7 +36,7 @@ export async function reassignAgent( ) { const newAgentPolicy = await agentPolicyService.get(soClient, newAgentPolicyId); if (!newAgentPolicy) { - throw Boom.notFound(`Agent policy not found: ${newAgentPolicyId}`); + throw new AgentPolicyNotFoundError(`Agent policy not found: ${newAgentPolicyId}`); } await reassignAgentIsAllowed(soClient, esClient, agentId, newAgentPolicyId); @@ -87,7 +90,7 @@ export async function reassignAgents( ): Promise<{ actionId: string }> { const newAgentPolicy = await agentPolicyService.get(soClient, newAgentPolicyId); if (!newAgentPolicy) { - throw Boom.notFound(`Agent policy not found: ${newAgentPolicyId}`); + throw new AgentPolicyNotFoundError(`Agent policy not found: ${newAgentPolicyId}`); } if (newAgentPolicy.is_managed) { throw new HostedAgentPolicyRestrictionRelatedError( diff --git a/x-pack/plugins/fleet/server/services/agents/update_agent_tags.test.ts b/x-pack/plugins/fleet/server/services/agents/update_agent_tags.test.ts index 84aa2226b485c..d962279f0ca32 100644 --- a/x-pack/plugins/fleet/server/services/agents/update_agent_tags.test.ts +++ b/x-pack/plugins/fleet/server/services/agents/update_agent_tags.test.ts @@ -185,7 +185,7 @@ describe('update_agent_tags', () => { await expect( updateAgentTags(soClient, esClient, { agentIds: ['agent1'] }, ['one'], []) - ).rejects.toThrowError('version conflict of 100 agents'); + ).rejects.toThrowError('Version conflict of 100 agents'); }); it('should write out error results on last retry with version conflicts', async () => { @@ -211,7 +211,7 @@ describe('update_agent_tags', () => { retryCount: MAX_RETRY_COUNT, } ) - ).rejects.toThrowError('version conflict of 100 agents'); + ).rejects.toThrowError('Version conflict of 100 agents'); const agentAction = esClient.create.mock.calls[0][0] as any; expect(agentAction?.body.agents.length).toEqual(100); @@ -243,7 +243,7 @@ describe('update_agent_tags', () => { retryCount: MAX_RETRY_COUNT, } ) - ).rejects.toThrowError('version conflict of 1 agents'); + ).rejects.toThrowError('Version conflict of 1 agents'); const agentAction = esClient.create.mock.calls[0][0] as any; expect(agentAction?.body.agents.length).toEqual(3); diff --git a/x-pack/plugins/fleet/server/services/agents/update_agent_tags_action_runner.ts b/x-pack/plugins/fleet/server/services/agents/update_agent_tags_action_runner.ts index d8208fd5f8d08..1c8db8451ccfa 100644 --- a/x-pack/plugins/fleet/server/services/agents/update_agent_tags_action_runner.ts +++ b/x-pack/plugins/fleet/server/services/agents/update_agent_tags_action_runner.ts @@ -15,6 +15,8 @@ import { AGENTS_INDEX } from '../../constants'; import { appContextService } from '../app_context'; +import { FleetError } from '../../errors'; + import { ActionRunner } from './action_runner'; import { BulkActionTaskType } from './bulk_action_types'; @@ -124,7 +126,9 @@ export async function updateTagsBatch( conflicts: 'proceed', // relying on the task to retry in case of conflicts - retry only conflicted agents }); } catch (error) { - throw new Error('Caught error: ' + JSON.stringify(error).slice(0, 1000)); + throw new FleetError( + 'Caught error while batch updating tags: ' + JSON.stringify(error).slice(0, 1000) + ); } appContextService.getLogger().debug(JSON.stringify(res).slice(0, 1000)); @@ -203,7 +207,7 @@ export async function updateTagsBatch( .getLogger() .debug(`action conflict result wrote on ${versionConflictCount} agents`); } - throw new Error(`version conflict of ${versionConflictCount} agents`); + throw new FleetError(`Version conflict of ${versionConflictCount} agents`); } return { actionId, updated: res.updated, took: res.took }; diff --git a/x-pack/plugins/fleet/server/services/agents/upgrade_action_runner.ts b/x-pack/plugins/fleet/server/services/agents/upgrade_action_runner.ts index 157eaa688c746..61e3ad42c8aae 100644 --- a/x-pack/plugins/fleet/server/services/agents/upgrade_action_runner.ts +++ b/x-pack/plugins/fleet/server/services/agents/upgrade_action_runner.ts @@ -10,7 +10,11 @@ import type { SavedObjectsClientContract, ElasticsearchClient } from '@kbn/core/ import { v4 as uuidv4 } from 'uuid'; import moment from 'moment'; -import { getRecentUpgradeInfoForAgent, isAgentUpgradeable } from '../../../common/services'; +import { + getRecentUpgradeInfoForAgent, + isAgentUpgradeable, + getNotUpgradeableMessage, +} from '../../../common/services'; import type { Agent } from '../../types'; @@ -86,7 +90,13 @@ export async function upgradeBatch( getRecentUpgradeInfoForAgent(agent).hasBeenUpgradedRecently || (!options.force && !isAgentUpgradeable(agent, latestAgentVersion, options.version)); if (isNotAllowed) { - throw new FleetError(`Agent ${agent.id} is not upgradeable`); + throw new FleetError( + `Agent ${agent.id} is not upgradeable: ${getNotUpgradeableMessage( + agent, + latestAgentVersion, + options.version + )}` + ); } if (!options.force && isHostedAgent(hostedPolicies, agent)) { diff --git a/x-pack/plugins/fleet/server/services/agents/versions.test.ts b/x-pack/plugins/fleet/server/services/agents/versions.test.ts index 513fba910705d..15fa38d4949bc 100644 --- a/x-pack/plugins/fleet/server/services/agents/versions.test.ts +++ b/x-pack/plugins/fleet/server/services/agents/versions.test.ts @@ -179,4 +179,29 @@ describe('getAvailableVersions', () => { expect(mockedFetch).toBeCalledTimes(1); expect(res2).not.toContain('300.0.0'); }); + + it('should gracefully handle 400 errors when fetching from product versions API', async () => { + mockKibanaVersion = '300.0.0'; + mockedReadFile.mockResolvedValue(`["8.1.0", "8.0.0", "7.17.0", "7.16.0"]`); + mockedFetch.mockResolvedValue({ + status: 400, + text: 'Bad request', + } as any); + + const res = await getAvailableVersions({ ignoreCache: true }); + + // Should sort, uniquify and filter out versions < 7.17 + expect(res).toEqual(['8.1.0', '8.0.0', '7.17.0']); + }); + + it('should gracefully handle network errors when fetching from product versions API', async () => { + mockKibanaVersion = '300.0.0'; + mockedReadFile.mockResolvedValue(`["8.1.0", "8.0.0", "7.17.0", "7.16.0"]`); + mockedFetch.mockRejectedValue('ECONNREFUSED'); + + const res = await getAvailableVersions({ ignoreCache: true }); + + // Should sort, uniquify and filter out versions < 7.17 + expect(res).toEqual(['8.1.0', '8.0.0', '7.17.0']); + }); }); diff --git a/x-pack/plugins/fleet/server/services/agents/versions.ts b/x-pack/plugins/fleet/server/services/agents/versions.ts index 8f31d3f12b344..b5a09bc99e2cb 100644 --- a/x-pack/plugins/fleet/server/services/agents/versions.ts +++ b/x-pack/plugins/fleet/server/services/agents/versions.ts @@ -24,6 +24,7 @@ const AGENT_VERSION_BUILD_FILE = 'x-pack/plugins/fleet/target/agent_versions_lis // Endpoint maintained by the web-team and hosted on the elastic website const PRODUCT_VERSIONS_URL = 'https://www.elastic.co/api/product_versions'; +const MAX_REQUEST_TIMEOUT = 60 * 1000; // Only attempt to fetch product versions for one minute total // Cache available versions in memory for 1 hour const CACHE_DURATION = 1000 * 60 * 60; @@ -118,21 +119,29 @@ async function fetchAgentVersionsFromApi() { }, }; - const response = await pRetry(() => fetch(PRODUCT_VERSIONS_URL, options), { retries: 1 }); - const rawBody = await response.text(); - - // We need to handle non-200 responses gracefully here to support airgapped environments where - // Kibana doesn't have internet access to query this API - if (response.status >= 400) { - logger.debug(`Status code ${response.status} received from versions API: ${rawBody}`); - return []; - } + try { + const response = await pRetry(() => fetch(PRODUCT_VERSIONS_URL, options), { + retries: 1, + maxRetryTime: MAX_REQUEST_TIMEOUT, + }); + const rawBody = await response.text(); + + // We need to handle non-200 responses gracefully here to support airgapped environments where + // Kibana doesn't have internet access to query this API + if (response.status >= 400) { + logger.debug(`Status code ${response.status} received from versions API: ${rawBody}`); + return []; + } - const jsonBody = JSON.parse(rawBody); + const jsonBody = JSON.parse(rawBody); - const versions: string[] = (jsonBody.length ? jsonBody[0] : []) - .filter((item: any) => item?.title?.includes('Elastic Agent')) - .map((item: any) => item?.version_number); + const versions: string[] = (jsonBody.length ? jsonBody[0] : []) + .filter((item: any) => item?.title?.includes('Elastic Agent')) + .map((item: any) => item?.version_number); - return versions; + return versions; + } catch (error) { + logger.debug(`Error fetching available versions from API: ${error.message}`); + return []; + } } diff --git a/x-pack/plugins/fleet/server/services/api_keys/enrollment_api_key.test.ts b/x-pack/plugins/fleet/server/services/api_keys/enrollment_api_key.test.ts index bc58941e4c295..9200346961f15 100644 --- a/x-pack/plugins/fleet/server/services/api_keys/enrollment_api_key.test.ts +++ b/x-pack/plugins/fleet/server/services/api_keys/enrollment_api_key.test.ts @@ -6,6 +6,10 @@ */ import { elasticsearchServiceMock, savedObjectsClientMock } from '@kbn/core/server/mocks'; +import { loggerMock } from '@kbn/logging-mocks'; + +import type { Logger } from '@kbn/core/server'; +import { securityMock } from '@kbn/security-plugin/server/mocks'; import { ENROLLMENT_API_KEYS_INDEX } from '../../constants'; @@ -27,10 +31,20 @@ jest.mock('uuid', () => { const mockedAgentPolicyService = agentPolicyService as jest.Mocked; const mockedAuditLoggingService = auditLoggingService as jest.Mocked; + const mockedAppContextService = appContextService as jest.Mocked; +mockedAppContextService.getSecuritySetup.mockImplementation(() => ({ + ...securityMock.createSetup(), +})); + +let mockedLogger: jest.Mocked; describe('enrollment api keys', () => { beforeEach(() => { + mockedLogger = loggerMock.create(); + mockedAppContextService.getLogger.mockReturnValue(mockedLogger); + }); + afterEach(() => { jest.resetAllMocks(); }); diff --git a/x-pack/plugins/fleet/server/services/api_keys/enrollment_api_key.ts b/x-pack/plugins/fleet/server/services/api_keys/enrollment_api_key.ts index ac7087c95296a..360723ebcf220 100644 --- a/x-pack/plugins/fleet/server/services/api_keys/enrollment_api_key.ts +++ b/x-pack/plugins/fleet/server/services/api_keys/enrollment_api_key.ts @@ -16,13 +16,15 @@ import { toElasticsearchQuery, fromKueryExpression } from '@kbn/es-query'; import type { ESSearchResponse as SearchResponse } from '@kbn/es-types'; import type { EnrollmentAPIKey, FleetServerEnrollmentAPIKey } from '../../types'; -import { FleetError } from '../../errors'; +import { FleetError, EnrollmentKeyNameExistsError, EnrollmentKeyNotFoundError } from '../../errors'; import { ENROLLMENT_API_KEYS_INDEX } from '../../constants'; import { agentPolicyService } from '../agent_policy'; import { escapeSearchQueryPhrase } from '../saved_object'; import { auditLoggingService } from '../audit_logging'; +import { appContextService } from '../app_context'; + import { invalidateAPIKeys } from './security'; const uuidRegex = @@ -90,7 +92,7 @@ export async function getEnrollmentAPIKey( return esDocToEnrollmentApiKey(body); } catch (e) { if (e instanceof errors.ResponseError && e.statusCode === 404) { - throw Boom.notFound(`Enrollment api key ${id} not found`); + throw new EnrollmentKeyNotFoundError(`Enrollment api key ${id} not found`); } throw e; @@ -106,6 +108,9 @@ export async function deleteEnrollmentApiKey( id: string, forceDelete = false ) { + const logger = appContextService.getLogger(); + logger.debug(`Deleting enrollment API key ${id}`); + const enrollmentApiKey = await getEnrollmentAPIKey(esClient, id); auditLoggingService.writeCustomAuditLog({ @@ -132,6 +137,9 @@ export async function deleteEnrollmentApiKey( refresh: 'wait_for', }); } + logger.debug( + `Deleted enrollment API key ${enrollmentApiKey.id} [api_key_id=${enrollmentApiKey.api_key_id}` + ); } export async function deleteEnrollmentApiKeyForAgentPolicyId( @@ -169,6 +177,9 @@ export async function generateEnrollmentAPIKey( ): Promise { const id = uuidv4(); const { name: providedKeyName, forceRecreate } = data; + const logger = appContextService.getLogger(); + logger.debug(`Creating enrollment API key ${data}`); + if (data.agentPolicyId) { await validateAgentPolicyId(soClient, data.agentPolicyId); } @@ -199,7 +210,7 @@ export async function generateEnrollmentAPIKey( k.name?.replace(providedKeyName, '').trim().match(uuidRegex) ) ) { - throw new FleetError( + throw new EnrollmentKeyNameExistsError( i18n.translate('xpack.fleet.serverError.enrollmentKeyDuplicate', { defaultMessage: 'An enrollment key named {providedKeyName} already exists for agent policy {agentPolicyId}', @@ -217,6 +228,7 @@ export async function generateEnrollmentAPIKey( auditLoggingService.writeCustomAuditLog({ message: `User creating enrollment API key [name=${name}] [policy_id=${agentPolicyId}]`, }); + logger.debug(`Creating enrollment API key [name=${name}] [policy_id=${agentPolicyId}]`); const key = await esClient.security .createApiKey({ @@ -245,11 +257,11 @@ export async function generateEnrollmentAPIKey( }, }) .catch((err) => { - throw new Error(`Impossible to create an api key: ${err.message}`); + throw new FleetError(`Impossible to create an api key: ${err.message}`); }); if (!key) { - throw new Error( + throw new FleetError( i18n.translate('xpack.fleet.serverError.unableToCreateEnrollmentKey', { defaultMessage: 'Unable to create an enrollment api key', }) @@ -332,9 +344,9 @@ export async function getEnrollmentAPIKeyById(esClient: ElasticsearchClient, api const [enrollmentAPIKey] = res.hits.hits.map(esDocToEnrollmentApiKey); if (enrollmentAPIKey?.api_key_id !== apiKeyId) { - throw new Error( + throw new FleetError( i18n.translate('xpack.fleet.serverError.returnedIncorrectKey', { - defaultMessage: 'find enrollmentKeyById returned an incorrect key', + defaultMessage: 'Find enrollmentKeyById returned an incorrect key', }) ); } diff --git a/x-pack/plugins/fleet/server/services/download_source.test.ts b/x-pack/plugins/fleet/server/services/download_source.test.ts index 8b63d376340ff..e244ec80077b2 100644 --- a/x-pack/plugins/fleet/server/services/download_source.test.ts +++ b/x-pack/plugins/fleet/server/services/download_source.test.ts @@ -8,6 +8,9 @@ import { savedObjectsClientMock } from '@kbn/core/server/mocks'; import { securityMock } from '@kbn/security-plugin/server/mocks'; +import { loggerMock } from '@kbn/logging-mocks'; + +import type { Logger } from '@kbn/core/server'; import type { DownloadSourceSOAttributes } from '../types'; @@ -132,9 +135,13 @@ function getMockedSoClient(options: { defaultDownloadSourceId?: string; sameName return soClient; } - +let mockedLogger: jest.Mocked; describe('Download Service', () => { beforeEach(() => { + mockedLogger = loggerMock.create(); + mockedAppContextService.getLogger.mockReturnValue(mockedLogger); + }); + afterEach(() => { mockedAgentPolicyService.list.mockClear(); mockedAgentPolicyService.hasAPMIntegration.mockClear(); mockedAgentPolicyService.removeDefaultSourceFromAll.mockReset(); diff --git a/x-pack/plugins/fleet/server/services/download_source.ts b/x-pack/plugins/fleet/server/services/download_source.ts index f1719e2eb4798..e679123f7e255 100644 --- a/x-pack/plugins/fleet/server/services/download_source.ts +++ b/x-pack/plugins/fleet/server/services/download_source.ts @@ -41,7 +41,7 @@ class DownloadSourceService { ); if (soResponse.error) { - throw new Error(soResponse.error.message); + throw new FleetError(soResponse.error.message); } return savedObjectToDownloadSource(soResponse); @@ -69,6 +69,9 @@ class DownloadSourceService { downloadSource: DownloadSourceBase, options?: { id?: string; overwrite?: boolean } ): Promise { + const logger = appContextService.getLogger(); + logger.debug(`Creating new download source`); + const data: DownloadSourceSOAttributes = downloadSource; await this.requireUniqueName(soClient, { @@ -100,6 +103,7 @@ class DownloadSourceService { overwrite: options?.overwrite ?? false, } ); + logger.debug(`Creating new download source ${options?.id}`); return savedObjectToDownloadSource(newSo); } @@ -108,6 +112,8 @@ class DownloadSourceService { id: string, newData: Partial ) { + const logger = appContextService.getLogger(); + logger.debug(`Updating download source ${id} with ${newData}`); const updateData: Partial = newData; if (updateData.proxy_id) { @@ -134,11 +140,16 @@ class DownloadSourceService { updateData ); if (soResponse.error) { - throw new Error(soResponse.error.message); + throw new FleetError(soResponse.error.message); + } else { + logger.debug(`Updated download source ${id}`); } } public async delete(soClient: SavedObjectsClientContract, id: string) { + const logger = appContextService.getLogger(); + logger.debug(`Deleting download source ${id}`); + const targetDS = await this.get(soClient, id); if (targetDS.is_default) { @@ -149,7 +160,7 @@ class DownloadSourceService { appContextService.getInternalUserESClient(), id ); - + logger.debug(`Deleted download source ${id}`); return soClient.delete(DOWNLOAD_SOURCE_SAVED_OBJECT_TYPE, id); } diff --git a/x-pack/plugins/fleet/server/services/epm/agent/agent.test.ts b/x-pack/plugins/fleet/server/services/epm/agent/agent.test.ts index 9163b39575f87..0ab728affd751 100644 --- a/x-pack/plugins/fleet/server/services/epm/agent/agent.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/agent/agent.test.ts @@ -5,9 +5,30 @@ * 2.0. */ +import { loggerMock } from '@kbn/logging-mocks'; +import { securityMock } from '@kbn/security-plugin/server/mocks'; + +import type { Logger } from '@kbn/core/server'; + +import { appContextService } from '../..'; + import { compileTemplate } from './agent'; +jest.mock('../../app_context'); + +const mockedAppContextService = appContextService as jest.Mocked; +mockedAppContextService.getSecuritySetup.mockImplementation(() => ({ + ...securityMock.createSetup(), +})); + +let mockedLogger: jest.Mocked; + describe('compileTemplate', () => { + beforeEach(() => { + mockedLogger = loggerMock.create(); + mockedAppContextService.getLogger.mockReturnValue(mockedLogger); + }); + it('should work', () => { const streamTemplate = ` input: log diff --git a/x-pack/plugins/fleet/server/services/epm/agent/agent.ts b/x-pack/plugins/fleet/server/services/epm/agent/agent.ts index 0bc220a500fb1..b58ab3e3fca0c 100644 --- a/x-pack/plugins/fleet/server/services/epm/agent/agent.ts +++ b/x-pack/plugins/fleet/server/services/epm/agent/agent.ts @@ -7,15 +7,18 @@ import Handlebars from 'handlebars'; import { safeLoad, safeDump } from 'js-yaml'; +import type { Logger } from '@kbn/core/server'; import type { PackagePolicyConfigRecord } from '../../../../common/types'; import { toCompiledSecretRef } from '../../secrets'; import { PackageInvalidArchiveError } from '../../../errors'; +import { appContextService } from '../..'; const handlebars = Handlebars.create(); export function compileTemplate(variables: PackagePolicyConfigRecord, templateStr: string) { - const { vars, yamlValues } = buildTemplateVariables(variables); + const logger = appContextService.getLogger(); + const { vars, yamlValues } = buildTemplateVariables(logger, variables); let compiledTemplate: string; try { const template = handlebars.compile(templateStr, { noEscape: true }); @@ -65,12 +68,13 @@ function replaceVariablesInYaml(yamlVariables: { [k: string]: any }, yaml: any) return yaml; } -function buildTemplateVariables(variables: PackagePolicyConfigRecord) { +function buildTemplateVariables(logger: Logger, variables: PackagePolicyConfigRecord) { const yamlValues: { [k: string]: any } = {}; const vars = Object.entries(variables).reduce((acc, [key, recordEntry]) => { // support variables with . like key.patterns const keyParts = key.split('.'); const lastKeyPart = keyParts.pop(); + logger.debug(`Building agent template variables`); if (!lastKeyPart || !isValidKey(lastKeyPart)) { throw new PackageInvalidArchiveError( diff --git a/x-pack/plugins/fleet/server/services/epm/archive/cache.ts b/x-pack/plugins/fleet/server/services/epm/archive/cache.ts index 8b1fd141f3000..db0b0d709e683 100644 --- a/x-pack/plugins/fleet/server/services/epm/archive/cache.ts +++ b/x-pack/plugins/fleet/server/services/epm/archive/cache.ts @@ -43,7 +43,7 @@ export const getArchiveFilelist = (keyArgs: SharedKey) => export const setArchiveFilelist = (keyArgs: SharedKey, paths: string[]) => { const logger = appContextService.getLogger(); - logger.debug(`setting file list to the cache for ${keyArgs.name}-${keyArgs.version}`); + logger.debug(`Setting file list to the cache for ${keyArgs.name}-${keyArgs.version}`); logger.trace(JSON.stringify(paths)); return archiveFilelistCache.set(sharedKey(keyArgs), paths); }; @@ -79,7 +79,7 @@ export const setPackageInfo = ({ }: SharedKey & { packageInfo: ArchivePackage | RegistryPackage }) => { const logger = appContextService.getLogger(); const key = sharedKey({ name, version }); - logger.debug(`setting package info to the cache for ${name}-${version}`); + logger.debug(`Setting package info to the cache for ${name}-${version}`); logger.trace(JSON.stringify(packageInfo)); return packageInfoCache.set(key, packageInfo); }; diff --git a/x-pack/plugins/fleet/server/services/epm/archive/index.ts b/x-pack/plugins/fleet/server/services/epm/archive/index.ts index 330839b13dba9..ec19fbba55a19 100644 --- a/x-pack/plugins/fleet/server/services/epm/archive/index.ts +++ b/x-pack/plugins/fleet/server/services/epm/archive/index.ts @@ -6,7 +6,11 @@ */ import type { AssetParts } from '../../../../common/types'; -import { PackageInvalidArchiveError, PackageUnsupportedMediaTypeError } from '../../../errors'; +import { + PackageInvalidArchiveError, + PackageUnsupportedMediaTypeError, + PackageNotFoundError, +} from '../../../errors'; import { getArchiveEntry, @@ -41,10 +45,10 @@ export async function unpackBufferToCache({ contentType: string; archiveBuffer: Buffer; }): Promise { + const entries = await unpackBufferEntries(archiveBuffer, contentType); // Make sure any buffers from previous installations from registry or upload are deleted first clearPackageFileCache({ name, version }); - const entries = await unpackBufferEntries(archiveBuffer, contentType); const paths: string[] = []; entries.forEach((entry) => { const { path, buffer } = entry; @@ -149,7 +153,7 @@ export function getPathParts(path: string): AssetParts { export function getAsset(key: string) { const buffer = getArchiveEntry(key); - if (buffer === undefined) throw new Error(`Cannot find asset ${key}`); + if (buffer === undefined) throw new PackageNotFoundError(`Cannot find asset ${key}`); return buffer; } diff --git a/x-pack/plugins/fleet/server/services/epm/archive/parse.test.ts b/x-pack/plugins/fleet/server/services/epm/archive/parse.test.ts index 1e7d7d24e2b8f..ac72f56946d04 100644 --- a/x-pack/plugins/fleet/server/services/epm/archive/parse.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/archive/parse.test.ts @@ -4,9 +4,16 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { loggerMock } from '@kbn/logging-mocks'; + +import type { Logger } from '@kbn/core/server'; +import { securityMock } from '@kbn/security-plugin/server/mocks'; + import type { ArchivePackage } from '../../../../common/types'; import { PackageInvalidArchiveError } from '../../../errors'; +import { appContextService } from '../..'; + import { parseDefaultIngestPipeline, parseDataStreamElasticsearchEntry, @@ -21,7 +28,20 @@ import { parseAndVerifyReadme, } from './parse'; +jest.mock('../../app_context'); + +const mockedAppContextService = appContextService as jest.Mocked; +mockedAppContextService.getSecuritySetup.mockImplementation(() => ({ + ...securityMock.createSetup(), +})); + +let mockedLogger: jest.Mocked; describe('parseDefaultIngestPipeline', () => { + beforeEach(() => { + mockedLogger = loggerMock.create(); + mockedAppContextService.getLogger.mockReturnValue(mockedLogger); + }); + it('Should return undefined for stream without any elasticsearch dir', () => { expect( parseDefaultIngestPipeline('pkg-1.0.0/data_stream/stream1/', [ diff --git a/x-pack/plugins/fleet/server/services/epm/archive/parse.ts b/x-pack/plugins/fleet/server/services/epm/archive/parse.ts index e0111e196ddb0..eac5c2ac43db1 100644 --- a/x-pack/plugins/fleet/server/services/epm/archive/parse.ts +++ b/x-pack/plugins/fleet/server/services/epm/archive/parse.ts @@ -16,6 +16,8 @@ import { pick } from 'lodash'; import semverMajor from 'semver/functions/major'; import semverPrerelease from 'semver/functions/prerelease'; +import { appContextService } from '../..'; + import type { ArchivePackage, RegistryPolicyTemplate, @@ -198,7 +200,9 @@ export function parseAndVerifyArchive( topLevelDirOverride?: string ): ArchivePackage { // The top-level directory must match pkgName-pkgVersion, and no other top-level files or directories may be present + const logger = appContextService.getLogger(); const toplevelDir = topLevelDirOverride || paths[0].split('/')[0]; + paths.forEach((filePath) => { if (!filePath.startsWith(toplevelDir)) { throw new PackageInvalidArchiveError( @@ -210,6 +214,7 @@ export function parseAndVerifyArchive( // The package must contain a manifest file ... const manifestFile = path.posix.join(toplevelDir, MANIFEST_NAME); const manifestBuffer = assetsMap[manifestFile]; + logger.debug(`Verifying archive - checking manifest file and manifest buffer`); if (!paths.includes(manifestFile) || !manifestBuffer) { throw new PackageInvalidArchiveError( `Package at top-level directory ${toplevelDir} must contain a top-level ${MANIFEST_NAME} file.` @@ -219,6 +224,7 @@ export function parseAndVerifyArchive( // ... which must be valid YAML let manifest: ArchivePackage; try { + logger.debug(`Verifying archive - loading yaml`); manifest = yaml.safeLoad(manifestBuffer.toString()); } catch (error) { throw new PackageInvalidArchiveError( @@ -227,6 +233,7 @@ export function parseAndVerifyArchive( } // must have mandatory fields + logger.debug(`Verifying archive - verifying manifest content`); const reqGiven = pick(manifest, requiredArchivePackageProps); const requiredKeysMatch = Object.keys(reqGiven).toString() === requiredArchivePackageProps.toString(); @@ -246,13 +253,15 @@ export function parseAndVerifyArchive( const parsed: ArchivePackage = { ...reqGiven, ...optGiven }; // Package name and version from the manifest must match those from the toplevel directory + logger.debug(`Verifying archive - parsing manifest: ${parsed}`); const pkgKey = pkgToPkgKey({ name: parsed.name, version: parsed.version }); + if (!topLevelDirOverride && toplevelDir !== pkgKey) { throw new PackageInvalidArchiveError( `Name ${parsed.name} and version ${parsed.version} do not match top-level directory ${toplevelDir}` ); } - + logger.debug(`Parsing archive - parsing and verifying data streams`); const parsedDataStreams = parseAndVerifyDataStreams({ paths, pkgName: parsed.name, @@ -265,9 +274,11 @@ export function parseAndVerifyArchive( parsed.data_streams = parsedDataStreams; } + logger.debug(`Parsing archive - parsing and verifying policy templates`); parsed.policy_templates = parseAndVerifyPolicyTemplates(manifest); // add readme if exists + logger.debug(`Parsing archive - parsing and verifying Readme`); const readme = parseAndVerifyReadme(paths, parsed.name, parsed.version); if (readme) { parsed.readme = readme; @@ -283,6 +294,7 @@ export function parseAndVerifyArchive( // Ensure top-level variables are parsed as well if (manifest.vars) { + logger.debug(`Parsing archive - parsing and verifying top-level vars`); parsed.vars = parseAndVerifyVars(manifest.vars, 'manifest.yml'); } @@ -294,6 +306,7 @@ export function parseAndVerifyArchive( let tags: PackageSpecTags[]; try { tags = yaml.safeLoad(tagsBuffer.toString()); + logger.debug(`Parsing archive - parsing kibana/tags.yml file`); if (tags.length) { parsed.asset_tags = tags; } diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/retry.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/retry.ts index 773ed7273d885..32d0c9e23fb78 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/retry.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/retry.ts @@ -17,7 +17,10 @@ const retryResponseStatuses = [ 410, // Gone ]; +const retryMessages = ['no_shard_available_action_exception']; + const isRetryableError = (e: any, additionalResponseStatuses: number[] = []) => + retryMessages.some((msg) => e.message.includes(msg)) || e instanceof EsErrors.NoLivingConnectionsError || e instanceof EsErrors.ConnectionError || e instanceof EsErrors.TimeoutError || diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.test.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.test.ts index a621df33062a8..670ba36ed6a8d 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.test.ts @@ -755,6 +755,84 @@ describe('EPM template', () => { expect(mappings).toEqual(objectFieldWithPropertyReversedMapping); }); + it('tests processing object field with subobjects set to false (case B)', () => { + const objectFieldWithPropertyReversedLiteralYml = ` +- name: b.labels.* + type: object + object_type: keyword + subobjects: false + `; + const objectFieldWithPropertyReversedMapping = { + dynamic_templates: [ + { + 'b.labels.*': { + path_match: 'b.labels.*', + match_mapping_type: 'string', + mapping: { + type: 'keyword', + }, + }, + }, + ], + properties: { + b: { + type: 'object', + dynamic: true, + properties: { + labels: { + dynamic: true, + type: 'object', + subobjects: false, + }, + }, + }, + }, + }; + const fields: Field[] = safeLoad(objectFieldWithPropertyReversedLiteralYml); + const processedFields = processFields(fields); + const mappings = generateMappings(processedFields); + expect(mappings).toEqual(objectFieldWithPropertyReversedMapping); + }); + + it('tests processing object field with subobjects set to false (case D)', () => { + const objectFieldWithPropertyReversedLiteralYml = ` +- name: d.labels + type: object + object_type: keyword + subobjects: false + `; + const objectFieldWithPropertyReversedMapping = { + dynamic_templates: [ + { + 'd.labels': { + path_match: 'd.labels.*', + match_mapping_type: 'string', + mapping: { + type: 'keyword', + }, + }, + }, + ], + properties: { + d: { + type: 'object', + dynamic: true, + properties: { + labels: { + dynamic: true, + type: 'object', + subobjects: false, + }, + }, + }, + }, + }; + const fields: Field[] = safeLoad(objectFieldWithPropertyReversedLiteralYml); + const processedFields = processFields(fields); + const mappings = generateMappings(processedFields); + expect(mappings).toEqual(objectFieldWithPropertyReversedMapping); + }); + it('tests processing nested field with property', () => { const nestedYaml = ` - name: a.b diff --git a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.ts b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.ts index a26aa2c8e0114..a60e9dc0c7ced 100644 --- a/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.ts +++ b/x-pack/plugins/fleet/server/services/epm/elasticsearch/template/template.ts @@ -222,9 +222,11 @@ function _generateMappings( properties: IndexTemplateMappings['properties']; hasNonDynamicTemplateMappings: boolean; hasDynamicTemplateMappings: boolean; + subobjects?: boolean; } { let hasNonDynamicTemplateMappings = false; let hasDynamicTemplateMappings = false; + let subobjects: boolean | undefined; const props: Properties = {}; function addParentObjectAsStaticProperty(field: Field) { @@ -237,6 +239,7 @@ function _generateMappings( const fieldProps = { type: 'object', dynamic: true, + ...(field.subobjects !== undefined && { subobjects: field.subobjects }), }; props[field.name] = fieldProps; @@ -435,6 +438,17 @@ function _generateMappings( ); } + // When a wildcard field specifies the subobjects setting, + // the parent intermediate object should set the subobjects + // setting. + // + // For example, if a wildcard field `foo.*` has subobjects, + // we should set subobjects on the intermediate object `foo`. + // + if (field.subobjects !== undefined && path.includes('*')) { + subobjects = field.subobjects; + } + if (dynProperties && matchingType) { addDynamicMappingWithIntermediateObjects(path, pathMatch, matchingType, dynProperties); @@ -476,6 +490,9 @@ function _generateMappings( } else { return; } + if (mappings.subobjects !== undefined) { + fieldProps.subobjects = mappings.subobjects; + } break; case 'group-nested': fieldProps = { @@ -583,6 +600,10 @@ function _generateMappings( fieldProps.time_series_dimension = field.dimension; } + if (field.subobjects !== undefined) { + fieldProps.subobjects = field.subobjects; + } + // Even if we don't add the property because it has a wildcard, notify // the parent that there is some kind of property, so the intermediate object // is still created. @@ -601,7 +622,12 @@ function _generateMappings( }); } - return { properties: props, hasNonDynamicTemplateMappings, hasDynamicTemplateMappings }; + return { + properties: props, + hasNonDynamicTemplateMappings, + hasDynamicTemplateMappings, + subobjects, + }; } function generateDynamicAndEnabled(field: Field) { diff --git a/x-pack/plugins/fleet/server/services/epm/fields/field.ts b/x-pack/plugins/fleet/server/services/epm/fields/field.ts index 437eed7c64192..495c0ddcaaf8a 100644 --- a/x-pack/plugins/fleet/server/services/epm/fields/field.ts +++ b/x-pack/plugins/fleet/server/services/epm/fields/field.ts @@ -40,6 +40,7 @@ export interface Field { dimension?: boolean; default_field?: boolean; runtime?: boolean | string; + subobjects?: boolean; // Fields specific of the aggregate_metric_double type metrics?: string[]; diff --git a/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts b/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts index 23327a2253f86..27be17ef2a170 100644 --- a/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/kibana/assets/install.ts @@ -32,7 +32,11 @@ import type { PackageSpecTags, } from '../../../../types'; import { savedObjectTypes } from '../../packages'; -import { indexPatternTypes, getIndexPatternSavedObjects } from '../index_pattern/install'; +import { + indexPatternTypes, + getIndexPatternSavedObjects, + makeManagedIndexPatternsGlobal, +} from '../index_pattern/install'; import { saveKibanaAssetsRefs } from '../../packages/install'; import { deleteKibanaSavedObjectsAssets } from '../../packages/remove'; import { KibanaSOReferenceError } from '../../../../errors'; @@ -114,12 +118,14 @@ export function createSavedObjectKibanaAsset(asset: ArchiveAsset): SavedObjectTo } export async function installKibanaAssets(options: { + savedObjectsClient: SavedObjectsClientContract; savedObjectsImporter: SavedObjectsImporterContract; logger: Logger; pkgName: string; kibanaAssets: Record; }): Promise { - const { kibanaAssets, savedObjectsImporter, logger } = options; + const { kibanaAssets, savedObjectsClient, savedObjectsImporter, logger } = options; + const assetsToInstall = Object.entries(kibanaAssets).flatMap(([assetType, assets]) => { if (!validKibanaAssetTypes.has(assetType as KibanaAssetType)) { return []; @@ -152,6 +158,8 @@ export async function installKibanaAssets(options: { managed: true, }); + await makeManagedIndexPatternsGlobal(savedObjectsClient); + const installedAssets = await installKibanaSavedObjects({ logger, savedObjectsImporter, @@ -196,6 +204,7 @@ export async function installKibanaAssetsAndReferences({ ); const importedAssets = await installKibanaAssets({ + savedObjectsClient, logger, savedObjectsImporter, pkgName, diff --git a/x-pack/plugins/fleet/server/services/epm/kibana/index_pattern/install.test.ts b/x-pack/plugins/fleet/server/services/epm/kibana/index_pattern/install.test.ts new file mode 100644 index 0000000000000..9e826d7f126ae --- /dev/null +++ b/x-pack/plugins/fleet/server/services/epm/kibana/index_pattern/install.test.ts @@ -0,0 +1,54 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; +import { savedObjectsClientMock } from '@kbn/core-saved-objects-api-server-mocks'; + +import { createAppContextStartContractMock } from '../../../../mocks'; + +import { appContextService } from '../../../app_context'; + +import { makeManagedIndexPatternsGlobal } from './install'; + +describe('Fleet index patterns', () => { + let mockSoClient: jest.Mocked; + let mockContract: ReturnType; + + beforeEach(() => { + mockSoClient = savedObjectsClientMock.create(); + mockContract = createAppContextStartContractMock(); + appContextService.start(mockContract); + }); + + afterEach(() => { + appContextService.stop(); + }); + + describe('makeManagedIndexPatternsGlobal', () => { + it('should call updateObjectsSpaces with the correct params', async () => { + const result = await makeManagedIndexPatternsGlobal(mockSoClient); + + for (const pattern of ['logs-*', 'metrics-*']) { + expect(mockSoClient.updateObjectsSpaces).toHaveBeenCalledWith( + [{ id: pattern, type: 'index-pattern' }], + ['*'], + [] + ); + } + + expect(result).toHaveLength(2); + }); + + it('handles errors from updateObjectsSpaces', async () => { + mockSoClient.updateObjectsSpaces.mockRejectedValue(new Error('foo')); + + const result = await makeManagedIndexPatternsGlobal(mockSoClient); + + expect(result).toHaveLength(0); + }); + }); +}); diff --git a/x-pack/plugins/fleet/server/services/epm/kibana/index_pattern/install.ts b/x-pack/plugins/fleet/server/services/epm/kibana/index_pattern/install.ts index 91e603e80aee2..6f1390f5e17cd 100644 --- a/x-pack/plugins/fleet/server/services/epm/kibana/index_pattern/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/kibana/index_pattern/install.ts @@ -28,6 +28,30 @@ export function getIndexPatternSavedObjects() { })); } +export async function makeManagedIndexPatternsGlobal( + savedObjectsClient: SavedObjectsClientContract +) { + const logger = appContextService.getLogger(); + + const results = []; + + for (const indexPatternType of indexPatternTypes) { + try { + const result = await savedObjectsClient.updateObjectsSpaces( + [{ id: `${indexPatternType}-*`, type: INDEX_PATTERN_SAVED_OBJECT_TYPE }], + ['*'], + [] + ); + + results.push(result); + } catch (error) { + logger.error(`Error making managed index patterns global: ${error.message}`); + } + } + + return results; +} + export async function removeUnusedIndexPatterns(savedObjectsClient: SavedObjectsClientContract) { const logger = appContextService.getLogger(); // get all user installed packages diff --git a/x-pack/plugins/fleet/server/services/epm/package_service.mock.ts b/x-pack/plugins/fleet/server/services/epm/package_service.mock.ts index 4007ad7545ece..3eb689dfa1a2a 100644 --- a/x-pack/plugins/fleet/server/services/epm/package_service.mock.ts +++ b/x-pack/plugins/fleet/server/services/epm/package_service.mock.ts @@ -10,6 +10,7 @@ import type { PackageClient, PackageService } from './package_service'; const createClientMock = (): jest.Mocked => ({ getInstallation: jest.fn(), ensureInstalledPackage: jest.fn(), + installPackage: jest.fn(), fetchFindLatestPackage: jest.fn(), readBundledPackage: jest.fn(), getPackage: jest.fn(), diff --git a/x-pack/plugins/fleet/server/services/epm/package_service.test.ts b/x-pack/plugins/fleet/server/services/epm/package_service.test.ts index ef9141ae9dfe5..9da46439a53c7 100644 --- a/x-pack/plugins/fleet/server/services/epm/package_service.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/package_service.test.ts @@ -169,12 +169,16 @@ function getTest( }; break; case testKeys[5]: - const bundledPackage = { name: 'package name', version: '8.0.0', buffer: Buffer.from([]) }; + const bundledPackage = { + name: 'package name', + version: '8.0.0', + getBuffer: () => Buffer.from([]), + }; test = { method: mocks.packageClient.readBundledPackage.bind(mocks.packageClient), args: [bundledPackage], spy: jest.spyOn(epmArchiveParse, 'generatePackageInfoFromArchiveBuffer'), - spyArgs: [bundledPackage.buffer, 'application/zip'], + spyArgs: [bundledPackage.getBuffer(), 'application/zip'], spyResponse: { packageInfo: { name: 'readBundledPackage test' }, paths: ['/some/test/path'], diff --git a/x-pack/plugins/fleet/server/services/epm/package_service.ts b/x-pack/plugins/fleet/server/services/epm/package_service.ts index e6f71cb7cb96c..a535af9636d1a 100644 --- a/x-pack/plugins/fleet/server/services/epm/package_service.ts +++ b/x-pack/plugins/fleet/server/services/epm/package_service.ts @@ -8,34 +8,40 @@ /* eslint-disable max-classes-per-file */ import type { - KibanaRequest, ElasticsearchClient, - SavedObjectsClientContract, + KibanaRequest, Logger, + SavedObjectsClientContract, } from '@kbn/core/server'; +import { DEFAULT_SPACE_ID } from '@kbn/spaces-plugin/common'; + import { HTTPAuthorizationHeader } from '../../../common/http_authorization_header'; import type { PackageList } from '../../../common'; import type { + ArchivePackage, + BundledPackage, CategoryId, EsAssetReference, InstallablePackage, Installation, RegistryPackage, - ArchivePackage, - BundledPackage, } from '../../types'; import type { FleetAuthzRouteConfig } from '../security/types'; -import { checkSuperuser, getAuthzFromRequest, doesNotHaveRequiredFleetAuthz } from '../security'; -import { FleetUnauthorizedError, FleetError } from '../../errors'; +import { checkSuperuser, doesNotHaveRequiredFleetAuthz, getAuthzFromRequest } from '../security'; +import { FleetError, FleetUnauthorizedError } from '../../errors'; import { INSTALL_PACKAGES_AUTHZ, READ_PACKAGE_INFO_AUTHZ } from '../../routes/epm'; -import { installTransforms, isTransform } from './elasticsearch/transform/install'; +import type { InstallResult } from '../../../common'; + import type { FetchFindLatestPackageOptions } from './registry'; +import * as Registry from './registry'; import { fetchFindLatestPackageOrThrow, getPackage } from './registry'; -import { ensureInstalledPackage, getInstallation, getPackages } from './packages'; + +import { installTransforms, isTransform } from './elasticsearch/transform/install'; +import { ensureInstalledPackage, getInstallation, getPackages, installPackage } from './packages'; import { generatePackageInfoFromArchiveBuffer } from './archive'; export type InstalledAssetType = EsAssetReference; @@ -52,8 +58,16 @@ export interface PackageClient { pkgName: string; pkgVersion?: string; spaceId?: string; + force?: boolean; }): Promise; + installPackage(options: { + pkgName: string; + pkgVersion?: string; + spaceId?: string; + force?: boolean; + }): Promise; + fetchFindLatestPackage( packageName: string, options?: FetchFindLatestPackageOptions @@ -151,6 +165,7 @@ class PackageClientImpl implements PackageClient { pkgName: string; pkgVersion?: string; spaceId?: string; + force?: boolean; }): Promise { await this.#runPreflight(INSTALL_PACKAGES_AUTHZ); @@ -160,6 +175,32 @@ class PackageClientImpl implements PackageClient { savedObjectsClient: this.internalSoClient, }); } + public async installPackage(options: { + pkgName: string; + pkgVersion?: string; + spaceId?: string; + force?: boolean; + }): Promise { + await this.#runPreflight(INSTALL_PACKAGES_AUTHZ); + + const { pkgName, pkgVersion, spaceId = DEFAULT_SPACE_ID, force = false } = options; + + // If pkgVersion isn't specified, find the latest package version + const pkgKeyProps = pkgVersion + ? { name: pkgName, version: pkgVersion } + : await Registry.fetchFindLatestPackageOrThrow(pkgName, { prerelease: true }); + const pkgkey = Registry.pkgToPkgKey(pkgKeyProps); + + return await installPackage({ + force, + pkgkey, + spaceId, + installSource: 'registry', + esClient: this.internalEsClient, + savedObjectsClient: this.internalSoClient, + neverIgnoreVerificationError: !force, + }); + } public async fetchFindLatestPackage( packageName: string, @@ -171,7 +212,9 @@ class PackageClientImpl implements PackageClient { public async readBundledPackage(bundledPackage: BundledPackage) { await this.#runPreflight(READ_PACKAGE_INFO_AUTHZ); - return generatePackageInfoFromArchiveBuffer(bundledPackage.buffer, 'application/zip'); + const archiveBuffer = await bundledPackage.getBuffer(); + + return generatePackageInfoFromArchiveBuffer(archiveBuffer, 'application/zip'); } public async getPackage( diff --git a/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts b/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts index d8a943fef5341..8f45e8eee6b13 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/_install_package.ts @@ -100,7 +100,6 @@ export async function _installPackage({ skipDataStreamRollover?: boolean; }): Promise { const { name: pkgName, version: pkgVersion, title: pkgTitle } = packageInfo; - try { // if some installation already exists if (installedPkg) { @@ -108,12 +107,16 @@ export async function _installPackage({ const hasExceededTimeout = Date.now() - Date.parse(installedPkg.attributes.install_started_at) < MAX_TIME_COMPLETE_INSTALL; + logger.debug(`Package install - Install status ${installedPkg.attributes.install_status}`); // if the installation is currently running, don't try to install // instead, only return already installed assets if (isStatusInstalling && hasExceededTimeout) { // If this is a forced installation, ignore the timeout and restart the installation anyway + logger.debug(`Package install - Installation is running and has exceeded timeout`); + if (force) { + logger.debug(`Package install - Forced installation, restarting`); await restartInstallation({ savedObjectsClient, pkgName, @@ -131,6 +134,9 @@ export async function _installPackage({ } else { // if no installation is running, or the installation has been running longer than MAX_TIME_COMPLETE_INSTALL // (it might be stuck) update the saved object and proceed + logger.debug( + `Package install - no installation running or the installation has been running longer than ${MAX_TIME_COMPLETE_INSTALL}, restarting` + ); await restartInstallation({ savedObjectsClient, pkgName, @@ -140,6 +146,7 @@ export async function _installPackage({ }); } } else { + logger.debug(`Package install - Create installation`); await createInstallation({ savedObjectsClient, packageInfo, @@ -148,7 +155,7 @@ export async function _installPackage({ verificationResult, }); } - + logger.debug(`Package install - Installing Kibana assets`); const kibanaAssetPromise = withPackageSpan('Install Kibana assets', () => installKibanaAssetsAndReferences({ savedObjectsClient, @@ -182,7 +189,7 @@ export async function _installPackage({ esReferences = await withPackageSpan('Install ILM policies', () => installILMPolicy(packageInfo, paths, esClient, savedObjectsClient, logger, esReferences) ); - + logger.debug(`Package install - Installing Data Stream ILM policies`); ({ esReferences } = await withPackageSpan('Install Data Stream ILM policies', () => installIlmForDataStream( packageInfo, @@ -196,6 +203,7 @@ export async function _installPackage({ } // installs ml models + logger.debug(`Package install - installing ML models`); esReferences = await withPackageSpan('Install ML models', () => installMlModel(packageInfo, paths, esClient, savedObjectsClient, logger, esReferences) ); @@ -203,6 +211,9 @@ export async function _installPackage({ let indexTemplates: IndexTemplateEntry[] = []; if (packageInfo.type === 'integration') { + logger.debug( + `Package install - Installing index templates and pipelines, packageInfo.type ${packageInfo.type}` + ); const { installedTemplates, esReferences: templateEsReferences } = await installIndexTemplatesAndPipelines({ installedPkg: installedPkg ? installedPkg.attributes : undefined, @@ -221,6 +232,7 @@ export async function _installPackage({ // input packages create their data streams during package policy creation // we must use installed_es to infer which streams exist first then // we can install the new index templates + logger.debug(`Package install - packageInfo.type ${packageInfo.type}`); const dataStreamNames = installedPkg.attributes.installed_es .filter((ref) => ref.type === 'index_template') // index templates are named {type}-{dataset}, remove everything before first hyphen @@ -231,6 +243,9 @@ export async function _installPackage({ ); if (dataStreams.length) { + logger.debug( + `Package install - installing index templates and pipelines with datastreams length ${dataStreams.length}` + ); const { installedTemplates, esReferences: templateEsReferences } = await installIndexTemplatesAndPipelines({ installedPkg: installedPkg ? installedPkg.attributes : undefined, @@ -248,19 +263,21 @@ export async function _installPackage({ } try { + logger.debug(`Package install - Removing legacy templates`); await removeLegacyTemplates({ packageInfo, esClient, logger }); } catch (e) { logger.warn(`Error removing legacy templates: ${e.message}`); } // update current backing indices of each data stream + logger.debug(`Package install - Updating backing indices of each data stream`); await withPackageSpan('Update write indices', () => updateCurrentWriteIndices(esClient, logger, indexTemplates, { ignoreMappingUpdateErrors, skipDataStreamRollover, }) ); - + logger.debug(`Package install - Installing transforms`); ({ esReferences } = await withPackageSpan('Install transforms', () => installTransforms({ installablePackage: packageInfo, @@ -282,6 +299,9 @@ export async function _installPackage({ (installType === 'update' || installType === 'reupdate') && installedPkg ) { + logger.debug( + `Package install - installType ${installType} Deleting previous ingest pipelines` + ); esReferences = await withPackageSpan('Delete previous ingest pipelines', () => deletePreviousPipelines( esClient, @@ -294,6 +314,9 @@ export async function _installPackage({ } // pipelines from a different version may have installed during a failed update if (installType === 'rollback' && installedPkg) { + logger.debug( + `Package install - installType ${installType} Deleting previous ingest pipelines` + ); esReferences = await withPackageSpan('Delete previous ingest pipelines', () => deletePreviousPipelines( esClient, @@ -306,6 +329,7 @@ export async function _installPackage({ } const installedKibanaAssetsRefs = await kibanaAssetPromise; + logger.debug(`Package install - Updating archive entries`); const packageAssetResults = await withPackageSpan('Update archive entries', () => saveArchiveEntries({ savedObjectsClient, @@ -326,7 +350,7 @@ export async function _installPackage({ id: pkgName, savedObjectType: PACKAGES_SAVED_OBJECT_TYPE, }); - + logger.debug(`Package install - Updating install status`); const updatedPackage = await withPackageSpan('Update install status', () => savedObjectsClient.update(PACKAGES_SAVED_OBJECT_TYPE, pkgName, { version: pkgVersion, @@ -340,6 +364,7 @@ export async function _installPackage({ ), }) ); + logger.debug(`Package install - Install status ${updatedPackage?.attributes?.install_status}`); // If the package is flagged with the `keep_policies_up_to_date` flag, upgrade its // associated package policies after installation @@ -350,11 +375,13 @@ export async function _installPackage({ perPage: SO_SEARCH_LIMIT, kuery: `${PACKAGE_POLICY_SAVED_OBJECT_TYPE}.package.name:${pkgName}`, }); - + logger.debug( + `Package install - Package is flagged with keep_policies_up_to_date, upgrading its associated package policies ${policyIdsToUpgrade}` + ); await packagePolicyService.upgrade(savedObjectsClient, esClient, policyIdsToUpgrade.items); }); } - + logger.debug(`Package install - Installation complete`); return [...installedKibanaAssetsRefs, ...esReferences]; } catch (err) { if (SavedObjectsErrorHelpers.isConflictError(err)) { diff --git a/x-pack/plugins/fleet/server/services/epm/packages/bundled_package.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/bundled_packages.test.ts similarity index 50% rename from x-pack/plugins/fleet/server/services/epm/packages/bundled_package.test.ts rename to x-pack/plugins/fleet/server/services/epm/packages/bundled_packages.test.ts index f44a865e8992c..ff3ea5f0676c7 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/bundled_package.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/bundled_packages.test.ts @@ -7,28 +7,37 @@ import fs from 'fs/promises'; +import { omit } from 'lodash'; + import { loggingSystemMock } from '@kbn/core-logging-server-mocks'; import { appContextService } from '../../app_context'; -import { getBundledPackageByPkgKey, getBundledPackages } from './bundled_packages'; +import { + getBundledPackageByPkgKey, + getBundledPackages, + _purgeBundledPackagesCache, +} from './bundled_packages'; jest.mock('fs/promises'); jest.mock('../../app_context'); describe('bundledPackages', () => { - beforeAll(() => { + beforeEach(() => { jest.mocked(appContextService.getConfig).mockReturnValue({ developer: { bundledPackageLocation: '/tmp/test', }, } as any); jest.mocked(appContextService.getLogger).mockReturnValue(loggingSystemMock.createLogger()); - }); - beforeEach(() => { + _purgeBundledPackagesCache(); jest.mocked(fs.stat).mockResolvedValue({} as any); - jest.mocked(fs.readdir).mockResolvedValue(['apm-8.8.0.zip', 'test-1.0.0.zip'] as any); - jest.mocked(fs.readFile).mockResolvedValue(Buffer.from('TEST')); + jest + .mocked(fs.readdir) + .mockReset() + .mockResolvedValue(['apm-8.8.0.zip', 'test-1.0.0.zip'] as any); + + jest.mocked(fs.readFile).mockReset().mockResolvedValue(Buffer.from('TEST')); }); afterEach(() => { @@ -44,17 +53,47 @@ describe('bundledPackages', () => { it('return packages in bundled directory', async () => { const packages = await getBundledPackages(); expect(packages).toEqual([ - { + expect.objectContaining({ name: 'apm', version: '8.8.0', - buffer: Buffer.from('TEST'), - }, - { + }), + expect.objectContaining({ name: 'test', version: '1.0.0', - buffer: Buffer.from('TEST'), - }, + }), ]); + + expect(await packages[0]?.getBuffer()).toEqual(Buffer.from('TEST')); + expect(await packages[1]?.getBuffer()).toEqual(Buffer.from('TEST')); + }); + + it('should use cache if called multiple time', async () => { + const packagesRes1 = await getBundledPackages(); + const packagesRes2 = await getBundledPackages(); + expect(packagesRes1.map((p) => omit(p, 'getBuffer'))).toEqual( + packagesRes2.map((p) => omit(p, 'getBuffer')) + ); + expect(fs.readdir).toBeCalledTimes(1); + }); + + it('should cache getBuffer if called multiple time in the scope of getBundledPackages', async () => { + const packagesRes1 = await getBundledPackages(); + + await packagesRes1[0].getBuffer(); + await packagesRes1[0].getBuffer(); + expect(fs.readFile).toBeCalledTimes(1); + }); + + it('should not use cache if called multiple time and cache is disabled', async () => { + jest.mocked(appContextService.getConfig).mockReturnValue({ + developer: { + bundledPackageLocation: '/tmp/test', + disableBundledPackagesCache: true, + }, + } as any); + await getBundledPackages(); + await getBundledPackages(); + expect(fs.readdir).toBeCalledTimes(2); }); }); describe('getBundledPackageByPkgKey', () => { @@ -62,22 +101,28 @@ describe('bundledPackages', () => { const pkg = await getBundledPackageByPkgKey('apm'); expect(pkg).toBeDefined(); - expect(pkg).toEqual({ - name: 'apm', - version: '8.8.0', - buffer: Buffer.from('TEST'), - }); + expect(pkg).toEqual( + expect.objectContaining({ + name: 'apm', + version: '8.8.0', + }) + ); + + expect(await pkg?.getBuffer()).toEqual(Buffer.from('TEST')); }); it('should return package by name and version if version is provided', async () => { const pkg = await getBundledPackageByPkgKey('apm-8.8.0'); expect(pkg).toBeDefined(); - expect(pkg).toEqual({ - name: 'apm', - version: '8.8.0', - buffer: Buffer.from('TEST'), - }); + expect(pkg).toEqual( + expect.objectContaining({ + name: 'apm', + version: '8.8.0', + }) + ); + + expect(await pkg?.getBuffer()).toEqual(Buffer.from('TEST')); }); it('should return package by name and version if version is provided and do not exists', async () => { diff --git a/x-pack/plugins/fleet/server/services/epm/packages/bundled_packages.ts b/x-pack/plugins/fleet/server/services/epm/packages/bundled_packages.ts index 92f9674656103..9a6b09bc3e49e 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/bundled_packages.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/bundled_packages.ts @@ -8,13 +8,36 @@ import fs from 'fs/promises'; import path from 'path'; +import { once } from 'lodash'; + import type { BundledPackage, Installation } from '../../../types'; import { BundledPackageLocationNotFoundError } from '../../../errors'; import { appContextService } from '../../app_context'; import { splitPkgKey, pkgToPkgKey } from '../registry'; +let CACHE_BUNDLED_PACKAGES: BundledPackage[] | undefined; + +export function _purgeBundledPackagesCache() { + CACHE_BUNDLED_PACKAGES = undefined; +} + +function bundledPackagesFromCache() { + if (!CACHE_BUNDLED_PACKAGES) { + throw new Error('CACHE_BUNDLED_PACKAGES is not populated'); + } + + return CACHE_BUNDLED_PACKAGES.map(({ name, version, getBuffer }) => ({ + name, + version, + getBuffer: once(getBuffer), + })); +} + export async function getBundledPackages(): Promise { const config = appContextService.getConfig(); + if (config?.developer?.disableBundledPackagesCache !== true && CACHE_BUNDLED_PACKAGES) { + return bundledPackagesFromCache(); + } const bundledPackageLocation = config?.developer?.bundledPackageLocation; @@ -38,19 +61,21 @@ export async function getBundledPackages(): Promise { const result = await Promise.all( zipFiles.map(async (zipFile) => { - const file = await fs.readFile(path.join(bundledPackageLocation, zipFile)); - const { pkgName, pkgVersion } = splitPkgKey(zipFile.replace(/\.zip$/, '')); + const getBuffer = () => fs.readFile(path.join(bundledPackageLocation, zipFile)); + return { name: pkgName, version: pkgVersion, - buffer: file, + getBuffer, }; }) ); - return result; + CACHE_BUNDLED_PACKAGES = result; + + return bundledPackagesFromCache(); } catch (err) { const logger = appContextService.getLogger(); logger.warn(`Unable to read bundled packages from ${bundledPackageLocation}`); diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts b/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts index bc3e138b5619c..bfb99d0f17980 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.test.ts @@ -186,7 +186,7 @@ describe('install', () => { expect(sendTelemetryEvents).toHaveBeenCalledWith(expect.anything(), undefined, { currentVersion: 'not_installed', dryRun: false, - errorMessage: 'Requires basic license', + errorMessage: 'Installation requires basic license', eventType: 'package-install', installType: 'install', newVersion: '1.3.0', @@ -269,7 +269,7 @@ describe('install', () => { mockGetBundledPackageByPkgKey.mockResolvedValue({ name: 'test_package', version: '1.0.0', - buffer: Buffer.from('test_package'), + getBuffer: async () => Buffer.from('test_package'), }); const response = await installPackage({ diff --git a/x-pack/plugins/fleet/server/services/epm/packages/install.ts b/x-pack/plugins/fleet/server/services/epm/packages/install.ts index 2dbf1662d4364..33f1b3a6ab73e 100644 --- a/x-pack/plugins/fleet/server/services/epm/packages/install.ts +++ b/x-pack/plugins/fleet/server/services/epm/packages/install.ts @@ -516,6 +516,7 @@ async function installPackageCommon(options: { } = options; let { telemetryEvent } = options; const logger = appContextService.getLogger(); + logger.info(`Install - Starting installation of ${pkgName}@${pkgVersion} from ${installSource} `); // Workaround apm issue with async spans: https://github.com/elastic/apm-agent-nodejs/issues/2611 await Promise.resolve(); @@ -564,7 +565,8 @@ async function installPackageCommon(options: { } const elasticSubscription = getElasticSubscription(packageInfo); if (!licenseService.hasAtLeast(elasticSubscription)) { - const err = new Error(`Requires ${elasticSubscription} license`); + logger.error(`Installation requires ${elasticSubscription} license`); + const err = new FleetError(`Installation requires ${elasticSubscription} license`); sendEvent({ ...telemetryEvent, errorMessage: err.message, @@ -606,6 +608,7 @@ async function installPackageCommon(options: { skipDataStreamRollover, }) .then(async (assets) => { + logger.debug(`Removing old assets from previous versions of ${pkgName}`); await removeOldAssets({ soClient: savedObjectsClient, pkgName: packageInfo.name, @@ -759,13 +762,15 @@ export async function installPackage(args: InstallPackageParams): Promise { mockGetBundledPackageByName.mockResolvedValueOnce({ name: 'test-package', version: '1.0.0', - buffer: Buffer.from(''), + getBuffer: async () => Buffer.from(''), }); MockArchive.generatePackageInfoFromArchiveBuffer.mockResolvedValueOnce({ paths: [], 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 24c81dd023244..de513062f5873 100644 --- a/x-pack/plugins/fleet/server/services/epm/registry/index.ts +++ b/x-pack/plugins/fleet/server/services/epm/registry/index.ts @@ -198,8 +198,9 @@ export async function getBundledArchive( const bundledPackage = await getBundledPackageByName(pkgName); if (bundledPackage && bundledPackage.version === pkgVersion) { + const archiveBuffer = await bundledPackage.getBuffer(); const archivePackage = await generatePackageInfoFromArchiveBuffer( - bundledPackage.buffer, + archiveBuffer, 'application/zip' ); diff --git a/x-pack/plugins/fleet/server/services/files/utils.ts b/x-pack/plugins/fleet/server/services/files/utils.ts index 281f14cd7e30b..809568e6cf7ae 100644 --- a/x-pack/plugins/fleet/server/services/files/utils.ts +++ b/x-pack/plugins/fleet/server/services/files/utils.ts @@ -14,6 +14,7 @@ import { FILE_STORAGE_TO_HOST_METADATA_INDEX_PATTERN, } from '../../../common/constants'; +import { FleetError } from '../../errors'; interface ParsedFileStorageIndex { index: string; integration: string; @@ -55,7 +56,7 @@ export const parseFileStorageIndex = (index: string): ParsedFileStorageIndex => .at(integrationPosition); if (!integration) { - throw new Error(`Index name ${index} does not seem to be a File storage index`); + throw new FleetError(`Index name ${index} does not seem to be a File storage index`); } response.direction = isDeliveryToHost ? 'to-host' : 'from-host'; @@ -69,7 +70,7 @@ export const parseFileStorageIndex = (index: string): ParsedFileStorageIndex => } } - throw new Error( + throw new FleetError( `Unable to parse index [${index}]. Does not match a known index pattern: [${fileStorageIndexPatterns.join( ' | ' )}]` diff --git a/x-pack/plugins/fleet/server/services/fleet_proxies.test.ts b/x-pack/plugins/fleet/server/services/fleet_proxies.test.ts index 89801b66b49c1..730b368495cb6 100644 --- a/x-pack/plugins/fleet/server/services/fleet_proxies.test.ts +++ b/x-pack/plugins/fleet/server/services/fleet_proxies.test.ts @@ -4,11 +4,16 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { loggerMock } from '@kbn/logging-mocks'; +import type { Logger } from '@kbn/core/server'; +import { securityMock } from '@kbn/security-plugin/server/mocks'; import { savedObjectsClientMock, elasticsearchServiceMock } from '@kbn/core/server/mocks'; import { FLEET_PROXY_SAVED_OBJECT_TYPE } from '../constants'; +import { appContextService } from './app_context'; + import { deleteFleetProxy } from './fleet_proxies'; import { listFleetServerHostsForProxyId, updateFleetServerHost } from './fleet_server_host'; import { outputService } from './output'; @@ -17,6 +22,7 @@ import { downloadSourceService } from './download_source'; jest.mock('./output'); jest.mock('./download_source'); jest.mock('./fleet_server_host'); +jest.mock('./app_context'); const mockedListFleetServerHostsForProxyId = listFleetServerHostsForProxyId as jest.MockedFunction< typeof listFleetServerHostsForProxyId @@ -35,8 +41,19 @@ const PROXY_IDS = { PRECONFIGURED: 'test-preconfigured', RELATED_PRECONFIGURED: 'test-related-preconfigured', }; +const mockedAppContextService = appContextService as jest.Mocked; +mockedAppContextService.getSecuritySetup.mockImplementation(() => ({ + ...securityMock.createSetup(), +})); + +let mockedLogger: jest.Mocked; describe('Fleet proxies service', () => { + beforeEach(() => { + mockedLogger = loggerMock.create(); + mockedAppContextService.getLogger.mockReturnValue(mockedLogger); + }); + const soClientMock = savedObjectsClientMock.create(); const esClientMock = elasticsearchServiceMock.createElasticsearchClient(); diff --git a/x-pack/plugins/fleet/server/services/fleet_proxies.ts b/x-pack/plugins/fleet/server/services/fleet_proxies.ts index cf45b90804c22..61aa07b8e0614 100644 --- a/x-pack/plugins/fleet/server/services/fleet_proxies.ts +++ b/x-pack/plugins/fleet/server/services/fleet_proxies.ts @@ -24,6 +24,8 @@ import type { Output, } from '../types'; +import { appContextService } from './app_context'; + import { listFleetServerHostsForProxyId, updateFleetServerHost } from './fleet_server_host'; import { outputService } from './output'; import { downloadSourceService } from './download_source'; @@ -70,6 +72,9 @@ export async function createFleetProxy( data: NewFleetProxy, options?: { id?: string; overwrite?: boolean; fromPreconfiguration?: boolean } ): Promise { + const logger = appContextService.getLogger(); + logger.debug(`Creating fleet proxy ${data}`); + const res = await soClient.create( FLEET_PROXY_SAVED_OBJECT_TYPE, fleetProxyDataToSOAttribute(data), @@ -78,7 +83,7 @@ export async function createFleetProxy( overwrite: options?.overwrite, } ); - + logger.debug(`Created fleet proxy ${options?.id}`); return savedObjectToFleetProxy(res); } @@ -97,6 +102,9 @@ export async function deleteFleetProxy( id: string, options?: { fromPreconfiguration?: boolean } ) { + const logger = appContextService.getLogger(); + logger.debug(`Deleting fleet proxy ${id}`); + const fleetProxy = await getFleetProxy(soClient, id); if (fleetProxy.is_preconfigured && !options?.fromPreconfiguration) { @@ -120,6 +128,7 @@ export async function deleteFleetProxy( } await updateRelatedSavedObject(soClient, esClient, fleetServerHosts, outputs, downloadSources); + logger.debug(`Deleted fleet proxy ${id}`); return await soClient.delete(FLEET_PROXY_SAVED_OBJECT_TYPE, id); } @@ -130,6 +139,8 @@ export async function updateFleetProxy( data: Partial, options?: { fromPreconfiguration?: boolean } ) { + const logger = appContextService.getLogger(); + logger.debug(`Updating fleet proxy ${id}`); const originalItem = await getFleetProxy(soClient, id); if (data.is_preconfigured && !options?.fromPreconfiguration) { @@ -141,7 +152,7 @@ export async function updateFleetProxy( id, fleetProxyDataToSOAttribute(data) ); - + logger.debug(`Updated fleet proxy ${id}`); return { ...originalItem, ...data, diff --git a/x-pack/plugins/fleet/server/services/fleet_server_host.test.ts b/x-pack/plugins/fleet/server/services/fleet_server_host.test.ts index 40f65ca21c5ef..f92261ffb9f8e 100644 --- a/x-pack/plugins/fleet/server/services/fleet_server_host.test.ts +++ b/x-pack/plugins/fleet/server/services/fleet_server_host.test.ts @@ -6,6 +6,10 @@ */ import { savedObjectsClientMock } from '@kbn/core/server/mocks'; +import { loggerMock } from '@kbn/logging-mocks'; + +import type { Logger } from '@kbn/core/server'; +import { securityMock } from '@kbn/security-plugin/server/mocks'; import { GLOBAL_SETTINGS_SAVED_OBJECT_TYPE, @@ -13,9 +17,25 @@ import { DEFAULT_FLEET_SERVER_HOST_ID, } from '../constants'; +import { appContextService } from './app_context'; + import { migrateSettingsToFleetServerHost } from './fleet_server_host'; +jest.mock('./app_context'); + +const mockedAppContextService = appContextService as jest.Mocked; +mockedAppContextService.getSecuritySetup.mockImplementation(() => ({ + ...securityMock.createSetup(), +})); + +let mockedLogger: jest.Mocked; + describe('migrateSettingsToFleetServerHost', () => { + beforeEach(() => { + mockedLogger = loggerMock.create(); + mockedAppContextService.getLogger.mockReturnValue(mockedLogger); + }); + it('should not migrate settings if a default fleet server policy config exists', async () => { const soClient = savedObjectsClientMock.create(); soClient.find.mockImplementation(({ type }) => { diff --git a/x-pack/plugins/fleet/server/services/fleet_server_host.ts b/x-pack/plugins/fleet/server/services/fleet_server_host.ts index 156d7e478b02b..66bed0b61977e 100644 --- a/x-pack/plugins/fleet/server/services/fleet_server_host.ts +++ b/x-pack/plugins/fleet/server/services/fleet_server_host.ts @@ -26,7 +26,9 @@ import type { NewFleetServerHost, AgentPolicy, } from '../types'; -import { FleetServerHostUnauthorizedError } from '../errors'; +import { FleetServerHostUnauthorizedError, FleetServerHostNotFoundError } from '../errors'; + +import { appContextService } from './app_context'; import { agentPolicyService } from './agent_policy'; import { escapeSearchQueryPhrase } from './saved_object'; @@ -46,6 +48,7 @@ export async function createFleetServerHost( data: NewFleetServerHost, options?: { id?: string; overwrite?: boolean; fromPreconfiguration?: boolean } ): Promise { + const logger = appContextService.getLogger(); if (data.is_default) { const defaultItem = await getDefaultFleetServerHost(soClient); if (defaultItem && defaultItem.id !== options?.id) { @@ -61,13 +64,13 @@ export async function createFleetServerHost( if (data.host_urls) { data.host_urls = data.host_urls.map(normalizeHostsForAgents); } - + logger.debug(`Creating fleet server host with ${data}`); const res = await soClient.create( FLEET_SERVER_HOST_SAVED_OBJECT_TYPE, data, { id: options?.id, overwrite: options?.overwrite } ); - + logger.debug(`Created fleet server host ${options?.id}`); return savedObjectToFleetServerHost(res); } @@ -122,6 +125,9 @@ export async function deleteFleetServerHost( id: string, options?: { fromPreconfiguration?: boolean } ) { + const logger = appContextService.getLogger(); + logger.debug(`Deleting fleet server host ${id}`); + const fleetServerHost = await getFleetServerHost(soClient, id); if (fleetServerHost.is_preconfigured && !options?.fromPreconfiguration) { @@ -147,6 +153,9 @@ export async function updateFleetServerHost( data: Partial, options?: { fromPreconfiguration?: boolean } ) { + const logger = appContextService.getLogger(); + logger.debug(`Updating fleet server host ${id}`); + const originalItem = await getFleetServerHost(soClient, id); if (data.is_preconfigured && !options?.fromPreconfiguration) { @@ -174,7 +183,7 @@ export async function updateFleetServerHost( } await soClient.update(FLEET_SERVER_HOST_SAVED_OBJECT_TYPE, id, data); - + logger.debug(`Updated fleet server host ${id}`); return { ...originalItem, ...data, @@ -224,7 +233,7 @@ export async function getFleetServerHostsForAgentPolicy( const defaultFleetServerHost = await getDefaultFleetServerHost(soClient); if (!defaultFleetServerHost) { - throw new Error('Default Fleet Server host is not setup'); + throw new FleetServerHostNotFoundError('Default Fleet Server host is not setup'); } return defaultFleetServerHost; diff --git a/x-pack/plugins/fleet/server/services/output.test.ts b/x-pack/plugins/fleet/server/services/output.test.ts index 6de778e9fd48a..8c2fee196d8d8 100644 --- a/x-pack/plugins/fleet/server/services/output.test.ts +++ b/x-pack/plugins/fleet/server/services/output.test.ts @@ -11,6 +11,8 @@ import { securityMock } from '@kbn/security-plugin/server/mocks'; import type { Logger } from '@kbn/logging'; +import { RESERVED_CONFIG_YML_KEYS } from '../../common/constants'; + import type { OutputSOAttributes } from '../types'; import { OUTPUT_SAVED_OBJECT_TYPE } from '../constants'; @@ -703,12 +705,12 @@ describe('Output Service', () => { ); }); - it('should throw when a remote es output is attempted to be created as default data output', async () => { + it('should not throw when a remote es output is attempted to be created as default data output', async () => { const soClient = getMockedSoClient({ defaultOutputId: 'output-test', }); - await expect( + expect( outputService.create( soClient, esClientMock, @@ -720,9 +722,123 @@ describe('Output Service', () => { }, { id: 'output-1' } ) + ).resolves.not.toThrow(); + }); + + it('should set preset: balanced by default when creating a new ES output', async () => { + const soClient = getMockedSoClient({}); + + await outputService.create( + soClient, + esClientMock, + { + is_default: false, + is_default_monitoring: false, + name: 'Test', + type: 'elasticsearch', + }, + { + id: 'output-1', + } + ); + + expect(soClient.create).toBeCalledWith( + OUTPUT_SAVED_OBJECT_TYPE, + // Preset should be inferred as balanced if not provided + expect.objectContaining({ + preset: 'balanced', + }), + expect.anything() + ); + }); + + it('should set preset: custom when config_yaml contains a reserved key', async () => { + const soClient = getMockedSoClient({}); + + await outputService.create( + soClient, + esClientMock, + { + is_default: false, + is_default_monitoring: false, + name: 'Test', + type: 'elasticsearch', + config_yaml: ` + bulk_max_size: 1000 + `, + }, + { + id: 'output-1', + } + ); + + expect(soClient.create).toBeCalledWith( + OUTPUT_SAVED_OBJECT_TYPE, + expect.objectContaining({ + preset: 'custom', + }), + expect.anything() + ); + }); + + it('should honor preset: custom in attributes', async () => { + const soClient = getMockedSoClient({}); + + await outputService.create( + soClient, + esClientMock, + { + is_default: false, + is_default_monitoring: false, + name: 'Test', + type: 'elasticsearch', + config_yaml: ` + some_non_reserved_key: foo + `, + preset: 'custom', + }, + { + id: 'output-1', + } + ); + + expect(soClient.create).toBeCalledWith( + OUTPUT_SAVED_OBJECT_TYPE, + expect.objectContaining({ + preset: 'custom', + }), + expect.anything() + ); + }); + + it('should throw an error when preset: balanced is provided but config_yaml contains a reserved key', async () => { + const soClient = getMockedSoClient({}); + + expect( + outputService.create( + soClient, + esClientMock, + { + is_default: false, + is_default_monitoring: false, + name: 'Test', + type: 'elasticsearch', + config_yaml: ` + bulk_max_size: 1000 + `, + preset: 'balanced', + }, + { + id: 'output-1', + } + ) ).rejects.toThrow( - `Remote elasticsearch output cannot be set as default output for integration data. Please set "is_default" to false.` + `preset cannot be balanced when config_yaml contains one of ${RESERVED_CONFIG_YML_KEYS.join( + ', ' + )}` ); + + expect(soClient.create).not.toBeCalled(); }); }); @@ -931,6 +1047,7 @@ describe('Output Service', () => { type: 'elasticsearch', hosts: ['http://test:4343'], ssl: null, + preset: 'balanced', }); }); @@ -969,6 +1086,7 @@ describe('Output Service', () => { headers: null, username: null, version: null, + preset: 'balanced', }); }); @@ -1524,21 +1642,19 @@ describe('Output Service', () => { ); }); - it('should throw when a remote es output is attempted to be updated as default data output', async () => { + it('should not throw when a remote es output is attempted to be updated as default data output', async () => { const soClient = getMockedSoClient({ defaultOutputId: 'output-test', }); - await expect( + expect( outputService.update(soClient, esClientMock, 'output-test', { is_default: true, is_default_monitoring: false, name: 'Test', type: 'remote_elasticsearch', }) - ).rejects.toThrow( - `Remote elasticsearch output cannot be set as default output for integration data. Please set "is_default" to false.` - ); + ).resolves.not.toThrow(); }); }); @@ -1681,7 +1797,7 @@ describe('Output Service', () => { }); describe('getLatestOutputHealth', () => { - it('should return unkown state if no hits', async () => { + it('should return unknown state if no hits', async () => { esClientMock.search.mockResolvedValue({ hits: { hits: [], @@ -1691,7 +1807,7 @@ describe('Output Service', () => { const response = await outputService.getLatestOutputHealth(esClientMock, 'id'); expect(response).toEqual({ - state: 'UNKOWN', + state: 'UNKNOWN', message: '', timestamp: '', }); @@ -1721,4 +1837,66 @@ describe('Output Service', () => { }); }); }); + + describe('backfillAllOutputPresets', () => { + it('should update non-preconfigured output', async () => { + const soClient = getMockedSoClient({}); + + soClient.find.mockResolvedValue({ + saved_objects: [ + { + ...mockOutputSO('non-preconfigured-output', { + is_preconfigured: false, + type: 'elasticsearch', + }), + score: 0, + }, + ], + total: 1, + per_page: 1, + page: 1, + }); + + soClient.get.mockResolvedValue({ + ...mockOutputSO('non-preconfigured-output', { + is_preconfigured: false, + type: 'elasticsearch', + }), + }); + + const promise = outputService.backfillAllOutputPresets(soClient, esClientMock); + + await expect(promise).resolves.not.toThrow(); + }); + + it('should update preconfigured output', async () => { + const soClient = getMockedSoClient({}); + + soClient.find.mockResolvedValue({ + saved_objects: [ + { + ...mockOutputSO('preconfigured-output', { + is_preconfigured: true, + type: 'elasticsearch', + }), + score: 0, + }, + ], + total: 1, + per_page: 1, + page: 1, + }); + + soClient.get.mockResolvedValue({ + ...mockOutputSO('preconfigured-output', { + is_preconfigured: true, + type: 'elasticsearch', + }), + }); + + const promise = outputService.backfillAllOutputPresets(soClient, esClientMock); + + await expect(promise).resolves.not.toThrow(); + }); + }); }); diff --git a/x-pack/plugins/fleet/server/services/output.ts b/x-pack/plugins/fleet/server/services/output.ts index d4e55ea16ef2b..e73a070a54965 100644 --- a/x-pack/plugins/fleet/server/services/output.ts +++ b/x-pack/plugins/fleet/server/services/output.ts @@ -19,6 +19,14 @@ import { SavedObjectsUtils } from '@kbn/core/server'; import _ from 'lodash'; +import pMap from 'p-map'; + +import { + getDefaultPresetForEsOutput, + outputTypeSupportPresets, + outputYmlIncludesReservedPerformanceKey, +} from '../../common/services/output_helpers'; + import type { NewOutput, Output, @@ -42,12 +50,14 @@ import { kafkaPartitionType, kafkaCompressionType, kafkaAcknowledgeReliabilityLevel, + RESERVED_CONFIG_YML_KEYS, } from '../../common/constants'; import { normalizeHostsForAgents } from '../../common/services'; import { FleetEncryptedSavedObjectEncryptionKeyRequired, OutputInvalidError, OutputUnauthorizedError, + FleetError, } from '../errors'; import type { OutputType } from '../types'; @@ -427,14 +437,24 @@ class OutputService { secretHashes?: Record; } ): Promise { + const logger = appContextService.getLogger(); + logger.debug(`Creating new output`); + const data: OutputSOAttributes = { ...omit(output, ['ssl', 'secrets']) }; - if (output.type === outputType.RemoteElasticsearch) { - if (data.is_default) { + + if (outputTypeSupportPresets(data.type)) { + if ( + data.preset === 'balanced' && + outputYmlIncludesReservedPerformanceKey(output.config_yaml ?? '', safeLoad) + ) { throw new OutputInvalidError( - 'Remote elasticsearch output cannot be set as default output for integration data. Please set "is_default" to false.' + `preset cannot be balanced when config_yaml contains one of ${RESERVED_CONFIG_YML_KEYS.join( + ', ' + )}` ); } } + const defaultDataOutputId = await this.getDefaultDataOutputId(soClient); if (output.type === outputType.Logstash || output.type === outputType.Kafka) { @@ -500,6 +520,10 @@ class OutputService { data.shipper = null; } + if (!data.preset && data.type === outputType.Elasticsearch) { + data.preset = getDefaultPresetForEsOutput(data.config_yaml ?? '', safeLoad); + } + if (output.config_yaml) { const configJs = safeLoad(output.config_yaml); const isShipperDisabled = !configJs?.shipper || configJs?.shipper?.enabled === false; @@ -577,7 +601,7 @@ class OutputService { overwrite: options?.overwrite || options?.fromPreconfiguration, id, }); - + logger.debug(`Created new output ${id}`); return outputSavedObjectToOutput(newSo); } @@ -667,7 +691,7 @@ class OutputService { }); if (outputSO.error) { - throw new Error(outputSO.error.message); + throw new FleetError(outputSO.error.message); } return outputSavedObjectToOutput(outputSO); @@ -680,6 +704,9 @@ class OutputService { fromPreconfiguration: false, } ) { + const logger = appContextService.getLogger(); + logger.debug(`Deleting output ${id}`); + const originalOutput = await this.get(soClient, id); if (originalOutput.is_preconfigured && !fromPreconfiguration) { @@ -714,7 +741,7 @@ class OutputService { esClient: appContextService.getInternalUserESClient(), output: originalOutput, }); - + logger.debug(`Deleted output ${id}`); return soDeleteResult; } @@ -730,13 +757,8 @@ class OutputService { fromPreconfiguration: false, } ) { - if (data.type === outputType.RemoteElasticsearch) { - if (data.is_default) { - throw new OutputInvalidError( - 'Remote elasticsearch output cannot be set as default output for integration data. Please set "is_default" to false.' - ); - } - } + const logger = appContextService.getLogger(); + logger.debug(`Updating output ${id}`); let secretsToDelete: PolicySecretReference[] = []; const originalOutput = await this.get(soClient, id); @@ -752,6 +774,19 @@ class OutputService { } const updateData: Nullable> = { ...omit(data, ['ssl', 'secrets']) }; + + if (updateData.type && outputTypeSupportPresets(updateData.type)) { + if ( + updateData.preset === 'balanced' && + outputYmlIncludesReservedPerformanceKey(updateData.config_yaml ?? '', safeLoad) + ) { + throw new OutputInvalidError( + `preset cannot be balanced when config_yaml contains one of ${RESERVED_CONFIG_YML_KEYS.join( + ', ' + )}` + ); + } + } if (isOutputSecretsEnabled()) { const secretsRes = await extractAndUpdateOutputSecrets({ oldOutput: originalOutput, @@ -800,6 +835,10 @@ class OutputService { // If the output type changed if (data.type && data.type !== originalOutput.type) { + if (data.type === outputType.Elasticsearch && updateData.type === outputType.Elasticsearch) { + updateData.preset = null; + } + if (data.type !== outputType.Kafka && originalOutput.type === outputType.Kafka) { removeKafkaFields(updateData as Nullable); } @@ -924,6 +963,10 @@ class OutputService { updateData.hosts = updateData.hosts.map(normalizeHostsForAgents); } + if (!data.preset && data.type === outputType.Elasticsearch) { + updateData.preset = getDefaultPresetForEsOutput(data.config_yaml ?? '', safeLoad); + } + // Remove the shipper data if the shipper is not enabled from the yaml config if (!data.config_yaml && data.shipper) { updateData.shipper = null; @@ -950,30 +993,59 @@ class OutputService { ); if (outputSO.error) { - throw new Error(outputSO.error.message); + throw new FleetError(outputSO.error.message); } if (secretsToDelete.length) { try { await deleteSecrets({ esClient, ids: secretsToDelete.map((s) => s.id) }); } catch (err) { - appContextService - .getLogger() - .warn(`Error cleaning up secrets for output ${id}: ${err.message}`); + logger.warn(`Error cleaning up secrets for output ${id}: ${err.message}`); } } + logger.debug(`Updated output ${id}`); + } + + public async backfillAllOutputPresets( + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient + ) { + const outputs = await this.list(soClient); + + await pMap( + outputs.items.filter((output) => outputTypeSupportPresets(output.type) && !output.preset), + async (output) => { + const preset = getDefaultPresetForEsOutput(output.config_yaml ?? '', safeLoad); + + await outputService.update( + soClient, + esClient, + output.id, + { preset }, + { fromPreconfiguration: true } + ); + await agentPolicyService.bumpAllAgentPoliciesForOutput(soClient, esClient, output.id); + }, + { + concurrency: 5, + } + ); } async getLatestOutputHealth(esClient: ElasticsearchClient, id: string): Promise { - const response = await esClient.search({ - index: OUTPUT_HEALTH_DATA_STREAM, - query: { bool: { filter: { term: { output: id } } } }, - sort: { '@timestamp': 'desc' }, - size: 1, - }); - if (response.hits.hits.length === 0) { + const response = await esClient.search( + { + index: OUTPUT_HEALTH_DATA_STREAM, + query: { bool: { filter: { term: { output: id } } } }, + sort: { '@timestamp': 'desc' }, + size: 1, + }, + { ignore: [404] } + ); + + if (!response.hits || response.hits.hits.length === 0) { return { - state: 'UNKOWN', + state: 'UNKNOWN', message: '', timestamp: '', }; diff --git a/x-pack/plugins/fleet/server/services/package_policy.ts b/x-pack/plugins/fleet/server/services/package_policy.ts index 983fdd7e8d29b..d3d49a7f001fc 100644 --- a/x-pack/plugins/fleet/server/services/package_policy.ts +++ b/x-pack/plugins/fleet/server/services/package_policy.ts @@ -80,6 +80,9 @@ import { HostedAgentPolicyRestrictionRelatedError, FleetUnauthorizedError, PackagePolicyNameExistsError, + AgentPolicyNotFoundError, + InputNotFoundError, + StreamNotFoundError, } from '../errors'; import { NewPackagePolicySchema, PackagePolicySchema, UpdatePackagePolicySchema } from '../types'; import type { @@ -171,6 +174,8 @@ class PackagePolicyClientImpl implements PackagePolicyClient { const logger = appContextService.getLogger(); let secretReferences: PolicySecretReference[] | undefined; + logger.debug(`Creating new package policy`); + let enrichedPackagePolicy = await packagePolicyService.runExternalCallbacks( 'packagePolicyCreate', packagePolicy, @@ -300,6 +305,7 @@ class PackagePolicyClientImpl implements PackagePolicyClient { } const createdPackagePolicy = { id: newSo.id, version: newSo.version, ...newSo.attributes }; + logger.debug(`Created new package policy with id ${newSo.id} and version ${newSo.version}`); return packagePolicyService.runExternalCallbacks( 'packagePolicyPostCreate', @@ -351,6 +357,7 @@ class PackagePolicyClientImpl implements PackagePolicyClient { }> = []; const logger = appContextService.getLogger(); + logger.debug(`Starting bulk create of package policy`); const packagePoliciesWithIds = packagePolicies.map((p) => { if (!p.id) { @@ -420,7 +427,7 @@ class PackagePolicyClientImpl implements PackagePolicyClient { if (hasCreatedSO?.error && !hasFailed) { failedPolicies.push({ packagePolicy, - error: hasCreatedSO?.error ?? new Error('Failed to create package policy.'), + error: hasCreatedSO?.error ?? new FleetError('Failed to create package policy.'), }); } }); @@ -434,7 +441,7 @@ class PackagePolicyClientImpl implements PackagePolicyClient { }); } } - + logger.debug(`Created new package policies`); return { created: newSos.map((newSo) => ({ id: newSo.id, @@ -498,7 +505,7 @@ class PackagePolicyClientImpl implements PackagePolicyClient { } if (packagePolicySO.error) { - throw new Error(packagePolicySO.error.message); + throw new FleetError(packagePolicySO.error.message); } let experimentalFeatures: ExperimentalDataStreamFeature[] | undefined; @@ -587,7 +594,7 @@ class PackagePolicyClientImpl implements PackagePolicyClient { } else if (so.error.statusCode === 404) { throw new PackagePolicyNotFoundError(`Package policy ${so.id} not found`); } else { - throw new Error(so.error.message); + throw new FleetError(so.error.message); } } @@ -689,12 +696,14 @@ class PackagePolicyClientImpl implements PackagePolicyClient { id, savedObjectType: PACKAGE_POLICY_SAVED_OBJECT_TYPE, }); + const logger = appContextService.getLogger(); let enrichedPackagePolicy: UpdatePackagePolicy; let secretReferences: PolicySecretReference[] | undefined; let secretsToDelete: PolicySecretReference[] | undefined; try { + logger.debug(`Starting update of package policy ${id}`); enrichedPackagePolicy = await packagePolicyService.runExternalCallbacks( 'packagePolicyUpdate', packagePolicyUpdate, @@ -702,7 +711,6 @@ class PackagePolicyClientImpl implements PackagePolicyClient { esClient ); } catch (error) { - const logger = appContextService.getLogger(); logger.error(`An error occurred executing "packagePolicyUpdate" callback: ${error}`); logger.error(error); if (error.apiPassThrough) { @@ -718,7 +726,7 @@ class PackagePolicyClientImpl implements PackagePolicyClient { throw new PackagePolicyRestrictionRelatedError(`Cannot update package policy ${id}`); } if (!oldPackagePolicy) { - throw new Error('Package policy not found'); + throw new PackagePolicyNotFoundError('Package policy not found'); } if ( @@ -774,6 +782,7 @@ class PackagePolicyClientImpl implements PackagePolicyClient { packagePolicy: restOfPackagePolicy, }); + logger.debug(`Updating SO with revision ${oldPackagePolicy.revision + 1}`); await soClient.update( SAVED_OBJECT_TYPE, id, @@ -825,6 +834,7 @@ class PackagePolicyClientImpl implements PackagePolicyClient { } } // Bump revision of associated agent policy + logger.debug(`Bumping revision of associated agent policy ${packagePolicy.policy_id}`); const bumpPromise = agentPolicyService.bumpRevision( soClient, esClient, @@ -845,6 +855,7 @@ class PackagePolicyClientImpl implements PackagePolicyClient { await Promise.all([bumpPromise, assetRemovePromise, deleteSecretsPromise]); sendUpdatePackagePolicyTelemetryEvent(soClient, [packagePolicyUpdate], [oldPackagePolicy]); + logger.debug(`Package policy ${id} update completed`); return newPolicy; } @@ -874,7 +885,7 @@ class PackagePolicyClientImpl implements PackagePolicyClient { ); if (!oldPackagePolicies || oldPackagePolicies.length === 0) { - throw new Error('Package policy not found'); + throw new PackagePolicyNotFoundError('Package policy not found'); } const packageInfos = await getPackageInfoForPackagePolicies(packagePolicyUpdates, soClient); @@ -892,7 +903,7 @@ class PackagePolicyClientImpl implements PackagePolicyClient { const packagePolicy = { ...packagePolicyUpdate, name: packagePolicyUpdate.name.trim() }; const oldPackagePolicy = oldPackagePolicies.find((p) => p.id === id); if (!oldPackagePolicy) { - throw new Error('Package policy not found'); + throw new PackagePolicyNotFoundError('Package policy not found'); } let secretReferences: PolicySecretReference[] | undefined; @@ -1050,6 +1061,7 @@ class PackagePolicyClientImpl implements PackagePolicyClient { const result: PostDeletePackagePoliciesResponse = []; const logger = appContextService.getLogger(); + logger.debug(`Deleting package policies ${ids}`); const packagePolicies = await this.getByIDs(soClient, ids, { ignoreMissing: true }); if (!packagePolicies) { @@ -1194,6 +1206,7 @@ class PackagePolicyClientImpl implements PackagePolicyClient { context, request ); + logger.debug(`Deleted package policies ${ids}`); } catch (error) { logger.error(`An error occurred executing "packagePolicyPostDelete" callback: ${error}`); logger.error(error); @@ -1924,7 +1937,9 @@ async function _compilePackagePolicyInput( const packageInput = packageInputs.find((pkgInput) => pkgInput.type === input.type); if (!packageInput) { - throw new Error(`Input template not found, unable to find input type ${input.type}`); + throw new InputNotFoundError( + `Input template not found, unable to find input type ${input.type}` + ); } if (!packageInput.template_path) { return undefined; @@ -1935,7 +1950,9 @@ async function _compilePackagePolicyInput( ); if (!pkgInputTemplate || !pkgInputTemplate.buffer) { - throw new Error(`Unable to load input template at /agent/input/${packageInput.template_path!}`); + throw new InputNotFoundError( + `Unable to load input template at /agent/input/${packageInput.template_path!}` + ); } return compileTemplate( @@ -2019,7 +2036,7 @@ async function _compilePackageStream( const packageDataStreams = getNormalizedDataStreams(pkgInfo); if (!packageDataStreams) { - throw new Error('Stream template not found, no data streams'); + throw new StreamNotFoundError('Stream template not found, no data streams'); } const packageDataStream = packageDataStreams.find( @@ -2027,7 +2044,7 @@ async function _compilePackageStream( ); if (!packageDataStream) { - throw new Error( + throw new StreamNotFoundError( `Stream template not found, unable to find dataset ${stream.data_stream.dataset}` ); } @@ -2038,11 +2055,15 @@ async function _compilePackageStream( (pkgStream) => pkgStream.input === input.type ); if (!streamFromPkg) { - throw new Error(`Stream template not found, unable to find stream for input ${input.type}`); + throw new StreamNotFoundError( + `Stream template not found, unable to find stream for input ${input.type}` + ); } if (!streamFromPkg.template_path) { - throw new Error(`Stream template path not found for dataset ${stream.data_stream.dataset}`); + throw new StreamNotFoundError( + `Stream template path not found for dataset ${stream.data_stream.dataset}` + ); } const datasetPath = packageDataStream.path; @@ -2054,7 +2075,7 @@ async function _compilePackageStream( ); if (!pkgStreamTemplate || !pkgStreamTemplate.buffer) { - throw new Error( + throw new StreamNotFoundError( `Unable to load stream template ${streamFromPkg.template_path} for dataset ${stream.data_stream.dataset}` ); } @@ -2484,7 +2505,7 @@ async function validateIsNotHostedPolicy( const agentPolicy = await agentPolicyService.get(soClient, id, false); if (!agentPolicy) { - throw new Error('Agent policy not found'); + throw new AgentPolicyNotFoundError('Agent policy not found'); } if (agentPolicy.is_managed && !force) { diff --git a/x-pack/plugins/fleet/server/services/preconfiguration.test.ts b/x-pack/plugins/fleet/server/services/preconfiguration.test.ts index fbeb2aa50c896..8c150aabb5b89 100644 --- a/x-pack/plugins/fleet/server/services/preconfiguration.test.ts +++ b/x-pack/plugins/fleet/server/services/preconfiguration.test.ts @@ -790,13 +790,13 @@ describe('policy preconfiguration', () => { { name: 'test_package', version: '1.0.0', - buffer: Buffer.from('test_package'), + getBuffer: () => Promise.resolve(Buffer.from('test_package')), }, { name: 'test_package_2', version: '1.0.0', - buffer: Buffer.from('test_package_2'), + getBuffer: () => Promise.resolve(Buffer.from('test_package_2')), }, ]); @@ -834,7 +834,7 @@ describe('policy preconfiguration', () => { { name: 'test_package', version: '1.0.0', - buffer: Buffer.from('test_package'), + getBuffer: () => Promise.resolve(Buffer.from('test_package')), }, ]); @@ -875,7 +875,7 @@ describe('policy preconfiguration', () => { { name: 'test_package', version: '1.0.0', - buffer: Buffer.from('test_package'), + getBuffer: () => Promise.resolve(Buffer.from('test_package')), }, ]); diff --git a/x-pack/plugins/fleet/server/services/preconfiguration.ts b/x-pack/plugins/fleet/server/services/preconfiguration.ts index 73fc61cf0fab9..ad1e1c0ddb8b5 100644 --- a/x-pack/plugins/fleet/server/services/preconfiguration.ts +++ b/x-pack/plugins/fleet/server/services/preconfiguration.ts @@ -26,6 +26,8 @@ import type { PreconfigurationError } from '../../common/constants'; import { PRECONFIGURATION_LATEST_KEYWORD } from '../../common/constants'; import { PRECONFIGURATION_DELETION_RECORD_SAVED_OBJECT_TYPE } from '../constants'; +import { FleetError } from '../errors'; + import { escapeSearchQueryPhrase } from './saved_object'; import { pkgToPkgKey } from './epm/registry'; import { getInstallation, getPackageInfo } from './epm/packages'; @@ -67,7 +69,7 @@ export async function ensurePreconfiguredPackagesAndPolicies( .map(([, versions]) => versions.map((v) => pkgToPkgKey(v)).join(', ')) .join('; '); - throw new Error( + throw new FleetError( i18n.translate('xpack.fleet.preconfiguration.duplicatePackageError', { defaultMessage: 'Duplicate packages specified in configuration: {duplicateList}', values: { @@ -119,6 +121,7 @@ export async function ensurePreconfiguredPackagesAndPolicies( await ensurePackagesCompletedInstall(soClient, esClient); // Create policies specified in Kibana config + logger.debug(`Creating preconfigured policies`); const preconfiguredPolicies = await Promise.allSettled( policies.map(async (preconfiguredAgentPolicy) => { if (preconfiguredAgentPolicy.id) { @@ -140,7 +143,7 @@ export async function ensurePreconfiguredPackagesAndPolicies( !preconfiguredAgentPolicy.is_default && !preconfiguredAgentPolicy.is_default_fleet_server ) { - throw new Error( + throw new FleetError( i18n.translate('xpack.fleet.preconfiguration.missingIDError', { defaultMessage: '{agentPolicyName} is missing an `id` field. `id` is required, except for policies marked is_default or is_default_fleet_server.', @@ -221,7 +224,7 @@ export async function ensurePreconfiguredPackagesAndPolicies( const rejectedPackage = rejectedPackages.find((rp) => rp.package?.name === pkg.name); if (rejectedPackage) { - throw new Error( + throw new FleetError( i18n.translate('xpack.fleet.preconfiguration.packageRejectedError', { defaultMessage: `[{agentPolicyName}] could not be added. [{pkgName}] could not be installed due to error: [{errorMessage}]`, values: { @@ -232,8 +235,7 @@ export async function ensurePreconfiguredPackagesAndPolicies( }) ); } - - throw new Error( + throw new FleetError( i18n.translate('xpack.fleet.preconfiguration.packageMissingError', { defaultMessage: '[{agentPolicyName}] could not be added. [{pkgName}] is not installed, add [{pkgName}] to [{packagesConfigValue}] or remove it from [{packagePolicyName}].', @@ -257,7 +259,7 @@ export async function ensurePreconfiguredPackagesAndPolicies( packagePolicy.name === installablePackagePolicy.name ); }); - + logger.debug(`Adding preconfigured package policies ${packagePoliciesToAdd}`); const s = apm.startSpan('Add preconfigured package policies', 'preconfiguration'); await addPreconfiguredPolicyPackages( soClient, diff --git a/x-pack/plugins/fleet/server/services/preconfiguration/fleet_server_host.ts b/x-pack/plugins/fleet/server/services/preconfiguration/fleet_server_host.ts index 8c6b9680318d7..f622b1115e16e 100644 --- a/x-pack/plugins/fleet/server/services/preconfiguration/fleet_server_host.ts +++ b/x-pack/plugins/fleet/server/services/preconfiguration/fleet_server_host.ts @@ -11,6 +11,8 @@ import { normalizeHostsForAgents } from '../../../common/services'; import type { FleetConfigType } from '../../config'; import { DEFAULT_FLEET_SERVER_HOST_ID } from '../../constants'; +import { FleetError } from '../../errors'; + import type { FleetServerHost } from '../../types'; import { appContextService } from '../app_context'; import { @@ -63,7 +65,7 @@ export function getPreconfiguredFleetServerHostFromConfig(config?: FleetConfigTy ]); if (fleetServerHosts.filter((fleetServerHost) => fleetServerHost.is_default).length > 1) { - throw new Error('Only one default Fleet Server host is allowed'); + throw new FleetError('Only one default Fleet Server host is allowed'); } return fleetServerHosts; diff --git a/x-pack/plugins/fleet/server/services/preconfiguration/outputs.ts b/x-pack/plugins/fleet/server/services/preconfiguration/outputs.ts index 8b22ce1009df2..f73481ff402e3 100644 --- a/x-pack/plugins/fleet/server/services/preconfiguration/outputs.ts +++ b/x-pack/plugins/fleet/server/services/preconfiguration/outputs.ts @@ -385,6 +385,8 @@ async function isPreconfiguredOutputDifferentFromCurrent( isDifferent(existingOutput.config_yaml, preconfiguredOutput.config_yaml) || isDifferent(existingOutput.proxy_id, preconfiguredOutput.proxy_id) || isDifferent(existingOutput.allow_edit ?? [], preconfiguredOutput.allow_edit ?? []) || + (preconfiguredOutput.preset && + isDifferent(existingOutput.preset, preconfiguredOutput.preset)) || (await kafkaFieldsAreDifferent()) || (await logstashFieldsAreDifferent()) || (await remoteESFieldsAreDifferent()) diff --git a/x-pack/plugins/fleet/server/services/preconfiguration/reset_agent_policies.ts b/x-pack/plugins/fleet/server/services/preconfiguration/reset_agent_policies.ts index f32ec17e22eaf..87adf58e42667 100644 --- a/x-pack/plugins/fleet/server/services/preconfiguration/reset_agent_policies.ts +++ b/x-pack/plugins/fleet/server/services/preconfiguration/reset_agent_policies.ts @@ -22,6 +22,7 @@ import { packagePolicyService } from '../package_policy'; import { getAgentsByKuery, forceUnenrollAgent } from '../agents'; import { listEnrollmentApiKeys, deleteEnrollmentApiKey } from '../api_keys'; import type { AgentPolicy } from '../../types'; +import { AgentPolicyInvalidError } from '../../errors'; export async function resetPreconfiguredAgentPolicies( soClient: SavedObjectsClientContract, @@ -135,7 +136,7 @@ async function _deleteExistingData( throw err; }); if (policy && !policy.is_preconfigured) { - throw new Error('Invalid policy'); + throw new AgentPolicyInvalidError(`Invalid policy ${agentPolicyId}`); } if (policy) { existingPolicies = [policy]; diff --git a/x-pack/plugins/fleet/server/services/secrets.test.ts b/x-pack/plugins/fleet/server/services/secrets.test.ts index 3a46d90f557ec..8e92836e3fbf4 100644 --- a/x-pack/plugins/fleet/server/services/secrets.test.ts +++ b/x-pack/plugins/fleet/server/services/secrets.test.ts @@ -18,7 +18,7 @@ import { elasticsearchServiceMock } from '@kbn/core-elasticsearch-server-mocks'; import { createAppContextStartContractMock } from '../mocks'; -import type { NewPackagePolicy, PackageInfo } from '../types'; +import type { NewPackagePolicy, PackageInfo, PackagePolicy, UpdatePackagePolicy } from '../types'; import { appContextService } from './app_context'; import { @@ -26,6 +26,7 @@ import { diffSecretPaths, diffOutputSecretPaths, extractAndWriteSecrets, + extractAndUpdateSecrets, } from './secrets'; describe('secrets', () => { @@ -108,13 +109,13 @@ describe('secrets', () => { expect(getPolicySecretPaths(packagePolicy, mockIntegrationPackage)).toEqual([ { - path: 'vars.pkg-secret-1', + path: ['vars', 'pkg-secret-1'], value: { value: 'pkg-secret-1-val', }, }, { - path: 'vars.pkg-secret-2', + path: ['vars', 'pkg-secret-2'], value: { value: 'pkg-secret-2-val', }, @@ -133,7 +134,7 @@ describe('secrets', () => { expect(getPolicySecretPaths(packagePolicy, mockIntegrationPackage)).toEqual([ { - path: 'vars.pkg-secret-1', + path: ['vars', 'pkg-secret-1'], value: { value: 'pkg-secret-1-val', }, @@ -161,11 +162,11 @@ describe('secrets', () => { expect(getPolicySecretPaths(packagePolicy, mockIntegrationPackage)).toEqual([ { - path: 'inputs[0].vars.input-secret-1', + path: ['inputs', '0', 'vars', 'input-secret-1'], value: { value: 'input-secret-1-val' }, }, { - path: 'inputs[0].vars.input-secret-2', + path: ['inputs', '0', 'vars', 'input-secret-2'], value: { value: 'input-secret-2-val' }, }, ]); @@ -198,15 +199,140 @@ describe('secrets', () => { expect(getPolicySecretPaths(packagePolicy, mockIntegrationPackage)).toEqual([ { - path: 'inputs[0].streams[0].vars.stream-secret-1', + path: ['inputs', '0', 'streams', '0', 'vars', 'stream-secret-1'], value: { value: 'stream-secret-1-value' }, }, { - path: 'inputs[0].streams[0].vars.stream-secret-2', + path: ['inputs', '0', 'streams', '0', 'vars', 'stream-secret-2'], value: { value: 'stream-secret-2-value' }, }, ]); }); + + it('variables with dot notated names', () => { + const mockPackageWithDotNotatedVariables = { + name: 'mock-dot-package', + title: 'Mock dot package', + version: '0.0.0', + description: 'description', + type: 'integration', + status: 'not_installed', + vars: [ + { name: 'dot-notation.pkg-secret-1', type: 'text', secret: true }, + { name: 'dot-notation.pkg-secret-2', type: 'text', secret: true }, + ], + data_streams: [ + { + dataset: 'somedataset', + streams: [ + { + input: 'foo', + title: 'Foo', + vars: [ + { name: 'dot-notation.stream-secret-1', type: 'text', secret: true }, + { name: 'dot-notation.stream-secret-2', type: 'text', secret: true }, + ], + }, + ], + }, + ], + policy_templates: [ + { + name: 'pkgPolicy1', + title: 'Package policy 1', + description: 'test package policy', + inputs: [ + { + type: 'foo', + title: 'Foo', + vars: [ + { + default: 'foo-input-var-value', + name: 'dot-notation.foo-input-var-name', + type: 'text', + }, + { + name: 'dot-notation.input-secret-1', + type: 'text', + secret: true, + }, + { + name: 'dot-notation.input-secret-2', + type: 'text', + secret: true, + }, + { name: 'dot-notation.foo-input3-var-name', type: 'text', multi: true }, + ], + }, + ], + }, + ], + } as unknown as PackageInfo; + const policy = { + vars: { + 'dot-notation.pkg-secret-1': { + value: 'Package level secret 1', + }, + 'dot-notation.pkg-secret-2': { + value: 'Package level secret 2', + }, + }, + inputs: [ + { + type: 'foo', + policy_template: 'pkgPolicy1', + enabled: false, + vars: { + 'dot-notation.foo-input-var-name': { value: 'foo' }, + 'dot-notation.input-secret-1': { value: 'Input level secret 1' }, + 'dot-notation.input-secret-2': { value: 'Input level secret 2' }, + 'dot-notation.input3-var-name': { value: 'bar' }, + }, + streams: [ + { + data_stream: { type: 'foo', dataset: 'somedataset' }, + vars: { + 'dot-notation.stream-secret-1': { value: 'Stream secret 1' }, + 'dot-notation.stream-secret-2': { value: 'Stream secret 2' }, + }, + }, + ], + }, + ], + }; + + expect( + getPolicySecretPaths( + policy as unknown as NewPackagePolicy, + mockPackageWithDotNotatedVariables as unknown as PackageInfo + ) + ).toEqual([ + { + path: ['vars', 'dot-notation.pkg-secret-1'], + value: { value: 'Package level secret 1' }, + }, + { + path: ['vars', 'dot-notation.pkg-secret-2'], + value: { value: 'Package level secret 2' }, + }, + { + path: ['inputs', '0', 'vars', 'dot-notation.input-secret-1'], + value: { value: 'Input level secret 1' }, + }, + { + path: ['inputs', '0', 'vars', 'dot-notation.input-secret-2'], + value: { value: 'Input level secret 2' }, + }, + { + path: ['inputs', '0', 'streams', '0', 'vars', 'dot-notation.stream-secret-1'], + value: { value: 'Stream secret 1' }, + }, + { + path: ['inputs', '0', 'streams', '0', 'vars', 'dot-notation.stream-secret-2'], + value: { value: 'Stream secret 2' }, + }, + ]); + }); }); describe('integration package with multiple policy templates (e.g AWS)', () => { @@ -391,20 +517,20 @@ describe('secrets', () => { ) ).toEqual([ { - path: 'vars.secret_access_key', + path: ['vars', 'secret_access_key'], value: { value: 'my_secret_access_key', }, }, { - path: 'inputs[0].vars.password', + path: ['inputs', '0', 'vars', 'password'], value: { type: 'text', value: 'billing_input_password', }, }, { - path: 'inputs[0].streams[0].vars.password', + path: ['inputs', '0', 'streams', '0', 'vars', 'password'], value: { type: 'text', value: 'billing_stream_password', @@ -464,31 +590,31 @@ describe('secrets', () => { ) ).toEqual([ { - path: 'vars.secret_access_key', + path: ['vars', 'secret_access_key'], value: { value: 'my_secret_access_key', }, }, { - path: 'inputs[0].vars.password', + path: ['inputs', '0', 'vars', 'password'], value: { value: 'cloudtrail_httpjson_input_password', }, }, { - path: 'inputs[0].streams[0].vars.password', + path: ['inputs', '0', 'streams', '0', 'vars', 'password'], value: { value: 'cloudtrail_httpjson_stream_password', }, }, { - path: 'inputs[1].vars.password', + path: ['inputs', '1', 'vars', 'password'], value: { value: 'cloudtrail_s3_input_password', }, }, { - path: 'inputs[1].streams[0].vars.password', + path: ['inputs', '1', 'streams', '0', 'vars', 'password'], value: { value: 'cloudtrail_s3_stream_password', }, @@ -592,13 +718,13 @@ describe('secrets', () => { ) ).toEqual([ { - path: 'inputs[0].streams[0].vars.secret-1', + path: ['inputs', '0', 'streams', '0', 'vars', 'secret-1'], value: { value: 'secret-1-value', }, }, { - path: 'inputs[0].streams[0].vars.secret-2', + path: ['inputs', '0', 'streams', '0', 'vars', 'secret-2'], value: { value: 'secret-2-value', }, @@ -619,7 +745,7 @@ describe('secrets', () => { it('should return empty array if single secret not changed', () => { const paths = [ { - path: 'somepath', + path: ['somepath'], value: { value: { isSecretRef: true, @@ -637,7 +763,7 @@ describe('secrets', () => { it('should return empty array if multiple secrets not changed', () => { const paths = [ { - path: 'somepath', + path: ['somepath'], value: { value: { isSecretRef: true, @@ -646,7 +772,7 @@ describe('secrets', () => { }, }, { - path: 'somepath2', + path: ['somepath2'], value: { value: { isSecretRef: true, @@ -655,7 +781,7 @@ describe('secrets', () => { }, }, { - path: 'somepath3', + path: ['somepath3'], value: { value: { isSecretRef: true, @@ -674,7 +800,7 @@ describe('secrets', () => { it('single secret modified', () => { const paths1 = [ { - path: 'somepath1', + path: ['somepath1'], value: { value: { isSecretRef: true, @@ -683,7 +809,7 @@ describe('secrets', () => { }, }, { - path: 'somepath2', + path: ['somepath2'], value: { value: { isSecretRef: true, id: 'secret-2' }, }, @@ -693,7 +819,7 @@ describe('secrets', () => { const paths2 = [ paths1[0], { - path: 'somepath2', + path: ['somepath2'], value: { value: 'newvalue' }, }, ]; @@ -701,13 +827,13 @@ describe('secrets', () => { expect(diffSecretPaths(paths1, paths2)).toEqual({ toCreate: [ { - path: 'somepath2', + path: ['somepath2'], value: { value: 'newvalue' }, }, ], toDelete: [ { - path: 'somepath2', + path: ['somepath2'], value: { value: { isSecretRef: true, @@ -722,7 +848,7 @@ describe('secrets', () => { it('double secret modified', () => { const paths1 = [ { - path: 'somepath1', + path: ['somepath1'], value: { value: { isSecretRef: true, @@ -731,7 +857,7 @@ describe('secrets', () => { }, }, { - path: 'somepath2', + path: ['somepath2'], value: { value: { isSecretRef: true, @@ -743,11 +869,11 @@ describe('secrets', () => { const paths2 = [ { - path: 'somepath1', + path: ['somepath1'], value: { value: 'newvalue1' }, }, { - path: 'somepath2', + path: ['somepath2'], value: { value: 'newvalue2' }, }, ]; @@ -755,17 +881,17 @@ describe('secrets', () => { expect(diffSecretPaths(paths1, paths2)).toEqual({ toCreate: [ { - path: 'somepath1', + path: ['somepath1'], value: { value: 'newvalue1' }, }, { - path: 'somepath2', + path: ['somepath2'], value: { value: 'newvalue2' }, }, ], toDelete: [ { - path: 'somepath1', + path: ['somepath1'], value: { value: { isSecretRef: true, @@ -774,7 +900,7 @@ describe('secrets', () => { }, }, { - path: 'somepath2', + path: ['somepath2'], value: { value: { isSecretRef: true, @@ -790,7 +916,7 @@ describe('secrets', () => { it('single secret added', () => { const paths1 = [ { - path: 'somepath1', + path: ['somepath1'], value: { value: { isSecretRef: true, @@ -803,7 +929,7 @@ describe('secrets', () => { const paths2 = [ paths1[0], { - path: 'somepath2', + path: ['somepath2'], value: { value: 'newvalue' }, }, ]; @@ -811,7 +937,7 @@ describe('secrets', () => { expect(diffSecretPaths(paths1, paths2)).toEqual({ toCreate: [ { - path: 'somepath2', + path: ['somepath2'], value: { value: 'newvalue' }, }, ], @@ -844,6 +970,7 @@ describe('secrets', () => { vars: [ { name: 'pkg-secret-1', type: 'text', secret: true, required: true }, { name: 'pkg-secret-2', type: 'text', secret: true, required: false }, + { name: 'dot-notation.pkg-secret-3', type: 'text', secret: true, required: false }, ], data_streams: [ { @@ -852,6 +979,14 @@ describe('secrets', () => { { input: 'foo', title: 'Foo', + vars: [ + { + name: 'dot-notation.stream-secret-1', + type: 'text', + secret: true, + required: false, + }, + ], }, ], }, @@ -865,7 +1000,14 @@ describe('secrets', () => { { type: 'foo', title: 'Foo', - vars: [], + vars: [ + { + name: 'dot-notation.input-secret-1', + type: 'text', + secret: true, + required: false, + }, + ], }, ], }, @@ -918,6 +1060,302 @@ describe('secrets', () => { expect(result.secretReferences).toHaveLength(2); }); }); + + describe('when variable name uses dot notation', () => { + it('places variable at the right path', async () => { + const mockPackagePolicy = { + vars: { + 'dot-notation.pkg-secret-3': { + value: 'pkg-secret-3-val', + }, + }, + inputs: [ + { + type: 'foo', + vars: { + 'dot-notation.input-secret-1': { + value: 'dot-notation-input-secret-1-val', + }, + }, + streams: [ + { + data_stream: { type: 'foo', dataset: 'somedataset' }, + vars: { + 'dot-notation.stream-secret-1': { + value: 'dot-notation-stream-var-1-val', + }, + }, + }, + ], + }, + ], + } as unknown as NewPackagePolicy; + + const result = await extractAndWriteSecrets({ + packagePolicy: mockPackagePolicy, + packageInfo: mockIntegrationPackage, + esClient: esClientMock, + }); + + expect(esClientMock.transport.request).toHaveBeenCalledTimes(3); + expect(result.secretReferences).toHaveLength(3); + + expect(result.packagePolicy.vars!['dot-notation.pkg-secret-3'].value.id).toBeTruthy(); + expect(result.packagePolicy.vars!['dot-notation.pkg-secret-3'].value.isSecretRef).toBe( + true + ); + + expect( + result.packagePolicy.inputs[0].vars!['dot-notation.input-secret-1'].value.id + ).toBeTruthy(); + expect( + result.packagePolicy.inputs[0].vars!['dot-notation.input-secret-1'].value.isSecretRef + ).toBe(true); + + expect( + result.packagePolicy.inputs[0].streams[0].vars!['dot-notation.stream-secret-1'].value.id + ).toBeTruthy(); + expect( + result.packagePolicy.inputs[0].streams[0].vars!['dot-notation.stream-secret-1'].value + .isSecretRef + ).toBe(true); + }); + }); + }); + + describe('extractAndUpdateSecrets', () => { + const esClientMock = elasticsearchServiceMock.createInternalClient(); + + esClientMock.transport.request.mockImplementation(async (req) => { + return { + id: uuidv4(), + }; + }); + + beforeEach(() => { + esClientMock.transport.request.mockClear(); + }); + + const mockIntegrationPackage = { + name: 'mock-package', + title: 'Mock package', + version: '0.0.0', + description: 'description', + type: 'integration', + status: 'not_installed', + vars: [ + { name: 'pkg-secret-1', type: 'text', secret: true, required: true }, + { name: 'pkg-secret-2', type: 'text', secret: true, required: false }, + { name: 'dot-notation.pkg-secret-3', type: 'text', secret: true, required: false }, + ], + data_streams: [ + { + dataset: 'somedataset', + streams: [ + { + input: 'foo', + title: 'Foo', + vars: [ + { + name: 'dot-notation.stream-secret-1', + type: 'text', + secret: true, + required: false, + }, + ], + }, + ], + }, + ], + policy_templates: [ + { + name: 'pkgPolicy1', + title: 'Package policy 1', + description: 'test package policy', + inputs: [ + { + type: 'foo', + title: 'Foo', + vars: [ + { + name: 'dot-notation.input-secret-1', + type: 'text', + secret: true, + required: false, + }, + ], + }, + ], + }, + ], + } as unknown as PackageInfo; + + describe('when only required secret value is provided', () => { + it('returns single secret reference for required secret', async () => { + const oldPackagePolicy = { + vars: { + 'pkg-secret-1': { + value: 'pkg-secret-1-val', + }, + 'pkg-secret-2': {}, + }, + inputs: [], + } as unknown as PackagePolicy; + + const mockPackagePolicy = { + vars: { + 'pkg-secret-1': { + value: 'pkg-secret-1-val-update', + }, + 'pkg-secret-2': {}, + }, + inputs: [], + } as unknown as UpdatePackagePolicy; + + const result = await extractAndUpdateSecrets({ + oldPackagePolicy, + packagePolicyUpdate: mockPackagePolicy, + packageInfo: mockIntegrationPackage, + esClient: esClientMock, + }); + + expect(esClientMock.transport.request).toHaveBeenCalledTimes(1); + expect(result.secretReferences).toHaveLength(1); + expect((result.packagePolicyUpdate.vars as any)['pkg-secret-1'].value.isSecretRef).toEqual( + true + ); + expect((result.packagePolicyUpdate.vars as any)['pkg-secret-2'].value).toBeUndefined(); + }); + }); + + describe('when both required and optional secret values are provided', () => { + it('returns secret reference for both required and optional secret', async () => { + const oldPackagePolicy = { + vars: { + 'pkg-secret-1': { + value: 'pkg-secret-1-val', + }, + 'pkg-secret-2': { + value: { id: '1234', isSecretRef: true }, + }, + }, + inputs: [], + } as unknown as PackagePolicy; + + const mockPackagePolicy = { + vars: { + 'pkg-secret-1': { + value: 'pkg-secret-1-val-update', + }, + 'pkg-secret-2': { + value: 'pkg-secret-2-val-update', + }, + }, + inputs: [], + } as unknown as UpdatePackagePolicy; + + const result = await extractAndUpdateSecrets({ + oldPackagePolicy, + packagePolicyUpdate: mockPackagePolicy, + packageInfo: mockIntegrationPackage, + esClient: esClientMock, + }); + + expect(esClientMock.transport.request).toHaveBeenCalledTimes(2); + expect(result.secretReferences).toHaveLength(2); + expect((result.packagePolicyUpdate.vars as any)['pkg-secret-1'].value.isSecretRef).toEqual( + true + ); + expect((result.packagePolicyUpdate.vars as any)['pkg-secret-2'].value.isSecretRef).toEqual( + true + ); + }); + }); + + describe('when variable name uses dot notation', () => { + it('places variable at the right path', async () => { + const oldPackagePolicy = { + vars: { + 'dot-notation.pkg-secret-3': { + value: { id: 123, isSecretRef: true }, + }, + }, + inputs: [ + { + type: 'foo', + vars: { + 'dot-notation.input-secret-1': { + value: { id: 12234, isSecretRef: true }, + }, + }, + streams: [], + }, + ], + } as unknown as PackagePolicy; + + const updatedPackagePolicy = { + vars: { + 'dot-notation.pkg-secret-3': { + value: 'pkg-secret-3-val', + }, + }, + inputs: [ + { + type: 'foo', + vars: { + 'dot-notation.input-secret-1': { + value: 'dot-notation-input-secret-1-val', + }, + }, + streams: [ + { + data_stream: { type: 'foo', dataset: 'somedataset' }, + vars: { + 'dot-notation.stream-secret-1': { + value: 'dot-notation-stream-var-1-val', + }, + }, + }, + ], + }, + ], + } as unknown as UpdatePackagePolicy; + + const result = await extractAndUpdateSecrets({ + oldPackagePolicy, + packagePolicyUpdate: updatedPackagePolicy, + packageInfo: mockIntegrationPackage, + esClient: esClientMock, + }); + + expect(esClientMock.transport.request).toHaveBeenCalledTimes(3); + expect(result.secretReferences).toHaveLength(3); + + expect(result.packagePolicyUpdate.vars!['dot-notation.pkg-secret-3'].value.id).toBeTruthy(); + expect( + result.packagePolicyUpdate.vars!['dot-notation.pkg-secret-3'].value.isSecretRef + ).toBe(true); + + expect( + result.packagePolicyUpdate.inputs[0].vars!['dot-notation.input-secret-1'].value.id + ).toBeTruthy(); + expect( + result.packagePolicyUpdate.inputs[0].vars!['dot-notation.input-secret-1'].value + .isSecretRef + ).toBe(true); + + expect( + result.packagePolicyUpdate.inputs[0].streams[0].vars!['dot-notation.stream-secret-1'] + .value.id + ).toBeTruthy(); + expect( + result.packagePolicyUpdate.inputs[0].streams[0].vars!['dot-notation.stream-secret-1'] + .value.isSecretRef + ).toBe(true); + + expect(result.secretsToDelete).toHaveLength(2); + }); + }); }); }); diff --git a/x-pack/plugins/fleet/server/services/secrets.ts b/x-pack/plugins/fleet/server/services/secrets.ts index c425a8a9e172a..6f50b2cd8b73b 100644 --- a/x-pack/plugins/fleet/server/services/secrets.ts +++ b/x-pack/plugins/fleet/server/services/secrets.ts @@ -241,10 +241,7 @@ export async function extractAndWriteSecrets(opts: { values: secretsToCreate.map((secretPath) => secretPath.value.value), }); - const policyWithSecretRefs = JSON.parse(JSON.stringify(packagePolicy)); - secretsToCreate.forEach((secretPath, i) => { - set(policyWithSecretRefs, secretPath.path + '.value', toVarSecretRef(secrets[i].id)); - }); + const policyWithSecretRefs = getPolicyWithSecretReferences(secretPaths, secrets, packagePolicy); return { packagePolicy: policyWithSecretRefs, @@ -405,15 +402,18 @@ export async function extractAndUpdateSecrets(opts: { const { toCreate, toDelete, noChange } = diffSecretPaths(oldSecretPaths, updatedSecretPaths); + const secretsToCreate = toCreate.filter((secretPath) => !!secretPath.value.value); + const createdSecrets = await createSecrets({ esClient, - values: toCreate.map((secretPath) => secretPath.value.value), + values: secretsToCreate.map((secretPath) => secretPath.value.value), }); - const policyWithSecretRefs = JSON.parse(JSON.stringify(packagePolicyUpdate)); - toCreate.forEach((secretPath, i) => { - set(policyWithSecretRefs, secretPath.path + '.value', toVarSecretRef(createdSecrets[i].id)); - }); + const policyWithSecretRefs = getPolicyWithSecretReferences( + secretsToCreate, + createdSecrets, + packagePolicyUpdate + ); const secretReferences = [ ...noChange.map((secretPath) => ({ id: secretPath.value.value.id })), @@ -516,14 +516,14 @@ export function diffSecretPaths( const toCreate: SecretPath[] = []; const toDelete: SecretPath[] = []; const noChange: SecretPath[] = []; - const newPathsByPath = keyBy(newPaths, 'path'); + const newPathsByPath = keyBy(newPaths, (x) => x.path.join('.')); for (const oldPath of oldPaths) { - if (!newPathsByPath[oldPath.path]) { + if (!newPathsByPath[oldPath.path.join('.')]) { toDelete.push(oldPath); } - const newPath = newPathsByPath[oldPath.path]; + const newPath = newPathsByPath[oldPath.path.join('.')]; if (newPath && newPath.value.value) { const newValue = newPath.value?.value; if (!newValue?.isSecretRef) { @@ -532,7 +532,7 @@ export function diffSecretPaths( } else { noChange.push(newPath); } - delete newPathsByPath[oldPath.path]; + delete newPathsByPath[oldPath.path.join('.')]; } } @@ -654,7 +654,7 @@ function _getPackageLevelSecretPaths( if (packageSecretVarsByName[name]) { vars.push({ value: configEntry, - path: `vars.${name}`, + path: ['vars', name], }); } return vars; @@ -686,7 +686,7 @@ function _getInputSecretPaths( inputVars.forEach(([name, configEntry]) => { if (inputSecretVarDefsByPolicyTemplateAndType[inputKey]?.[name]) { currentInputVarPaths.push({ - path: `inputs[${inputIndex}].vars.${name}`, + path: ['inputs', inputIndex.toString(), 'vars', name], value: configEntry, }); } @@ -701,7 +701,14 @@ function _getInputSecretPaths( Object.entries(stream.vars || {}).forEach(([name, configEntry]) => { if (streamVarDefs[name]) { currentInputVarPaths.push({ - path: `inputs[${inputIndex}].streams[${streamIndex}].vars.${name}`, + path: [ + 'inputs', + inputIndex.toString(), + 'streams', + streamIndex.toString(), + 'vars', + name, + ], value: configEntry, }); } @@ -758,3 +765,34 @@ function _getInputSecretVarDefsByPolicyTemplateAndType(packageInfo: PackageInfo) {} ); } + +/** + * Given an array of secret paths, existing secrets, and a package policy, generates a + * new package policy object that includes resolved secret reference values at each + * provided path. + */ +function getPolicyWithSecretReferences( + secretPaths: SecretPath[], + secrets: Secret[], + packagePolicy: NewPackagePolicy +) { + const result = JSON.parse(JSON.stringify(packagePolicy)); + + secretPaths.forEach((secretPath, secretPathIndex) => { + secretPath.path.reduce((acc, val, secretPathComponentIndex) => { + if (!acc[val]) { + acc[val] = {}; + } + + const isLast = secretPathComponentIndex === secretPath.path.length - 1; + + if (isLast) { + acc[val].value = toVarSecretRef(secrets[secretPathIndex].id); + } + + return acc[val]; + }, result); + }); + + return result; +} diff --git a/x-pack/plugins/fleet/server/services/security/message_signing_service.ts b/x-pack/plugins/fleet/server/services/security/message_signing_service.ts index b3d08e9a0b8e9..741b9e33dec88 100644 --- a/x-pack/plugins/fleet/server/services/security/message_signing_service.ts +++ b/x-pack/plugins/fleet/server/services/security/message_signing_service.ts @@ -22,6 +22,7 @@ import { MessageSigningError } from '../../../common/errors'; import { MESSAGE_SIGNING_KEYS_SAVED_OBJECT_TYPE } from '../../constants'; import { appContextService } from '../app_context'; +import { SigningServiceNotFoundError } from '../../errors'; interface MessageSigningKeys { private_key: string; @@ -118,10 +119,10 @@ export class MessageSigningService implements MessageSigningServiceInterface { signer.end(); if (!serializedPrivateKey) { - throw new Error('unable to find private key'); + throw new SigningServiceNotFoundError('Unable to find private key'); } if (!passphrase) { - throw new Error('unable to find passphrase'); + throw new SigningServiceNotFoundError('Unable to find passphrase'); } const privateKey = Buffer.from(serializedPrivateKey, 'base64'); @@ -139,7 +140,7 @@ export class MessageSigningService implements MessageSigningServiceInterface { const { publicKey } = await this.generateKeyPair(); if (!publicKey) { - throw new Error('unable to find public key'); + throw new SigningServiceNotFoundError('Unable to find public key'); } return publicKey; diff --git a/x-pack/plugins/fleet/server/services/security/uninstall_token_service/index.ts b/x-pack/plugins/fleet/server/services/security/uninstall_token_service/index.ts index 7a2bdedffcdc8..330007e23963d 100644 --- a/x-pack/plugins/fleet/server/services/security/uninstall_token_service/index.ts +++ b/x-pack/plugins/fleet/server/services/security/uninstall_token_service/index.ts @@ -25,7 +25,10 @@ import type { KibanaRequest } from '@kbn/core-http-server'; import { SECURITY_EXTENSION_ID } from '@kbn/core-saved-objects-server'; import { asyncForEach } from '@kbn/std'; -import type { AggregationsTermsInclude } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { + AggregationsTermsInclude, + AggregationsTermsExclude, +} from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { UninstallTokenError } from '../../../../common/errors'; @@ -74,12 +77,14 @@ export interface UninstallTokenServiceInterface { * @param policyIdFilter a string for partial matching the policyId * @param page * @param perPage + * @param policyIdExcludeFilter * @returns Uninstall Tokens Metadata Response */ getTokenMetadata( policyIdFilter?: string, page?: number, - perPage?: number + perPage?: number, + policyIdExcludeFilter?: string ): Promise; /** @@ -170,11 +175,15 @@ export class UninstallTokenService implements UninstallTokenServiceInterface { public async getTokenMetadata( policyIdFilter?: string, page = 1, - perPage = 20 + perPage = 20, + policyIdExcludeFilter?: string ): Promise { const includeFilter = policyIdFilter ? `.*${policyIdFilter}.*` : undefined; - const tokenObjects = await this.getTokenObjectsByIncludeFilter(includeFilter); + const tokenObjects = await this.getTokenObjectsByIncludeFilter( + includeFilter, + policyIdExcludeFilter + ); const items: UninstallTokenMetadata[] = tokenObjects .slice((page - 1) * perPage, page * perPage) @@ -250,7 +259,8 @@ export class UninstallTokenService implements UninstallTokenServiceInterface { }; private async getTokenObjectsByIncludeFilter( - include?: AggregationsTermsInclude + include?: AggregationsTermsInclude, + exclude?: AggregationsTermsExclude ): Promise>> { const bucketSize = 10000; @@ -263,7 +273,7 @@ export class UninstallTokenService implements UninstallTokenServiceInterface { field: `${UNINSTALL_TOKENS_SAVED_OBJECT_TYPE}.attributes.policy_id`, size: bucketSize, include, - exclude: 'policy-elastic-agent-on-cloud', // todo: find a better way to not return or even generate token for managed policies + exclude, }, aggs: { latest: { diff --git a/x-pack/plugins/fleet/server/services/setup.ts b/x-pack/plugins/fleet/server/services/setup.ts index e0b3bdfea0bd3..cc4be9bab0bcc 100644 --- a/x-pack/plugins/fleet/server/services/setup.ts +++ b/x-pack/plugins/fleet/server/services/setup.ts @@ -125,12 +125,14 @@ async function createSetupSideEffects( esClient, getPreconfiguredOutputFromConfig(appContextService.getConfig()) ), - settingsService.settingsSetup(soClient), ]); const defaultOutput = await outputService.ensureDefaultOutput(soClient, esClient); + logger.debug('Backfilling output performance presets'); + await outputService.backfillAllOutputPresets(soClient, esClient); + logger.debug('Setting up Fleet Elasticsearch assets'); let stepSpan = apm.startSpan('Install Fleet global assets', 'preconfiguration'); await ensureFleetGlobalEsAssets(soClient, esClient); @@ -225,7 +227,7 @@ async function createSetupSideEffects( stepSpan?.end(); stepSpan = apm.startSpan('Set up enrollment keys for preconfigured policies', 'preconfiguration'); - logger.debug('Setting up Fleet enrollment keys'); + logger.debug('Setting up Fleet enrollment keys for preconfigured policies'); await ensureDefaultEnrollmentAPIKeysExists(soClient, esClient); stepSpan?.end(); diff --git a/x-pack/plugins/fleet/server/services/setup/clean_old_fleet_indices.tsx b/x-pack/plugins/fleet/server/services/setup/clean_old_fleet_indices.tsx index 856e3543cd962..cd3317c5eb23b 100644 --- a/x-pack/plugins/fleet/server/services/setup/clean_old_fleet_indices.tsx +++ b/x-pack/plugins/fleet/server/services/setup/clean_old_fleet_indices.tsx @@ -28,6 +28,7 @@ const INDEX_TEMPLATE_TO_CLEAN = [ export async function cleanUpOldFileIndices(esClient: ElasticsearchClient, logger: Logger) { try { // Clean indices + logger.info('Cleaning old indices'); await pMap( INDICES_TO_CLEAN, async (indiceToClean) => { diff --git a/x-pack/plugins/fleet/server/services/telemetry/fleet_usage_sender.ts b/x-pack/plugins/fleet/server/services/telemetry/fleet_usage_sender.ts index 4ee0a7dac4f89..1b90fe6d01e0b 100644 --- a/x-pack/plugins/fleet/server/services/telemetry/fleet_usage_sender.ts +++ b/x-pack/plugins/fleet/server/services/telemetry/fleet_usage_sender.ts @@ -24,7 +24,7 @@ const FLEET_AGENTS_EVENT_TYPE = 'fleet_agents'; export class FleetUsageSender { private taskManager?: TaskManagerStartContract; - private taskVersion = '1.1.3'; + private taskVersion = '1.1.4'; private taskType = 'Fleet-Usage-Sender'; private wasStarted: boolean = false; private interval = '1h'; @@ -83,6 +83,7 @@ export class FleetUsageSender { const { agents_per_version: agentsPerVersion, agents_per_output_type: agentsPerOutputType, + upgrade_details: upgradeDetails, ...fleetUsageData } = usageData; appContextService @@ -106,6 +107,13 @@ export class FleetUsageSender { agents_per_output_type: byOutputType, }); }); + + appContextService + .getLogger() + .debug('Agents upgrade details telemetry: ' + JSON.stringify(upgradeDetails)); + upgradeDetails.forEach((upgradeDetailsObj) => { + core.analytics.reportEvent(FLEET_AGENTS_EVENT_TYPE, { upgrade_details: upgradeDetailsObj }); + }); } catch (error) { appContextService .getLogger() diff --git a/x-pack/plugins/fleet/server/services/telemetry/fleet_usages_schema.ts b/x-pack/plugins/fleet/server/services/telemetry/fleet_usages_schema.ts index e59de684264bf..6bfbf1c794b52 100644 --- a/x-pack/plugins/fleet/server/services/telemetry/fleet_usages_schema.ts +++ b/x-pack/plugins/fleet/server/services/telemetry/fleet_usages_schema.ts @@ -76,6 +76,51 @@ export const fleetAgentsSchema: RootSchema = { description: 'Output type used by agent', }, }, + preset_counts: { + _meta: { + description: 'Count of agents per preset', + optional: true, + }, + properties: { + balanced: { + type: 'long', + _meta: { + description: 'Number of agents enrolled whose output uses the balanced preset', + }, + }, + custom: { + type: 'long', + _meta: { + description: 'Number of agents enrolled whose outputs uses the custom preset', + }, + }, + throughput: { + type: 'long', + _meta: { + description: 'Number of agents enrolled whose output uses the throughput preset', + }, + }, + scale: { + type: 'long', + _meta: { + description: 'Number of agents enrolled whose output uses the scale preset', + }, + }, + latency: { + type: 'long', + _meta: { + description: 'Number of agents enrolled whose output uses the latency preset', + }, + }, + }, + }, + output_preset: { + type: 'keyword', + _meta: { + description: 'Output preset used by agent, if applicable', + optional: true, + }, + }, count_as_data: { type: 'long', _meta: { @@ -90,6 +135,38 @@ export const fleetAgentsSchema: RootSchema = { }, }, }, + upgrade_details: { + _meta: { + description: 'Agent upgrade details telemetry', + optional: true, + }, + properties: { + target_version: { + type: 'keyword', + _meta: { + description: 'Target version of the agent upgrade', + }, + }, + state: { + type: 'keyword', + _meta: { + description: 'State of the agent upgrade', + }, + }, + error_msg: { + type: 'keyword', + _meta: { + description: 'Error message of the agent upgrade if failed', + }, + }, + agent_count: { + type: 'long', + _meta: { + description: 'How many agents have this upgrade details', + }, + }, + }, + }, }; export const fleetUsagesSchema: RootSchema = { diff --git a/x-pack/plugins/fleet/server/types/models/output.ts b/x-pack/plugins/fleet/server/types/models/output.ts index 4c391e8383a58..f9e78b6db25a0 100644 --- a/x-pack/plugins/fleet/server/types/models/output.ts +++ b/x-pack/plugins/fleet/server/types/models/output.ts @@ -115,12 +115,30 @@ export const ElasticSearchSchema = { ...BaseSchema, type: schema.literal(outputType.Elasticsearch), hosts: schema.arrayOf(schema.uri({ scheme: ['http', 'https'] }), { minSize: 1 }), + preset: schema.maybe( + schema.oneOf([ + schema.literal('balanced'), + schema.literal('custom'), + schema.literal('throughput'), + schema.literal('scale'), + schema.literal('latency'), + ]) + ), }; const ElasticSearchUpdateSchema = { ...UpdateSchema, type: schema.maybe(schema.literal(outputType.Elasticsearch)), hosts: schema.maybe(schema.arrayOf(schema.uri({ scheme: ['http', 'https'] }), { minSize: 1 })), + preset: schema.maybe( + schema.oneOf([ + schema.literal('balanced'), + schema.literal('custom'), + schema.literal('throughput'), + schema.literal('scale'), + schema.literal('latency'), + ]) + ), }; /** diff --git a/x-pack/plugins/fleet/server/types/so_attributes.ts b/x-pack/plugins/fleet/server/types/so_attributes.ts index df98f0b00a424..311e5159b8f15 100644 --- a/x-pack/plugins/fleet/server/types/so_attributes.ts +++ b/x-pack/plugins/fleet/server/types/so_attributes.ts @@ -16,6 +16,7 @@ import type { KafkaAcknowledgeReliabilityLevel, KafkaConnectionTypeType, AgentUpgradeDetails, + OutputPreset, } from '../../common/types'; import type { AgentType, FleetServerAgentComponent } from '../../common/types/models'; @@ -144,6 +145,7 @@ interface OutputSoBaseAttributes { allow_edit?: string[]; output_id?: string; ssl?: string | null; // encrypted ssl field + preset?: OutputPreset; } interface OutputSoElasticsearchAttributes extends OutputSoBaseAttributes { diff --git a/x-pack/plugins/fleet/tsconfig.json b/x-pack/plugins/fleet/tsconfig.json index 4d3c850b9e75d..4503d328e1503 100644 --- a/x-pack/plugins/fleet/tsconfig.json +++ b/x-pack/plugins/fleet/tsconfig.json @@ -102,5 +102,6 @@ "@kbn/dashboard-plugin", "@kbn/cloud", "@kbn/config", + "@kbn/core-http-server-mocks", ] } diff --git a/x-pack/plugins/global_search_bar/public/plugin.tsx b/x-pack/plugins/global_search_bar/public/plugin.tsx index cf421202a4864..12f15036886c4 100644 --- a/x-pack/plugins/global_search_bar/public/plugin.tsx +++ b/x-pack/plugins/global_search_bar/public/plugin.tsx @@ -22,7 +22,7 @@ export interface GlobalSearchBarPluginStartDeps { usageCollection?: UsageCollectionSetup; } -export class GlobalSearchBarPlugin implements Plugin<{}, {}> { +export class GlobalSearchBarPlugin implements Plugin<{}, {}, {}, GlobalSearchBarPluginStartDeps> { public setup({ analytics }: CoreSetup) { eventTypes.forEach((eventType) => { analytics.registerEventType(eventType); diff --git a/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/toggle_phase_action.ts b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/toggle_phase_action.ts index fc89332e47a67..7af36c2fb4f1d 100644 --- a/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/toggle_phase_action.ts +++ b/x-pack/plugins/index_lifecycle_management/integration_tests/helpers/actions/toggle_phase_action.ts @@ -27,7 +27,7 @@ const toggleDeletePhase = async (testBed: TestBed) => { if (action === 'disable') { button.simulate('click'); } else { - button.find('input').simulate('change'); + button.find('button').simulate('click'); } }); component.update(); diff --git a/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_create.test.tsx b/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_create.test.tsx index fd373efb6d17d..367437e5c5346 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_create.test.tsx +++ b/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_create.test.tsx @@ -100,14 +100,10 @@ describe('', () => { httpRequestsMockHelpers.setLoadComponentTemplatesResponse(componentTemplates); httpRequestsMockHelpers.setLoadNodesPluginsResponse([]); - - // disable all react-beautiful-dnd development warnings - (window as any)['__@hello-pangea/dnd-disable-dev-warnings'] = true; }); afterAll(() => { jest.useRealTimers(); - (window as any)['__@hello-pangea/dnd-disable-dev-warnings'] = false; }); describe('composable index template', () => { @@ -533,11 +529,6 @@ describe('', () => { name: TEMPLATE_NAME, indexPatterns: DEFAULT_INDEX_PATTERNS, dataStream: {}, - lifecycle: { - enabled: true, - value: 1, - unit: 'd', - }, allowAutoCreate: true, }); // Component templates @@ -551,13 +542,14 @@ describe('', () => { }); it('should send the correct payload', async () => { - const { actions, find } = testBed; + const { component, actions, find } = testBed; expect(find('stepTitle').text()).toEqual(`Review details for '${TEMPLATE_NAME}'`); await act(async () => { actions.clickNextButton(); }); + component.update(); expect(httpSetup.post).toHaveBeenLastCalledWith( `${API_BASE_PATH}/index_templates`, @@ -589,10 +581,6 @@ describe('', () => { }, }, aliases: ALIASES, - lifecycle: { - enabled: true, - data_retention: '1d', - }, }, }), }) @@ -620,44 +608,59 @@ describe('', () => { }); }); - test('preview data stream', async () => { - await act(async () => { - testBed = await setup(httpSetup); - }); - testBed.component.update(); - - const { actions } = testBed; - // Logistics - await actions.completeStepOne({ - name: TEMPLATE_NAME, - indexPatterns: DEFAULT_INDEX_PATTERNS, - dataStream: {}, - lifecycle: { - enabled: true, - value: 1, - unit: 'd', - }, + describe('DSL', () => { + beforeEach(async () => { + await act(async () => { + testBed = await setup(httpSetup); + }); + testBed.component.update(); + + await testBed.actions.completeStepOne({ + name: TEMPLATE_NAME, + indexPatterns: DEFAULT_INDEX_PATTERNS, + dataStream: {}, + lifecycle: { + enabled: true, + value: 1, + unit: 'd', + }, + }); }); - await act(async () => { - await actions.previewTemplate(); + test('should include DSL in summary when set in step 1', async () => { + const { find, component } = testBed; + + await act(async () => { + testBed.find('formWizardStep-5').simulate('click'); + }); + component.update(); + + expect(find('lifecycleValue').text()).toContain('1 day'); }); - expect(httpSetup.post).toHaveBeenLastCalledWith( - `${API_BASE_PATH}/index_templates/simulate`, - expect.objectContaining({ - body: JSON.stringify({ - template: { - lifecycle: { - enabled: true, - data_retention: '1d', + test('preview data stream', async () => { + const { actions } = testBed; + + await act(async () => { + await actions.previewTemplate(); + }); + + expect(httpSetup.post).toHaveBeenLastCalledWith( + `${API_BASE_PATH}/index_templates/simulate`, + expect.objectContaining({ + body: JSON.stringify({ + template: { + lifecycle: { + enabled: true, + data_retention: '1d', + }, }, - }, - index_patterns: DEFAULT_INDEX_PATTERNS, - data_stream: {}, - allow_auto_create: false, - }), - }) - ); + index_patterns: DEFAULT_INDEX_PATTERNS, + data_stream: {}, + allow_auto_create: false, + }), + }) + ); + }); }); }); diff --git a/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_form.helpers.ts b/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_form.helpers.ts index 8b98fb769959f..88cee63b0e693 100644 --- a/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_form.helpers.ts +++ b/x-pack/plugins/index_management/__jest__/client_integration/index_template_wizard/template_form.helpers.ts @@ -185,27 +185,24 @@ export const formSetup = async (initTestBed: SetupFunc) => { if (version) { form.setInputValue('versionField.input', JSON.stringify(version)); } + + if (allowAutoCreate) { + form.toggleEuiSwitch('allowAutoCreateField.input'); + } }); component.update(); if (lifecycle && lifecycle.enabled) { - act(() => { + await act(async () => { form.toggleEuiSwitch('dataRetentionToggle.input'); }); component.update(); - act(() => { - form.setInputValue('valueDataRetentionField', String(lifecycle.value)); - }); + form.setInputValue('valueDataRetentionField', String(lifecycle.value)); } await act(async () => { - if (allowAutoCreate) { - form.toggleEuiSwitch('allowAutoCreateField.input'); - } - clickNextButton(); - jest.advanceTimersByTime(0); }); component.update(); @@ -378,6 +375,8 @@ export type TestSubjects = | 'settingsEditor' | 'versionField.input' | 'valueDataRetentionField' + | 'formWizardStep-5' + | 'lifecycleValue' | 'mappingsEditor.formTab' | 'mappingsEditor.advancedConfiguration.sizeEnabledToggle' | 'previewIndexTemplate'; diff --git a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_list.tsx b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_list.tsx index 04a5a4a20f491..fa4efa61bf548 100644 --- a/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_list.tsx +++ b/x-pack/plugins/index_management/public/application/sections/home/data_stream_list/data_stream_list.tsx @@ -128,7 +128,7 @@ export const DataStreamList: React.FunctionComponent ; const defaultProps: Props = { - chartProps: { theme: EUI_CHARTS_THEME_LIGHT.theme, baseTheme: LIGHT_THEME }, + chartProps: { baseTheme: LIGHT_THEME }, comparator: Comparator.GT, id: 'componentId', threshold: 90, diff --git a/x-pack/plugins/infra/public/alerting/common/components/threshold.test.tsx b/x-pack/plugins/infra/public/alerting/common/components/threshold.test.tsx index fd50c54b65f61..b78216b78f60c 100644 --- a/x-pack/plugins/infra/public/alerting/common/components/threshold.test.tsx +++ b/x-pack/plugins/infra/public/alerting/common/components/threshold.test.tsx @@ -6,7 +6,6 @@ */ import { LIGHT_THEME } from '@elastic/charts'; -import { EUI_CHARTS_THEME_LIGHT } from '@elastic/eui/dist/eui_charts_theme'; import { Comparator } from '../../../../common/alerting/metrics'; import { render } from '@testing-library/react'; import { Props, Threshold } from './threshold'; @@ -15,7 +14,7 @@ import React from 'react'; describe('Threshold', () => { const renderComponent = (props: Partial = {}) => { const defaultProps: Props = { - chartProps: { theme: EUI_CHARTS_THEME_LIGHT.theme, baseTheme: LIGHT_THEME }, + chartProps: { baseTheme: LIGHT_THEME }, comparator: Comparator.GT, id: 'componentId', threshold: 90, diff --git a/x-pack/plugins/infra/public/alerting/common/components/threshold.tsx b/x-pack/plugins/infra/public/alerting/common/components/threshold.tsx index 79e065ae7b1f3..d9ca396f9aa33 100644 --- a/x-pack/plugins/infra/public/alerting/common/components/threshold.tsx +++ b/x-pack/plugins/infra/public/alerting/common/components/threshold.tsx @@ -13,7 +13,7 @@ import { i18n } from '@kbn/i18n'; import { Comparator } from '../../../../common/alerting/metrics'; export interface ChartProps { - theme: PartialTheme; + theme?: PartialTheme; baseTheme: Theme; } diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_details_app_section/components/log_rate_analysis.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_details_app_section/components/log_rate_analysis.tsx index a8ebb5c884b6b..c1f9231a3a1a9 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_details_app_section/components/log_rate_analysis.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_details_app_section/components/log_rate_analysis.tsx @@ -19,12 +19,7 @@ import { import { LogRateAnalysisContent, type LogRateAnalysisResultsData } from '@kbn/aiops-plugin/public'; import { Rule } from '@kbn/alerting-plugin/common'; import { TopAlert } from '@kbn/observability-plugin/public'; -import { - ContextualInsight, - useObservabilityAIAssistant, - type Message, - MessageRole, -} from '@kbn/observability-ai-assistant-plugin/public'; +import { type Message, MessageRole } from '@kbn/observability-ai-assistant-plugin/public'; import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; import { i18n } from '@kbn/i18n'; import { ALERT_END } from '@kbn/rule-data-utils'; @@ -54,7 +49,11 @@ interface SignificantFieldValue { export const LogRateAnalysis: FC = ({ rule, alert }) => { const { services } = useKibanaContextForPlugin(); - const { dataViews, logsShared } = services; + const { + dataViews, + logsShared, + observabilityAIAssistant: { ObservabilityAIAssistantContextualInsight }, + } = services; const [dataView, setDataView] = useState(); const [esSearchQuery, setEsSearchQuery] = useState(); const [logRateAnalysisParams, setLogRateAnalysisParams] = useState< @@ -180,8 +179,6 @@ export const LogRateAnalysis: FC = ({ r ); }; - const aiAssistant = useObservabilityAIAssistant(); - const messages = useMemo(() => { const hasLogRateAnalysisParams = logRateAnalysisParams && logRateAnalysisParams.significantFieldValues?.length > 0; @@ -290,9 +287,12 @@ export const LogRateAnalysis: FC = ({ r - {aiAssistant.isEnabled() && messages ? ( + {ObservabilityAIAssistantContextualInsight && messages ? ( - + ) : null} diff --git a/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_details_app_section/index.tsx b/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_details_app_section/index.tsx index 05b8c53f9ded9..055039091e074 100644 --- a/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_details_app_section/index.tsx +++ b/x-pack/plugins/infra/public/alerting/log_threshold/components/alert_details_app_section/index.tsx @@ -6,7 +6,7 @@ */ import React, { useEffect } from 'react'; import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; -import { LIGHT_THEME } from '@elastic/charts'; +import { LEGACY_LIGHT_THEME } from '@elastic/charts'; import { EuiPanel } from '@elastic/eui'; import { ALERT_CONTEXT, @@ -20,7 +20,6 @@ import { EuiTitle } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { getPaddedAlertTimeRange } from '@kbn/observability-get-padded-alert-time-range-util'; import { get, identity } from 'lodash'; -import { ObservabilityAIAssistantProvider } from '@kbn/observability-ai-assistant-plugin/public'; import { useLogView } from '@kbn/logs-shared-plugin/public'; import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana'; import { @@ -44,10 +43,7 @@ const AlertDetailsAppSection = ({ alert, setAlertSummaryFields, }: AlertDetailsAppSectionProps) => { - const { - logsShared, - observabilityAIAssistant: { service: observabilityAIAssistantService }, - } = useKibanaContextForPlugin().services; + const { logsShared } = useKibanaContextForPlugin().services; const theme = useTheme(); const timeRange = getPaddedAlertTimeRange(alert.fields[ALERT_START]!, alert.fields[ALERT_END]); const alertEnd = alert.fields[ALERT_END] ? moment(alert.fields[ALERT_END]).valueOf() : undefined; @@ -129,7 +125,7 @@ const AlertDetailsAppSection = ({ - - {getLogRatioChart()} - {getLogCountChart()} - {getLogRateAnalysisSection()} - {getLogsHistoryChart()} - - + + {getLogRatioChart()} + {getLogCountChart()} + {getLogRateAnalysisSection()} + {getLogsHistoryChart()} + ); }; diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_details_app_section.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_details_app_section.tsx index 6b19b7b340d5c..165edd080e45c 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_details_app_section.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/alert_details_app_section.tsx @@ -69,7 +69,6 @@ export function AlertDetailsAppSection({ [createDerivedIndexPattern] ); const chartProps = { - theme: charts.theme.useChartsTheme(), baseTheme: charts.theme.useChartsBaseTheme(), }; const timeRange = getPaddedAlertTimeRange(alert.fields[ALERT_START]!, alert.fields[ALERT_END]); diff --git a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_chart.test.tsx b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_chart.test.tsx index a932821b118b3..d27692b22f06b 100644 --- a/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_chart.test.tsx +++ b/x-pack/plugins/infra/public/alerting/metric_threshold/components/expression_chart.test.tsx @@ -26,7 +26,6 @@ jest.mock('../../../hooks/use_kibana', () => ({ activeCursor: jest.fn(), theme: { useChartsBaseTheme: jest.fn(() => ({})), - useChartsTheme: jest.fn(() => ({})), }, }, }, diff --git a/x-pack/plugins/infra/public/apps/common_providers.tsx b/x-pack/plugins/infra/public/apps/common_providers.tsx index 67c13bad48c96..158e0b95293e9 100644 --- a/x-pack/plugins/infra/public/apps/common_providers.tsx +++ b/x-pack/plugins/infra/public/apps/common_providers.tsx @@ -9,10 +9,7 @@ import { AppMountParameters, CoreStart } from '@kbn/core/public'; import React from 'react'; import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common'; import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public'; -import { - ObservabilityAIAssistantProvider, - ObservabilityAIAssistantPluginStart, -} from '@kbn/observability-ai-assistant-plugin/public'; +import type { ObservabilityAIAssistantPluginStart } from '@kbn/observability-ai-assistant-plugin/public'; import { Storage } from '@kbn/kibana-utils-plugin/public'; import { NavigationWarningPromptProvider } from '@kbn/observability-shared-plugin/public'; import { TriggersAndActionsUIPublicPluginStart } from '@kbn/triggers-actions-ui-plugin/public'; @@ -44,11 +41,9 @@ export const CommonInfraProviders: React.FC<{ - - - {children} - - + + {children} + diff --git a/x-pack/plugins/infra/public/components/asset_details/__stories__/decorator.tsx b/x-pack/plugins/infra/public/components/asset_details/__stories__/decorator.tsx index e55a70978a748..58eb37f58de71 100644 --- a/x-pack/plugins/infra/public/components/asset_details/__stories__/decorator.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/__stories__/decorator.tsx @@ -28,8 +28,6 @@ import type { AlertSummaryWidgetProps } from '@kbn/triggers-actions-ui-plugin/pu import { defaultLogViewAttributes } from '@kbn/logs-shared-plugin/common'; import { DataView, DataViewField } from '@kbn/data-views-plugin/common'; import { MemoryRouter } from 'react-router-dom'; -import { ObservabilityAIAssistantProvider } from '@kbn/observability-ai-assistant-plugin/public'; -import { ObservabilityAIAssistantService } from '@kbn/observability-ai-assistant-plugin/public/types'; import { PluginConfigProvider } from '../../../containers/plugin_config_context'; import type { PluginKibanaContextValue } from '../../../hooks/use_kibana'; import { SourceProvider } from '../../../containers/metrics_source'; @@ -104,7 +102,6 @@ export const DecorateWithKibanaContext: DecoratorFn = (story) => { }, charts: { theme: { - useChartsTheme: () => ({} as Theme), useChartsBaseTheme: () => ({} as Theme), }, }, @@ -199,20 +196,7 @@ export const DecorateWithKibanaContext: DecoratorFn = (story) => { - true, - callApi: () => {}, - getCurrentUser: () => {}, - getLicense: () => {}, - getLicenseManagementLocator: () => {}, - start: {}, - } as unknown as ObservabilityAIAssistantService - } - > - {story()} - + {story()} diff --git a/x-pack/plugins/infra/public/components/asset_details/hooks/use_page_header.tsx b/x-pack/plugins/infra/public/components/asset_details/hooks/use_page_header.tsx index 3fae1eca66a40..905def3ab0bc0 100644 --- a/x-pack/plugins/infra/public/components/asset_details/hooks/use_page_header.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/hooks/use_page_header.tsx @@ -5,22 +5,23 @@ * 2.0. */ import { - EuiIcon, - type EuiPageHeaderProps, - type EuiBreadcrumbsProps, EuiFlexGroup, EuiFlexItem, + EuiIcon, + type EuiBreadcrumbsProps, + type EuiPageHeaderProps, } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; import { useLinkProps } from '@kbn/observability-shared-plugin/public'; -import React, { useCallback, useMemo } from 'react'; import { capitalize } from 'lodash'; +import React, { useCallback, useMemo } from 'react'; import { useHistory, useLocation } from 'react-router-dom'; -import { FormattedMessage } from '@kbn/i18n-react'; import { usePluginConfig } from '../../../containers/plugin_config_context'; import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; +import { useProfilingIntegrationSetting } from '../../../hooks/use_profiling_integration_setting'; import { APM_HOST_FILTER_FIELD } from '../constants'; import { LinkToAlertsRule, LinkToApmServices, LinkToNodeDetails } from '../links'; -import { ContentTabIds, type RouteState, type LinkOptions, type Tab, type TabIds } from '../types'; +import { ContentTabIds, type LinkOptions, type RouteState, type Tab, type TabIds } from '../types'; import { useAssetDetailsRenderPropsContext } from './use_asset_details_render_props'; import { useTabSwitcherContext } from './use_tab_switcher'; @@ -110,12 +111,14 @@ const useRightSideItems = (links?: LinkOptions[]) => { const useFeatureFlagTabs = () => { const { featureFlags } = usePluginConfig(); + const isProfilingEnabled = useProfilingIntegrationSetting(); + const featureFlagControlledTabs: Partial> = useMemo( () => ({ [ContentTabIds.OSQUERY]: featureFlags.osqueryEnabled, - [ContentTabIds.PROFILING]: featureFlags.profilingEnabled, + [ContentTabIds.PROFILING]: isProfilingEnabled, }), - [featureFlags.osqueryEnabled, featureFlags.profilingEnabled] + [featureFlags.osqueryEnabled, isProfilingEnabled] ); const isTabEnabled = useCallback( diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/overview/alerts.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/overview/alerts.tsx index 9b964a0974a23..4d22688debc8c 100644 --- a/x-pack/plugins/infra/public/components/asset_details/tabs/overview/alerts.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/overview/alerts.tsx @@ -97,7 +97,6 @@ const MemoAlertSummaryWidget = React.memo( const { getAlertSummaryWidget: AlertSummaryWidget } = triggersActionsUi; const chartProps = { - theme: charts.theme.useChartsTheme(), baseTheme: charts.theme.useChartsBaseTheme(), }; diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/overview/kpis/cpu_profiling_prompt.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/overview/kpis/cpu_profiling_prompt.tsx new file mode 100644 index 0000000000000..291b255e7ce33 --- /dev/null +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/overview/kpis/cpu_profiling_prompt.tsx @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiButtonEmpty } from '@elastic/eui'; +import { EuiBadge } from '@elastic/eui'; +import { EuiFlexGroup } from '@elastic/eui'; +import { useProfilingIntegrationSetting } from '../../../../../hooks/use_profiling_integration_setting'; +import { useTabSwitcherContext } from '../../../hooks/use_tab_switcher'; + +export function CpuProfilingPrompt() { + const { showTab } = useTabSwitcherContext(); + const isProfilingEnabled = useProfilingIntegrationSetting(); + + if (!isProfilingEnabled) { + return null; + } + + return ( + + + {i18n.translate('xpack.infra.cpuProfilingPrompt.newBadgeLabel', { + defaultMessage: 'NEW', + })} + + + {i18n.translate('xpack.infra.cpuProfilingPrompt.p.viewCPUBreakdownUsingLabel', { + defaultMessage: 'View CPU Breakdown using', + })} + showTab('profiling')} + flush="both" + > + {i18n.translate('xpack.infra.cpuProfilingPrompt.profilingButtonEmptyLabel', { + defaultMessage: 'Profiling', + })} + + + + ); +} diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/overview/kpis/kpi_grid.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/overview/kpis/kpi_grid.tsx index 957cb7e628ee6..f1b8877cdca80 100644 --- a/x-pack/plugins/infra/public/components/asset_details/tabs/overview/kpis/kpi_grid.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/overview/kpis/kpi_grid.tsx @@ -13,6 +13,7 @@ import { findInventoryModel } from '@kbn/metrics-data-access-plugin/common'; import useAsync from 'react-use/lib/useAsync'; import { KPI_CHART_HEIGHT } from '../../../../../common/visualizations'; import { Kpi } from './kpi'; +import { CpuProfilingPrompt } from './cpu_profiling_prompt'; interface Props { dataView?: DataView; @@ -48,6 +49,7 @@ export const KPIGrid = ({ assetName, dataView, dateRange }: Props) => { assetName={assetName} height={KPI_CHART_HEIGHT} /> + {chartProps.id === 'cpuUsage' && } ))} diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/processes/process_row.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/processes/process_row.tsx index a874e071e6c23..c6f98c6d1422c 100644 --- a/x-pack/plugins/infra/public/components/asset_details/tabs/processes/process_row.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/processes/process_row.tsx @@ -23,12 +23,8 @@ import { } from '@elastic/eui'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; import useToggle from 'react-use/lib/useToggle'; -import { - useObservabilityAIAssistant, - type Message, - MessageRole, - ContextualInsight, -} from '@kbn/observability-ai-assistant-plugin/public'; +import { type Message, MessageRole } from '@kbn/observability-ai-assistant-plugin/public'; +import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana'; import { Process } from './types'; import { ProcessRowCharts } from './process_row_charts'; @@ -38,7 +34,10 @@ interface Props { supportAIAssistant?: boolean; } export const ContextualInsightProcessRow = ({ command }: { command: string }) => { - const aiAssistant = useObservabilityAIAssistant(); + const { + observabilityAIAssistant: { ObservabilityAIAssistantContextualInsight }, + } = useKibanaContextForPlugin().services; + const explainProcessMessages = useMemo(() => { if (!command) { return undefined; @@ -98,11 +97,11 @@ export const ContextualInsightProcessRow = ({ command }: { command: string }) => }, [command]); return ( <> - {aiAssistant.isEnabled() && explainProcessMessages ? ( + {ObservabilityAIAssistantContextualInsight && explainProcessMessages ? ( - diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/profiling/flamegraph.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/profiling/flamegraph.tsx index a0fbeb25ca3d1..a00b9373c2f02 100644 --- a/x-pack/plugins/infra/public/components/asset_details/tabs/profiling/flamegraph.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/profiling/flamegraph.tsx @@ -8,6 +8,8 @@ import React, { useMemo } from 'react'; import { EuiSpacer } from '@elastic/eui'; import { EmbeddableFlamegraph } from '@kbn/observability-shared-plugin/public'; +import { i18n } from '@kbn/i18n'; +import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana'; import { useAssetDetailsRenderPropsContext } from '../../hooks/use_asset_details_render_props'; import { useDatePickerContext } from '../../hooks/use_date_picker'; import { useProfilingFlamegraphData } from '../../hooks/use_profiling_flamegraph_data'; @@ -17,11 +19,17 @@ import { ErrorPrompt } from './error_prompt'; import { ProfilingLinks } from './profiling_links'; export function Flamegraph() { + const { services } = useKibanaContextForPlugin(); const { asset } = useAssetDetailsRenderPropsContext(); const { activeTabId } = useTabSwitcherContext(); - const { getDateRangeInTimestamp } = useDatePickerContext(); + const { dateRange, getDateRangeInTimestamp } = useDatePickerContext(); const { from, to } = getDateRangeInTimestamp(); + const profilingLinkLocator = services.observabilityShared.locators.profiling.flamegraphLocator; + const profilingLinkLabel = i18n.translate('xpack.infra.flamegraph.profilingAppFlamegraphLink', { + defaultMessage: 'Go to Universal Profiling Flamegraph', + }); + const params = useMemo( () => ({ hostname: asset.name, @@ -41,7 +49,13 @@ export function Flamegraph() { return ( <> - + diff --git a/x-pack/plugins/infra/public/components/asset_details/tabs/profiling/functions.tsx b/x-pack/plugins/infra/public/components/asset_details/tabs/profiling/functions.tsx index b6089df1bc67c..1b9a58ff561b3 100644 --- a/x-pack/plugins/infra/public/components/asset_details/tabs/profiling/functions.tsx +++ b/x-pack/plugins/infra/public/components/asset_details/tabs/profiling/functions.tsx @@ -8,6 +8,8 @@ import React, { useMemo } from 'react'; import { EmbeddableFunctions } from '@kbn/observability-shared-plugin/public'; import { EuiSpacer } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana'; import { useAssetDetailsRenderPropsContext } from '../../hooks/use_asset_details_render_props'; import { useDatePickerContext } from '../../hooks/use_date_picker'; import { useProfilingFunctionsData } from '../../hooks/use_profiling_functions_data'; @@ -17,11 +19,17 @@ import { ErrorPrompt } from './error_prompt'; import { ProfilingLinks } from './profiling_links'; export function Functions() { + const { services } = useKibanaContextForPlugin(); const { asset } = useAssetDetailsRenderPropsContext(); const { activeTabId } = useTabSwitcherContext(); - const { getDateRangeInTimestamp } = useDatePickerContext(); + const { dateRange, getDateRangeInTimestamp } = useDatePickerContext(); const { from, to } = getDateRangeInTimestamp(); + const profilingLinkLocator = services.observabilityShared.locators.profiling.topNFunctionsLocator; + const profilingLinkLabel = i18n.translate('xpack.infra.flamegraph.profilingAppTopFunctionsLink', { + defaultMessage: 'Go to Universal Profiling Functions', + }); + const params = useMemo( () => ({ hostname: asset.name, @@ -44,7 +52,13 @@ export function Functions() { return ( <> - + diff --git a/x-pack/plugins/infra/public/hooks/use_profiling_integration_setting.ts b/x-pack/plugins/infra/public/hooks/use_profiling_integration_setting.ts new file mode 100644 index 0000000000000..ee9101af55fc2 --- /dev/null +++ b/x-pack/plugins/infra/public/hooks/use_profiling_integration_setting.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useUiSetting } from '@kbn/kibana-react-plugin/public'; +import { enableInfrastructureProfilingIntegration } from '@kbn/observability-plugin/common'; +import { usePluginConfig } from '../containers/plugin_config_context'; + +export function useProfilingIntegrationSetting(): boolean { + const { + featureFlags: { profilingEnabled }, + } = usePluginConfig(); + const isProfilingUiSettingEnabled = useUiSetting( + enableInfrastructureProfilingIntegration + ); + + return profilingEnabled && isProfilingUiSettingEnabled; +} diff --git a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/single_metric_sparkline.tsx b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/single_metric_sparkline.tsx index 566b601991c7b..d51b1ff370fe3 100644 --- a/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/single_metric_sparkline.tsx +++ b/x-pack/plugins/infra/public/pages/logs/log_entry_categories/sections/top_categories/single_metric_sparkline.tsx @@ -6,12 +6,17 @@ */ import React, { useMemo } from 'react'; -import { Chart, Settings, AreaSeries, ScaleType, TooltipType, Tooltip } from '@elastic/charts'; import { - EUI_CHARTS_THEME_LIGHT, - EUI_SPARKLINE_THEME_PARTIAL, - EUI_CHARTS_THEME_DARK, -} from '@elastic/eui/dist/eui_charts_theme'; + Chart, + Settings, + AreaSeries, + ScaleType, + TooltipType, + Tooltip, + LIGHT_THEME, + DARK_THEME, +} from '@elastic/charts'; +import { EUI_SPARKLINE_THEME_PARTIAL } from '@elastic/eui/dist/eui_charts_theme'; import { i18n } from '@kbn/i18n'; import { useIsDarkMode } from '../../../../../hooks/use_is_dark_mode'; import { useKibanaTimeZoneSetting } from '../../../../../hooks/use_kibana_time_zone_setting'; @@ -35,15 +40,7 @@ export const SingleMetricSparkline: React.FunctionComponent<{ }> = ({ metric, timeRange }) => { const isDarkMode = useIsDarkMode(); const timeZone = useKibanaTimeZoneSetting(); - - const theme = useMemo( - () => [ - // localThemeOverride, - EUI_SPARKLINE_THEME_PARTIAL, - isDarkMode ? EUI_CHARTS_THEME_DARK.theme : EUI_CHARTS_THEME_LIGHT.theme, - ], - [isDarkMode] - ); + const baseTheme = useMemo(() => (isDarkMode ? DARK_THEME : LIGHT_THEME), [isDarkMode]); const xDomain = useMemo( () => ({ @@ -56,7 +53,13 @@ export const SingleMetricSparkline: React.FunctionComponent<{ return ( - + { - const enableDeveloperRoutes = isDevMode(); const uiCapabilities = useKibana().services.application?.capabilities; const { setHeaderActionMenu, theme$ } = useContext(HeaderActionMenuContext); - const kibana = useKibana(); + const { + application: { getUrlForApp }, + observabilityAIAssistant: { ObservabilityAIAssistantActionMenuItem }, + } = useKibanaContextForPlugin().services; + + const enableDeveloperRoutes = isDevMode(); useReadOnlyBadge(!uiCapabilities?.logs?.save); @@ -76,13 +80,15 @@ export const LogsPageContent: React.FunctionComponent = () => { {ADD_DATA_LABEL} - + {ObservabilityAIAssistantActionMenuItem ? ( + + ) : null} )} diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/chart/metric_chart_wrapper.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/chart/metric_chart_wrapper.tsx index e78aae020b46b..d3468548994b4 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/chart/metric_chart_wrapper.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/chart/metric_chart_wrapper.tsx @@ -5,7 +5,14 @@ * 2.0. */ import React, { useEffect, useRef, CSSProperties } from 'react'; -import { Chart, Metric, type MetricWNumber, type MetricWTrend } from '@elastic/charts'; +import { + Chart, + LEGACY_LIGHT_THEME, + Metric, + Settings, + type MetricWNumber, + type MetricWTrend, +} from '@elastic/charts'; import { EuiPanel, EuiToolTip, useEuiTheme } from '@elastic/eui'; import { css } from '@emotion/react'; import { ChartPlaceholder } from '../../../../../components/lens'; @@ -61,6 +68,10 @@ export const MetricChartWrapper = React.memo( } `} > + diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/alerts/alerts_tab_content.tsx b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/alerts/alerts_tab_content.tsx index 245a7ee752a64..5a64a4040b288 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/alerts/alerts_tab_content.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/components/tabs/alerts/alerts_tab_content.tsx @@ -91,7 +91,6 @@ const MemoAlertSummaryWidget = React.memo( }; const chartProps = { - theme: charts.theme.useChartsTheme(), baseTheme: charts.theme.useChartsBaseTheme(), onBrushEnd, }; diff --git a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_host_count.ts b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_host_count.ts index d261b4ec2e45d..134e651ed7a64 100644 --- a/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_host_count.ts +++ b/x-pack/plugins/infra/public/pages/metrics/hosts/hooks/use_host_count.ts @@ -7,10 +7,11 @@ import * as rt from 'io-ts'; import { ES_SEARCH_STRATEGY, IKibanaSearchResponse } from '@kbn/data-plugin/common'; -import { useCallback, useEffect } from 'react'; -import { catchError, map, Observable, of, startWith } from 'rxjs'; +import { useCallback, useEffect, useMemo } from 'react'; +import { catchError, map, Observable, of, startWith, tap } from 'rxjs'; import createContainer from 'constate'; import type { QueryDslQueryContainer, SearchResponse } from '@elastic/elasticsearch/lib/api/types'; +import type { ITelemetryClient } from '../../../../services/telemetry'; import { useKibanaContextForPlugin } from '../../../../hooks/use_kibana'; import { decodeOrThrow } from '../../../../../common/runtime_types'; import { useDataSearch, useLatestPartialDataSearchResponse } from '../../../../utils/data_search'; @@ -79,7 +80,23 @@ export const useHostCount = () => { searchCriteria.dateRange.from, searchCriteria.dateRange.to, ]), - parseResponses: normalizeDataSearchResponse, + parseResponses: useMemo( + () => + normalizeDataSearchResponse({ + telemetry, + telemetryData: { + withQuery: !!searchCriteria.query.query, + withFilters: + searchCriteria.filters.length > 0 || searchCriteria.panelFilters.length > 0, + }, + }), + [ + searchCriteria.filters.length, + searchCriteria.panelFilters.length, + searchCriteria.query.query, + telemetry, + ] + ), }); const { isRequestRunning, isResponsePartial, latestResponseData, latestResponseErrors } = @@ -89,14 +106,6 @@ export const useHostCount = () => { fetchHostCount(); }, [fetchHostCount]); - useEffect(() => { - if (latestResponseData) { - telemetry.reportHostsViewTotalHostCountRetrieved({ - total: latestResponseData.count.value, - }); - } - }, [latestResponseData, telemetry]); - return { errors: latestResponseErrors, isRequestRunning, @@ -116,27 +125,42 @@ const INITIAL_STATE = { loaded: 0, total: undefined, }; -const normalizeDataSearchResponse = ( - response$: Observable>>> -) => - response$.pipe( - map((response) => ({ - data: decodeOrThrow(HostCountResponseRT)(response.rawResponse.aggregations), - errors: [], - isPartial: response.isPartial ?? false, - isRunning: response.isRunning ?? false, - loaded: response.loaded, - total: response.total, - })), - startWith(INITIAL_STATE), - catchError((error) => - of({ - ...INITIAL_STATE, - errors: [error.message ?? error], - isRunning: false, - }) - ) - ); + +const normalizeDataSearchResponse = + ({ + telemetry, + telemetryData, + }: { + telemetry: ITelemetryClient; + telemetryData: { withQuery: boolean; withFilters: boolean }; + }) => + (response$: Observable>>>) => { + return response$.pipe( + map((response) => ({ + data: decodeOrThrow(HostCountResponseRT)(response.rawResponse.aggregations), + errors: [], + isPartial: response.isPartial ?? false, + isRunning: response.isRunning ?? false, + loaded: response.loaded, + total: response.total, + })), + tap(({ data }) => { + telemetry.reportHostsViewTotalHostCountRetrieved({ + total: data.count.value, + with_query: telemetryData.withQuery, + with_filters: telemetryData.withFilters, + }); + }), + startWith(INITIAL_STATE), + catchError((error) => + of({ + ...INITIAL_STATE, + errors: [error.message ?? error], + isRunning: false, + }) + ) + ); + }; const HostCountResponseRT = rt.type({ count: rt.type({ diff --git a/x-pack/plugins/infra/public/pages/metrics/index.tsx b/x-pack/plugins/infra/public/pages/metrics/index.tsx index 46f2f1c124767..e617edcebf965 100644 --- a/x-pack/plugins/infra/public/pages/metrics/index.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/index.tsx @@ -13,8 +13,8 @@ import { Routes, Route } from '@kbn/shared-ux-router'; import { EuiErrorBoundary, EuiHeaderLinks, EuiHeaderLink } from '@elastic/eui'; import { useKibana, useUiSetting } from '@kbn/kibana-react-plugin/public'; import { HeaderMenuPortal, useLinkProps } from '@kbn/observability-shared-plugin/public'; -import { ObservabilityAIAssistantActionMenuItem } from '@kbn/observability-ai-assistant-plugin/public'; import { enableInfrastructureHostsView } from '@kbn/observability-plugin/common'; +import { useKibanaContextForPlugin } from '../../hooks/use_kibana'; import { MetricsSourceConfigurationProperties } from '../../../common/metrics_sources'; import { HelpCenterContent } from '../../components/help_center_content'; import { useReadOnlyBadge } from '../../hooks/use_readonly_badge'; @@ -44,6 +44,9 @@ const ADD_DATA_LABEL = i18n.translate('xpack.infra.metricsHeaderAddDataButtonLab }); export const InfrastructurePage = () => { + const { + observabilityAIAssistant: { ObservabilityAIAssistantActionMenuItem }, + } = useKibanaContextForPlugin().services; const config = usePluginConfig(); const uiCapabilities = useKibana().services.application?.capabilities; const { setHeaderActionMenu, theme$ } = useContext(HeaderActionMenuContext); @@ -95,7 +98,9 @@ export const InfrastructurePage = () => { > {ADD_DATA_LABEL} - + {ObservabilityAIAssistantActionMenuItem ? ( + + ) : null} )} diff --git a/x-pack/plugins/infra/public/pages/metrics/settings/features_configuration_panel.tsx b/x-pack/plugins/infra/public/pages/metrics/settings/features_configuration_panel.tsx index 19d7392fb7ca1..aa69ef543c68f 100644 --- a/x-pack/plugins/infra/public/pages/metrics/settings/features_configuration_panel.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/settings/features_configuration_panel.tsx @@ -10,9 +10,13 @@ import { EuiSpacer } from '@elastic/eui'; import { EuiForm } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import React from 'react'; -import { enableInfrastructureHostsView } from '@kbn/observability-plugin/common'; +import { + enableInfrastructureHostsView, + enableInfrastructureProfilingIntegration, +} from '@kbn/observability-plugin/common'; import { useEditableSettings } from '@kbn/observability-shared-plugin/public'; import { LazyField } from '@kbn/advanced-settings-plugin/public'; +import { usePluginConfig } from '../../../containers/plugin_config_context'; import { useKibanaContextForPlugin } from '../../../hooks/use_kibana'; type Props = Pick< @@ -31,6 +35,7 @@ export function FeaturesConfigurationPanel({ const { services: { docLinks, notifications }, } = useKibanaContextForPlugin(); + const { featureFlags } = usePluginConfig(); return ( @@ -52,6 +57,17 @@ export function FeaturesConfigurationPanel({ toasts={notifications.toasts} unsavedChanges={unsavedChanges[enableInfrastructureHostsView]} /> + {featureFlags.profilingEnabled && ( + + )} ); } diff --git a/x-pack/plugins/infra/public/pages/metrics/settings/source_configuration_settings.tsx b/x-pack/plugins/infra/public/pages/metrics/settings/source_configuration_settings.tsx index 5769f861234c4..a064aaf0e151f 100644 --- a/x-pack/plugins/infra/public/pages/metrics/settings/source_configuration_settings.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/settings/source_configuration_settings.tsx @@ -17,7 +17,10 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import React, { useCallback } from 'react'; import { Prompt, useEditableSettings } from '@kbn/observability-shared-plugin/public'; -import { enableInfrastructureHostsView } from '@kbn/observability-plugin/common'; +import { + enableInfrastructureHostsView, + enableInfrastructureProfilingIntegration, +} from '@kbn/observability-plugin/common'; import { SourceLoadingPage } from '../../../components/source_loading_page'; import { useSourceContext } from '../../../containers/metrics_source'; import { useInfraMLCapabilitiesContext } from '../../../containers/ml/infra_ml_capabilities'; @@ -61,7 +64,10 @@ export const SourceConfigurationSettings = ({ formState, formStateChanges, } = useSourceConfigurationFormState(source && source.configuration); - const infraUiSettings = useEditableSettings('infra_metrics', [enableInfrastructureHostsView]); + const infraUiSettings = useEditableSettings('infra_metrics', [ + enableInfrastructureHostsView, + enableInfrastructureProfilingIntegration, + ]); const resetAllUnsavedChanges = useCallback(() => { resetForm(); diff --git a/x-pack/plugins/infra/public/services/telemetry/telemetry_events.ts b/x-pack/plugins/infra/public/services/telemetry/telemetry_events.ts index 6ce2d2b827623..b83cbfe262e63 100644 --- a/x-pack/plugins/infra/public/services/telemetry/telemetry_events.ts +++ b/x-pack/plugins/infra/public/services/telemetry/telemetry_events.ts @@ -109,6 +109,20 @@ const hostViewTotalHostCountRetrieved: InfraTelemetryEvent = { optional: false, }, }, + with_query: { + type: 'boolean', + _meta: { + description: 'Has KQL query', + optional: false, + }, + }, + with_filters: { + type: 'boolean', + _meta: { + description: 'Has filters', + optional: false, + }, + }, }, }; diff --git a/x-pack/plugins/infra/public/services/telemetry/telemetry_service.test.ts b/x-pack/plugins/infra/public/services/telemetry/telemetry_service.test.ts index ac450df7dd162..3fa8a9b447111 100644 --- a/x-pack/plugins/infra/public/services/telemetry/telemetry_service.test.ts +++ b/x-pack/plugins/infra/public/services/telemetry/telemetry_service.test.ts @@ -172,6 +172,8 @@ describe('TelemetryService', () => { telemetry.reportHostsViewTotalHostCountRetrieved({ total: 300, + with_filters: true, + with_query: false, }); expect(setupParams.analytics.reportEvent).toHaveBeenCalledTimes(1); @@ -179,6 +181,8 @@ describe('TelemetryService', () => { InfraTelemetryEventTypes.HOST_VIEW_TOTAL_HOST_COUNT_RETRIEVED, { total: 300, + with_filters: true, + with_query: false, } ); }); diff --git a/x-pack/plugins/infra/public/services/telemetry/types.ts b/x-pack/plugins/infra/public/services/telemetry/types.ts index 3b1665078ee3a..769cc303def50 100644 --- a/x-pack/plugins/infra/public/services/telemetry/types.ts +++ b/x-pack/plugins/infra/public/services/telemetry/types.ts @@ -41,6 +41,8 @@ export interface HostFlyoutFilterActionParams { export interface HostsViewQueryHostsCountRetrievedParams { total: number; + with_query: boolean; + with_filters: boolean; } export interface AssetDetailsFlyoutViewedParams { diff --git a/x-pack/plugins/infra/public/utils/use_timeline_chart_theme.ts b/x-pack/plugins/infra/public/utils/use_timeline_chart_theme.ts index 7c562c90ab434..47aac1cc26bfa 100644 --- a/x-pack/plugins/infra/public/utils/use_timeline_chart_theme.ts +++ b/x-pack/plugins/infra/public/utils/use_timeline_chart_theme.ts @@ -16,19 +16,15 @@ export function useTimelineChartTheme(): Pick getAlertByAlertUuid, } = services; const { basePath, alertsLocator } = libs; - const config = libs.getAlertDetailsConfig(); const alertFactory: LogThresholdAlertFactory = ( id, @@ -189,15 +183,7 @@ export const createLogThresholdExecutor = (libs: InfraBackendLibs) => alert.scheduleActions(actionGroup, { ...sharedContext, ...context, - alertDetailsUrl: getAlertDetailsPageEnabledForApp(config, 'logs') - ? getAlertDetailsUrl(libs.basePath, spaceId, alertUuid) - : await getAlertUrl( - alertUuid, - spaceId, - indexedStartedAt, - libs.alertsLocator, - libs.basePath.publicBaseUrl - ), + alertDetailsUrl: getAlertDetailsUrl(libs.basePath, spaceId, alertUuid), }); }); } @@ -254,7 +240,6 @@ export const createLogThresholdExecutor = (libs: InfraBackendLibs) => validatedParams, getAlertByAlertUuid, alertsLocator, - isAlertDetailsPageEnabled: getAlertDetailsPageEnabledForApp(config, 'logs'), }); } catch (e) { throw new Error(e); @@ -868,7 +853,6 @@ const processRecoveredAlerts = async ({ validatedParams, getAlertByAlertUuid, alertsLocator, - isAlertDetailsPageEnabled = false, }: { basePath: IBasePath; getAlertStartedDate: (alertId: string) => string | null; @@ -881,7 +865,6 @@ const processRecoveredAlerts = async ({ alertUuid: string ) => Promise | null> | null; alertsLocator?: LocatorPublic; - isAlertDetailsPageEnabled?: boolean; }) => { const groupByKeysObjectForRecovered = getGroupByObject( validatedParams.groupBy, @@ -898,15 +881,7 @@ const processRecoveredAlerts = async ({ const viewInAppUrl = addSpaceIdToPath(basePath.publicBaseUrl, spaceId, relativeViewInAppUrl); const baseContext = { - alertDetailsUrl: isAlertDetailsPageEnabled - ? getAlertDetailsUrl(basePath, spaceId, alertUuid) - : await getAlertUrl( - alertUuid, - spaceId, - indexedStartedAt, - alertsLocator, - basePath.publicBaseUrl - ), + alertDetailsUrl: getAlertDetailsUrl(basePath, spaceId, alertUuid), group: hasGroupBy(validatedParams) ? recoveredAlertId : null, groupByKeys: groupByKeysObjectForRecovered[recoveredAlertId], timestamp: startedAt.toISOString(), diff --git a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/test_pipeline.test.tsx b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/test_pipeline.test.tsx index 9102d2b5e307f..3698d07018c71 100644 --- a/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/test_pipeline.test.tsx +++ b/x-pack/plugins/ingest_pipelines/public/application/components/pipeline_editor/__jest__/test_pipeline.test.tsx @@ -377,7 +377,9 @@ describe('Test pipeline', () => { // Click the "Configuration" tab await actions.clickProcessorConfigurationTab(); // Verify type selector has not changed - expect(find('processorTypeSelector.input').text()).toBe('Set'); + expect(find('processorTypeSelector.input').find('[role="combobox"]').props().value).toBe( + 'Set' + ); }); }); diff --git a/x-pack/plugins/kubernetes_security/public/components/tree_view_container/tree_nav/index.test.tsx b/x-pack/plugins/kubernetes_security/public/components/tree_view_container/tree_nav/index.test.tsx index 554f34325016f..155f4236f8107 100644 --- a/x-pack/plugins/kubernetes_security/public/components/tree_view_container/tree_nav/index.test.tsx +++ b/x-pack/plugins/kubernetes_security/public/components/tree_view_container/tree_nav/index.test.tsx @@ -40,24 +40,26 @@ describe('TreeNav component', () => { it('mount with Logical View selected by default', async () => { renderResult = mockedContext.render(); - const elemLabel = await renderResult.getByDisplayValue(/logical/i); - expect(elemLabel).toBeChecked(); + const elemLabel = await renderResult.getByTestId('treeNavType_generated-idlogical'); + expect(elemLabel).toHaveAttribute('aria-pressed', 'true'); }); it('shows the tree path according with the selected view type', async () => { renderResult = mockedContext.render(); const logicalViewPath = 'cluster / namespace / pod / container image'; - const logicViewRadio = await renderResult.getByDisplayValue(/logical/i); - expect(logicViewRadio).toBeChecked(); + const logicViewButton = renderResult.getByTestId('treeNavType_generated-idlogical'); + expect(logicViewButton).toHaveAttribute('aria-pressed', 'true'); expect(renderResult.getByText(logicalViewPath)).toBeInTheDocument(); - const infraStructureViewRadio = await renderResult.getByDisplayValue(/infrastructure/i); + const infraStructureViewRadio = renderResult.getByTestId( + 'treeNavType_generated-idinfrastructure' + ); infraStructureViewRadio.click(); expect(renderResult.getByText('cluster / node / pod / container image')).toBeInTheDocument(); - logicViewRadio.click(); + logicViewButton.click(); expect(renderResult.getByText(logicalViewPath)).toBeInTheDocument(); }); diff --git a/x-pack/plugins/lens/common/expressions/formula_context/context_fns.test.ts b/x-pack/plugins/lens/common/expressions/formula_context/context_fns.test.ts new file mode 100644 index 0000000000000..f064238992b07 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/formula_context/context_fns.test.ts @@ -0,0 +1,91 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ExecutionContext } from '@kbn/expressions-plugin/common'; +import { Adapters } from '@kbn/inspector-plugin/common'; +import { formulaIntervalFn, formulaNowFn, formulaTimeRangeFn } from './context_fns'; + +describe('interval', () => { + it('should return 0 if no time range available', () => { + // (not sure if this case is actually possible) + const result = formulaIntervalFn.fn(undefined, { targetBars: 100 }, { + getSearchContext: () => ({ + /* no time range */ + }), + } as ExecutionContext); + expect(result).toEqual(0); + }); + + it('should return 0 if no targetBars is passed', () => { + const result = formulaIntervalFn.fn( + undefined, + { + /* no targetBars */ + }, + { + getSearchContext: () => ({ + timeRange: { + from: 'now-15m', + to: 'now', + }, + }), + } as ExecutionContext + ); + expect(result).toEqual(0); + }); + + it('should return a valid value > 0 if both timeRange and targetBars is passed', () => { + const result = formulaIntervalFn.fn(undefined, { targetBars: 100 }, { + getSearchContext: () => ({ + timeRange: { + from: 'now-15m', + to: 'now', + }, + }), + } as ExecutionContext); + expect(result).toEqual(10000); + }); +}); + +describe('time range', () => { + it('should return 0 if no time range is available', () => { + // (not sure if this case is actually possible) + const result = formulaTimeRangeFn.fn(undefined, {}, { + getSearchContext: () => ({ + /* no time range */ + }), + } as ExecutionContext); + expect(result).toEqual(0); + }); + + it('should return a valid value > 0 if time range is available', () => { + const result = formulaTimeRangeFn.fn(undefined, {}, { + getSearchContext: () => ({ + timeRange: { + from: 'now-15m', + to: 'now', + }, + now: 1000000, // important to provide this to make the result consistent + }), + } as ExecutionContext); + + expect(result).toBe(900000); + }); +}); + +describe('now', () => { + it('should return the now value when passed', () => { + const now = 123456789; + expect( + formulaNowFn.fn(undefined, {}, { + getSearchContext: () => ({ + now, + }), + } as ExecutionContext) + ).toEqual(now); + }); +}); diff --git a/x-pack/plugins/lens/common/expressions/formula_context/context_fns.ts b/x-pack/plugins/lens/common/expressions/formula_context/context_fns.ts new file mode 100644 index 0000000000000..2f77d1142f7d1 --- /dev/null +++ b/x-pack/plugins/lens/common/expressions/formula_context/context_fns.ts @@ -0,0 +1,97 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getAbsoluteTimeRange, calcAutoIntervalNear } from '@kbn/data-plugin/common'; +import type { TimeRange } from '@kbn/es-query'; +import type { ExpressionFunctionDefinition } from '@kbn/expressions-plugin/common'; +import moment from 'moment'; +import { i18n } from '@kbn/i18n'; + +export type ExpressionFunctionFormulaTimeRange = ExpressionFunctionDefinition< + 'formula_time_range', + undefined, + object, + number +>; + +const getTimeRangeAsNumber = (timeRange: TimeRange | undefined, now: number | undefined) => { + if (!timeRange) return 0; + const absoluteTimeRange = getAbsoluteTimeRange( + timeRange, + now != null ? { forceNow: new Date(now) } : {} + ); + return timeRange ? moment(absoluteTimeRange.to).diff(moment(absoluteTimeRange.from)) : 0; +}; + +export const formulaTimeRangeFn: ExpressionFunctionFormulaTimeRange = { + name: 'formula_time_range', + + help: i18n.translate('xpack.lens.formula.timeRange.help', { + defaultMessage: 'The specified time range, in milliseconds (ms).', + }), + + args: {}, + + fn(_input, _args, { getSearchContext }) { + const { timeRange, now } = getSearchContext(); + return getTimeRangeAsNumber(timeRange, now); + }, +}; + +export type ExpressionFunctionFormulaInterval = ExpressionFunctionDefinition< + 'formula_interval', + undefined, + { + targetBars?: number; + }, + number +>; + +export const formulaIntervalFn: ExpressionFunctionFormulaInterval = { + name: 'formula_interval', + + help: i18n.translate('xpack.lens.formula.interval.help', { + defaultMessage: 'The specified minimum interval for the date histogram, in milliseconds (ms).', + }), + + args: { + targetBars: { + types: ['number'], + help: i18n.translate('xpack.lens.formula.interval.targetBars.help', { + defaultMessage: 'The target number of bars for the date histogram.', + }), + }, + }, + + fn(_input, args, { getSearchContext }) { + const { timeRange, now } = getSearchContext(); + return timeRange && args.targetBars + ? calcAutoIntervalNear(args.targetBars, getTimeRangeAsNumber(timeRange, now)).asMilliseconds() + : 0; + }, +}; + +export type ExpressionFunctionFormulaNow = ExpressionFunctionDefinition< + 'formula_now', + undefined, + object, + number +>; + +export const formulaNowFn: ExpressionFunctionFormulaNow = { + name: 'formula_now', + + help: i18n.translate('xpack.lens.formula.now.help', { + defaultMessage: 'The current now moment used in Kibana expressed in milliseconds (ms).', + }), + + args: {}, + + fn(_input, _args, { getSearchContext }) { + return getSearchContext().now ?? Date.now(); + }, +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_guide/index.ts b/x-pack/plugins/lens/common/expressions/formula_context/index.ts similarity index 80% rename from x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_guide/index.ts rename to x-pack/plugins/lens/common/expressions/formula_context/index.ts index 853898403e364..da8931779cfbe 100644 --- a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_guide/index.ts +++ b/x-pack/plugins/lens/common/expressions/formula_context/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export { ElasticsearchGuide } from './elasticsearch_guide'; +export * from './context_fns'; diff --git a/x-pack/plugins/lens/common/expressions/index.ts b/x-pack/plugins/lens/common/expressions/index.ts index ccb6343334d62..c3ccaddac9fd3 100644 --- a/x-pack/plugins/lens/common/expressions/index.ts +++ b/x-pack/plugins/lens/common/expressions/index.ts @@ -11,3 +11,4 @@ export * from './format_column'; export * from './map_to_columns'; export * from './time_scale'; export * from './datatable'; +export * from './formula_context'; diff --git a/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx b/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx index 3a285ec4f33c8..7f501d408a02a 100644 --- a/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx +++ b/x-pack/plugins/lens/public/app_plugin/lens_top_nav.tsx @@ -668,7 +668,7 @@ export const LensTopNavMenu = ({ newCopyOnSave: false, isTitleDuplicateConfirmed: false, returnToOrigin: true, - newDescription: contextFromEmbeddable ? initialContext.description : '', + ...(contextFromEmbeddable && { newDescription: initialContext.description }), panelTimeRange: contextFromEmbeddable ? initialContext.panelTimeRange : undefined, }, { diff --git a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/flyout_wrapper.tsx b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/flyout_wrapper.tsx index 5f0abbf3a952f..bab45a9ffe856 100644 --- a/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/flyout_wrapper.tsx +++ b/x-pack/plugins/lens/public/app_plugin/shared/edit_on_the_fly/flyout_wrapper.tsx @@ -56,10 +56,18 @@ export const FlyoutWrapper = ({ values: { lang: language }, })} (attributes); + const previousAdapters = useRef | undefined>(lensAdapters); const prevQuery = useRef(attributes.state.query); const [query, setQuery] = useState(attributes.state.query); const [errors, setErrors] = useState(); @@ -64,26 +72,37 @@ export function LensEditConfigurationFlyout({ const [isLayerAccordionOpen, setIsLayerAccordionOpen] = useState(true); const [isSuggestionsAccordionOpen, setIsSuggestionsAccordionOpen] = useState(false); const datasourceState = attributes.state.datasourceStates[datasourceId]; - const activeVisualization = visualizationMap[attributes.visualizationType]; const activeDatasource = datasourceMap[datasourceId]; - const { datasourceStates, visualization, isLoading } = useLensSelector((state) => state.lens); + const { datasourceStates, visualization, isLoading, annotationGroups } = useLensSelector( + (state) => state.lens + ); + // use the latest activeId, but fallback to attributes + const activeVisualization = + visualizationMap[visualization.activeId ?? attributes.visualizationType]; + const framePublicAPI = useLensSelector((state) => selectFramePublicAPI(state, datasourceMap)); const suggestsLimitedColumns = activeDatasource?.suggestsLimitedColumns?.(datasourceState); - const activeData: Record = useMemo(() => { - return {}; - }, []); + + const layers = useMemo( + () => activeDatasource.getLayers(datasourceState), + [activeDatasource, datasourceState] + ); + + const dispatch = useLensDispatch(); useEffect(() => { const s = output$?.subscribe(() => { - const layers = activeDatasource.getLayers(datasourceState); - const adaptersTables = lensAdapters?.tables?.tables as Record; + const activeData: Record = {}; + const adaptersTables = previousAdapters.current?.tables?.tables as Record; const [table] = Object.values(adaptersTables || {}); - layers.forEach((layer) => { - if (table) { + if (table) { + layers.forEach((layer) => { activeData[layer] = table; - } - }); + }); + + dispatch(onActiveDataChange({ activeData })); + } }); return () => s?.unsubscribe(); - }, [activeDatasource, lensAdapters, datasourceState, output$, activeData]); + }, [dispatch, output$, layers]); const attributesChanged: boolean = useMemo(() => { const previousAttrs = previousAttributes.current; @@ -99,10 +118,33 @@ export function LensEditConfigurationFlyout({ : false; const visualizationState = visualization.state; - return ( - !isEqual(visualizationState, previousAttrs.state.visualization) || !datasourceStatesAreSame - ); - }, [attributes.references, datasourceId, datasourceMap, datasourceStates, visualization.state]); + const customIsEqual = visualizationMap[previousAttrs.visualizationType]?.isEqual; + const visualizationStateIsEqual = customIsEqual + ? (() => { + try { + return customIsEqual( + previousAttrs.state.visualization, + previousAttrs.references, + visualizationState, + attributes.references, + annotationGroups + ); + } catch (err) { + return false; + } + })() + : isEqual(visualizationState, previousAttrs.state.visualization); + + return !visualizationStateIsEqual || !datasourceStatesAreSame; + }, [ + attributes.references, + datasourceId, + datasourceMap, + datasourceStates, + visualizationMap, + annotationGroups, + visualization.state, + ]); const onCancel = useCallback(() => { const previousAttrs = previousAttributes.current; @@ -181,6 +223,18 @@ export function LensEditConfigurationFlyout({ datasourceMap, ]); + const { getUserMessages } = useApplicationUserMessages({ + coreStart, + framePublicAPI, + activeDatasourceId: datasourceId, + datasourceState: datasourceStates[datasourceId], + datasource: datasourceMap[datasourceId], + dispatch, + visualization: activeVisualization, + visualizationType: visualization.activeId, + visualizationState: visualization, + }); + // needed for text based languages mode which works ONLY with adHoc dataviews const adHocDataViews = Object.values(attributes.state.adHocDataViews ?? {}); @@ -210,17 +264,6 @@ export function LensEditConfigurationFlyout({ ] ); - const framePublicAPI = useLensSelector((state) => { - const newState = { - ...state, - lens: { - ...state.lens, - activeData, - }, - }; - return selectFramePublicAPI(newState, datasourceMap); - }); - const textBasedMode = isOfAggregateQueryType(query) ? getAggregateQueryMode(query) : undefined; if (isLoading) return null; @@ -237,6 +280,7 @@ export function LensEditConfigurationFlyout({ attributesChanged={attributesChanged} > void; + getUserMessages: UserMessagesGetter; } diff --git a/x-pack/plugins/lens/public/datasources/form_based/datapanel.scss b/x-pack/plugins/lens/public/datasources/form_based/datapanel.scss index a408a462b9de3..65e9209c95626 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/datapanel.scss +++ b/x-pack/plugins/lens/public/datasources/form_based/datapanel.scss @@ -14,10 +14,6 @@ margin-bottom: $euiSizeS; } -.lnsChangeIndexPatternPopover { - width: 320px; -} - .lnsChangeIndexPatternPopover__trigger { padding: 0 $euiSize; } diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/filters/filter_popover.tsx b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/filters/filter_popover.tsx index 4e3fe6336be9e..35327fb91b678 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/filters/filter_popover.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/filters/filter_popover.tsx @@ -53,7 +53,6 @@ export const FilterPopover = ({ return ( ( }; } -function createExpression(type: 'interval' | 'now' | 'time_range', value: number) { - return [ - { - type: 'function', - function: 'mathColumn', - arguments: { - id: ['col1'], - name: [`Constant: ${type}`], - expression: [String(value)], - }, - }, - ]; -} - describe('context variables', () => { describe('interval', () => { describe('getErrorMessages', () => { @@ -124,53 +110,6 @@ describe('context variables', () => { ).toBeUndefined(); }); }); - describe('toExpression', () => { - it('should return 0 if no dateRange is passed', () => { - expect( - intervalOperation.toExpression( - createLayer('interval'), - 'col1', - createMockedIndexPattern(), - { now: new Date(), targetBars: 100 } - ) - ).toEqual(expect.arrayContaining(createExpression('interval', 0))); - }); - - it('should return 0 if no targetBars is passed', () => { - expect( - intervalOperation.toExpression( - createLayer('interval'), - 'col1', - createMockedIndexPattern(), - { - dateRange: { - fromDate: new Date(2022, 0, 1).toISOString(), - toDate: new Date(2023, 0, 1).toISOString(), - }, - now: new Date(), - } - ) - ).toEqual(expect.arrayContaining(createExpression('interval', 0))); - }); - - it('should return a valid value > 0 if both dateRange and targetBars is passed', () => { - expect( - intervalOperation.toExpression( - createLayer('interval'), - 'col1', - createMockedIndexPattern(), - { - dateRange: { - fromDate: new Date(2022, 0, 1).toISOString(), - toDate: new Date(2023, 0, 1).toISOString(), - }, - now: new Date(), - targetBars: 100, - } - ) - ).toEqual(expect.arrayContaining(createExpression('interval', 86400000))); - }); - }); }); describe('time_range', () => { describe('getErrorMessages', () => { @@ -202,35 +141,6 @@ describe('context variables', () => { ).toEqual(expect.arrayContaining(['The current time range interval is not available'])); }); }); - - describe('toExpression', () => { - it('should return 0 if no dateRange is passed', () => { - expect( - timeRangeOperation.toExpression( - createLayer('time_range'), - 'col1', - createMockedIndexPattern(), - { now: new Date(), targetBars: 100 } - ) - ).toEqual(expect.arrayContaining(createExpression('time_range', 0))); - }); - - it('should return a valid value > 0 if dateRange is passed', () => { - expect( - timeRangeOperation.toExpression( - createLayer('time_range'), - 'col1', - createMockedIndexPattern(), - { - dateRange: { - fromDate: new Date(2022, 0, 1).toISOString(), - toDate: new Date(2023, 0, 1).toISOString(), - }, - } - ) - ).toEqual(expect.arrayContaining(createExpression('time_range', 31536000000))); - }); - }); }); describe('now', () => { describe('getErrorMessages', () => { @@ -240,16 +150,5 @@ describe('context variables', () => { ).toBeUndefined(); }); }); - - describe('toExpression', () => { - it('should return the now value when passed', () => { - const now = new Date(); - expect( - nowOperation.toExpression(createLayer('now'), 'col1', createMockedIndexPattern(), { - now, - }) - ).toEqual(expect.arrayContaining(createExpression('now', +now))); - }); - }); }); }); diff --git a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/context_variables.tsx b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/context_variables.tsx index 4b7b5b94b493b..f5f28d94ad228 100644 --- a/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/context_variables.tsx +++ b/x-pack/plugins/lens/public/datasources/form_based/operations/definitions/formula/context_variables.tsx @@ -6,9 +6,18 @@ */ import { i18n } from '@kbn/i18n'; -import moment from 'moment'; -import { calcAutoIntervalNear, UI_SETTINGS } from '@kbn/data-plugin/common'; +import { UI_SETTINGS } from '@kbn/data-plugin/common'; import { partition } from 'lodash'; +import { + buildExpressionFunction, + buildExpression, + ExpressionFunctionDefinitions, +} from '@kbn/expressions-plugin/common'; +import { + ExpressionFunctionFormulaInterval, + ExpressionFunctionFormulaNow, + ExpressionFunctionFormulaTimeRange, +} from '../../../../../../common/expressions/formula_context/context_fns'; import type { DateHistogramIndexPatternColumn, FormBasedLayer, @@ -58,13 +67,9 @@ export interface TimeRangeIndexPatternColumn extends ReferenceBasedIndexPatternC operationType: 'time_range'; } -function getTimeRangeFromContext({ dateRange }: ContextValues) { - return dateRange ? moment(dateRange.toDate).diff(moment(dateRange.fromDate)) : 0; -} - function getTimeRangeErrorMessages( - layer: FormBasedLayer, - columnId: string, + _layer: FormBasedLayer, + _columnId: string, indexPattern: IndexPattern, dateRange?: DateRange | undefined ) { @@ -89,12 +94,11 @@ function getTimeRangeErrorMessages( export const timeRangeOperation = createContextValueBasedOperation({ type: 'time_range', label: 'Time range', - description: i18n.translate('xpack.lens.indexPattern.timeRange.documentation.markdown', { - defaultMessage: ` -The specified time range, in milliseconds (ms). - `, + description: i18n.translate('xpack.lens.formula.timeRange.help', { + defaultMessage: 'The specified time range, in milliseconds (ms).', }), - getContextValue: getTimeRangeFromContext, + getExpressionFunction: (_context: ContextValues) => + buildExpressionFunction('formula_time_range', {}), getErrorMessage: getTimeRangeErrorMessages, }); @@ -102,9 +106,6 @@ export interface NowIndexPatternColumn extends ReferenceBasedIndexPatternColumn operationType: 'now'; } -function getNowFromContext({ now }: ContextValues) { - return now == null ? Date.now() : +now; -} function getNowErrorMessage() { return undefined; } @@ -112,12 +113,11 @@ function getNowErrorMessage() { export const nowOperation = createContextValueBasedOperation({ type: 'now', label: 'Current now', - description: i18n.translate('xpack.lens.indexPattern.now.documentation.markdown', { - defaultMessage: ` - The current now moment used in Kibana expressed in milliseconds (ms). - `, + description: i18n.translate('xpack.lens.formula.now.help', { + defaultMessage: 'The current now moment used in Kibana expressed in milliseconds (ms).', }), - getContextValue: getNowFromContext, + getExpressionFunction: (_context: ContextValues) => + buildExpressionFunction('formula_now', {}), getErrorMessage: getNowErrorMessage, }); @@ -125,12 +125,6 @@ export interface IntervalIndexPatternColumn extends ReferenceBasedIndexPatternCo operationType: 'interval'; } -function getIntervalFromContext(context: ContextValues) { - return context.dateRange && context.targetBars - ? calcAutoIntervalNear(context.targetBars, getTimeRangeFromContext(context)).asMilliseconds() - : 0; -} - function getIntervalErrorMessages( layer: FormBasedLayer, columnId: string, @@ -174,12 +168,13 @@ function getIntervalErrorMessages( export const intervalOperation = createContextValueBasedOperation({ type: 'interval', label: 'Date histogram interval', - description: i18n.translate('xpack.lens.indexPattern.interval.documentation.markdown', { - defaultMessage: ` -The specified minimum interval for the date histogram, in milliseconds (ms). - `, + description: i18n.translate('xpack.lens.formula.interval.help', { + defaultMessage: 'The specified minimum interval for the date histogram, in milliseconds (ms).', }), - getContextValue: getIntervalFromContext, + getExpressionFunction: ({ targetBars }: ContextValues) => + buildExpressionFunction('formula_interval', { + targetBars, + }), getErrorMessage: getIntervalErrorMessages, }); @@ -191,14 +186,14 @@ export type ConstantsIndexPatternColumn = function createContextValueBasedOperation({ label, type, - getContextValue, + getExpressionFunction, getErrorMessage, description, }: { label: string; type: ColumnType['operationType']; description: string; - getContextValue: (context: ContextValues) => number; + getExpressionFunction: (context: ContextValues) => ReturnType; getErrorMessage: OperationDefinition['getErrorMessage']; }): OperationDefinition { return { @@ -233,15 +228,11 @@ function createContextValueBasedOperation { const column = layer.columns[columnId] as ColumnType; return [ - { - type: 'function', - function: 'mathColumn', - arguments: { - id: [columnId], - name: [column.label], - expression: [String(getContextValue(context))], - }, - }, + buildExpressionFunction('mathColumn', { + id: columnId, + name: column.label, + expression: buildExpression([getExpressionFunction(context)]), + }).toAst(), ]; }, createCopy(layers, source, target) { diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.test.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.test.tsx index b0729cb489ba7..04d69c1afc571 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.test.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/config_panel/config_panel.test.tsx @@ -484,6 +484,7 @@ describe('ConfigPanel', () => { }, dateRange: expect.anything(), filters: [], + now: expect.anything(), query: undefined, }, groupId: 'a', diff --git a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx index c487f31fd82ff..024dba800011a 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/editor_frame/suggestion_panel.tsx @@ -28,7 +28,8 @@ import { IconType } from '@elastic/eui/src/components/icon/icon'; import { Ast, fromExpression, toExpression } from '@kbn/interpreter'; import { i18n } from '@kbn/i18n'; import classNames from 'classnames'; -import { DataPublicPluginStart, ExecutionContextSearch } from '@kbn/data-plugin/public'; +import { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import type { ExecutionContextSearch } from '@kbn/es-query'; import { ReactExpressionRendererProps, ReactExpressionRendererType, @@ -269,14 +270,13 @@ export function SuggestionPanel({ const framePublicAPI = useLensSelector((state) => selectFramePublicAPI(state, datasourceMap)); const changesApplied = useLensSelector(selectChangesApplied); // get user's selection from localStorage, this key defines if the suggestions panel will be hidden or not - const initialAccordionStatusValue = - typeof isAccordionOpen !== 'undefined' ? !Boolean(isAccordionOpen) : false; + const initialAccordionStatusValue = isAccordionOpen != null ? !Boolean(isAccordionOpen) : false; const [hideSuggestions, setHideSuggestions] = useLocalStorage( LOCAL_STORAGE_SUGGESTIONS_PANEL, initialAccordionStatusValue ); useEffect(() => { - if (typeof isAccordionOpen !== 'undefined') { + if (isAccordionOpen != null) { setHideSuggestions(!Boolean(isAccordionOpen)); } }, [isAccordionOpen, setHideSuggestions]); @@ -549,35 +549,37 @@ export function SuggestionPanel({ forceState={hideSuggestions ? 'closed' : 'open'} onToggle={toggleSuggestions} extraAction={ - !hideSuggestions && ( - <> - {existsStagedPreview && ( - - { - dispatchLens(submitSuggestion()); - }} - > - {i18n.translate('xpack.lens.sugegstion.refreshSuggestionLabel', { - defaultMessage: 'Refresh', + <> + {!hideSuggestions && ( + <> + {existsStagedPreview && ( + - - )} - {wrapSuggestions && ( - - {suggestions.length + 1} - - )} - - ) + > + { + dispatchLens(submitSuggestion()); + }} + > + {i18n.translate('xpack.lens.sugegstion.refreshSuggestionLabel', { + defaultMessage: 'Refresh', + })} + + + )} + + )} + {wrapSuggestions && ( + + {suggestions.length + 1} + + )} + } >

({ - query: context.query, - timeRange: { - from: context.dateRange.fromDate, - to: context.dateRange.toDate, - }, - filters: context.filters, - disableWarningToasts: true, - }), - [context] - ); const searchSessionId = useLensSelector(selectSearchSessionId); if (errors.length) { diff --git a/x-pack/plugins/lens/public/editor_frame_service/error_helper.tsx b/x-pack/plugins/lens/public/editor_frame_service/error_helper.tsx index c336101033eae..475e2e17da2ea 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/error_helper.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/error_helper.tsx @@ -10,7 +10,7 @@ import { isEqual, uniqWith } from 'lodash'; import { estypes } from '@elastic/elasticsearch'; import { ExpressionRenderError } from '@kbn/expressions-plugin/public'; import type { CoreStart } from '@kbn/core/public'; -import { isEsError } from '@kbn/data-plugin/public'; +import { isEsError } from '@kbn/search-errors'; import React from 'react'; import { EuiLink } from '@elastic/eui'; import { RemovableUserMessage } from '../types'; diff --git a/x-pack/plugins/lens/public/embeddable/embeddable.tsx b/x-pack/plugins/lens/public/embeddable/embeddable.tsx index 16d730139c14a..e5d34db62ba75 100644 --- a/x-pack/plugins/lens/public/embeddable/embeddable.tsx +++ b/x-pack/plugins/lens/public/embeddable/embeddable.tsx @@ -20,11 +20,11 @@ import { TimeRange, isOfQueryType, getAggregateQueryMode, + ExecutionContextSearch, } from '@kbn/es-query'; import type { PaletteOutput } from '@kbn/coloring'; import { DataPublicPluginStart, - ExecutionContextSearch, TimefilterContract, FilterManager, getEsQueryConfig, @@ -1253,6 +1253,7 @@ export class Embeddable const input = this.getInput(); const context: ExecutionContextSearch = { + now: this.deps.data.nowProvider.get().getTime(), timeRange: input.timeslice !== undefined ? { diff --git a/x-pack/plugins/lens/public/embeddable/expression_wrapper.tsx b/x-pack/plugins/lens/public/embeddable/expression_wrapper.tsx index 82205c5b5990e..329d7573c1832 100644 --- a/x-pack/plugins/lens/public/embeddable/expression_wrapper.tsx +++ b/x-pack/plugins/lens/public/embeddable/expression_wrapper.tsx @@ -13,7 +13,7 @@ import { ReactExpressionRendererType, } from '@kbn/expressions-plugin/public'; import type { CoreStart, KibanaExecutionContext } from '@kbn/core/public'; -import { ExecutionContextSearch } from '@kbn/data-plugin/public'; +import type { ExecutionContextSearch } from '@kbn/es-query'; import { DefaultInspectorAdapters, RenderMode } from '@kbn/expressions-plugin/common'; import classNames from 'classnames'; import { getOriginalRequestErrorMessages } from '../editor_frame_service/error_helper'; diff --git a/x-pack/plugins/lens/public/expressions.ts b/x-pack/plugins/lens/public/expressions.ts index ef12b43bec71d..32856175db1b0 100644 --- a/x-pack/plugins/lens/public/expressions.ts +++ b/x-pack/plugins/lens/public/expressions.ts @@ -14,6 +14,11 @@ import { formatColumn } from '../common/expressions/format_column'; import { counterRate } from '../common/expressions/counter_rate'; import { getTimeScale } from '../common/expressions/time_scale/time_scale'; import { collapse } from '../common/expressions/collapse'; +import { + formulaIntervalFn, + formulaNowFn, + formulaTimeRangeFn, +} from '../common/expressions/formula_context'; type TimeScaleArguments = Parameters; @@ -25,6 +30,9 @@ export const setupExpressions = ( getForceNow: TimeScaleArguments[2] ) => { [ + formulaTimeRangeFn, + formulaNowFn, + formulaIntervalFn, collapse, counterRate, formatColumn, diff --git a/x-pack/plugins/lens/public/state_management/selectors.ts b/x-pack/plugins/lens/public/state_management/selectors.ts index 7572c31287297..44121c4d064c7 100644 --- a/x-pack/plugins/lens/public/state_management/selectors.ts +++ b/x-pack/plugins/lens/public/state_management/selectors.ts @@ -46,9 +46,11 @@ export const selectTriggerApplyChanges = (state: LensState) => { return shouldApply; }; +// TODO - is there any point to keeping this around since we have selectExecutionSearchContext? export const selectExecutionContext = createSelector( [selectQuery, selectFilters, selectResolvedDateRange], (query, filters, dateRange) => ({ + now: Date.now(), dateRange, query, filters, @@ -56,6 +58,7 @@ export const selectExecutionContext = createSelector( ); export const selectExecutionContextSearch = createSelector(selectExecutionContext, (res) => ({ + now: res.now, query: res.query, timeRange: { from: res.dateRange.fromDate, diff --git a/x-pack/plugins/lens/public/visualizations/metric/dimension_editor.test.tsx b/x-pack/plugins/lens/public/visualizations/metric/dimension_editor.test.tsx index 9fc42f01a9302..429a681027e63 100644 --- a/x-pack/plugins/lens/public/visualizations/metric/dimension_editor.test.tsx +++ b/x-pack/plugins/lens/public/visualizations/metric/dimension_editor.test.tsx @@ -245,9 +245,9 @@ describe('dimension editor', () => { userEvent.type(customPrefixTextbox, prefix); }; return { - settingNone: screen.getByLabelText(/none/i), - settingAuto: screen.getByLabelText(/auto/i), - settingCustom: screen.getByLabelText(/custom/i), + settingNone: screen.getByTitle(/none/i), + settingAuto: screen.getByTitle(/auto/i), + settingCustom: screen.getByTitle(/custom/i), customPrefixTextbox, typePrefix, ...rtlRender, @@ -276,9 +276,9 @@ describe('dimension editor', () => { state: localState, }); - expect(settingAuto).toBeChecked(); - expect(settingNone).not.toBeChecked(); - expect(settingCustom).not.toBeChecked(); + expect(settingAuto).toHaveAttribute('aria-pressed', 'true'); + expect(settingNone).toHaveAttribute('aria-pressed', 'false'); + expect(settingCustom).toHaveAttribute('aria-pressed', 'false'); expect(customPrefixTextbox).not.toBeInTheDocument(); }); @@ -286,9 +286,9 @@ describe('dimension editor', () => { const { settingAuto, settingCustom, settingNone, customPrefixTextbox } = renderSecondaryMetricEditor({ state: { ...localState, secondaryPrefix: NONE_PREFIX } }); - expect(settingNone).toBeChecked(); - expect(settingAuto).not.toBeChecked(); - expect(settingCustom).not.toBeChecked(); + expect(settingNone).toHaveAttribute('aria-pressed', 'true'); + expect(settingAuto).toHaveAttribute('aria-pressed', 'false'); + expect(settingCustom).toHaveAttribute('aria-pressed', 'false'); expect(customPrefixTextbox).not.toBeInTheDocument(); }); @@ -297,9 +297,9 @@ describe('dimension editor', () => { const { settingAuto, settingCustom, settingNone, customPrefixTextbox } = renderSecondaryMetricEditor({ state: customPrefixState }); - expect(settingAuto).not.toBeChecked(); - expect(settingNone).not.toBeChecked(); - expect(settingCustom).toBeChecked(); + expect(settingAuto).toHaveAttribute('aria-pressed', 'false'); + expect(settingNone).toHaveAttribute('aria-pressed', 'false'); + expect(settingCustom).toHaveAttribute('aria-pressed', 'true'); expect(customPrefixTextbox).toHaveValue(customPrefixState.secondaryPrefix); }); @@ -454,11 +454,10 @@ describe('dimension editor', () => { ); const supportingVisOptions = { - none: screen.queryByLabelText(/none/i), + none: screen.queryByTitle(/none/i), // in eui when bar or line become disabled they change from input to button so we have to do this weird check - bar: screen.queryByLabelText(/bar/i) || screen.queryByRole('button', { name: /bar/i }), - trendline: - screen.queryByLabelText(/line/i) || screen.queryByRole('button', { name: /line/i }), + bar: screen.queryByTitle(/bar/i) || screen.queryByRole('button', { name: /bar/i }), + trendline: screen.queryByTitle(/line/i) || screen.queryByRole('button', { name: /line/i }), }; const clickOnSupportingVis = (type: SupportingVisType) => { @@ -472,8 +471,8 @@ describe('dimension editor', () => { return { progressDirectionShowing: screen.queryByTestId('lnsMetric_progress_direction_buttons'), progressOptions: { - vertical: screen.queryByLabelText(/vertical/i), - horizontal: screen.queryByLabelText(/horizontal/i), + vertical: screen.queryByTitle(/vertical/i), + horizontal: screen.queryByTitle(/horizontal/i), }, supportingVisOptions, clickOnSupportingVis, @@ -501,21 +500,21 @@ describe('dimension editor', () => { const { supportingVisOptions } = renderAdditionalSectionEditor({ state: { ...stateWOTrend, showBar: false, maxAccessor: undefined }, }); - expect(supportingVisOptions.none).toBeChecked(); + expect(supportingVisOptions.none).toHaveAttribute('aria-pressed', 'true'); }); it('when `showBar` is true and maximum value is not defined, bar should be selected', () => { const { supportingVisOptions } = renderAdditionalSectionEditor({ state: { ...stateWOTrend, showBar: true }, }); - expect(supportingVisOptions.bar).toBeChecked(); + expect(supportingVisOptions.bar).toHaveAttribute('aria-pressed', 'true'); }); it('when `showBar` is true and trendline is defined, line should be selected', () => { const { supportingVisOptions } = renderAdditionalSectionEditor({ state: metricAccessorState, }); - expect(supportingVisOptions.trendline).toBeChecked(); + expect(supportingVisOptions.trendline).toHaveAttribute('aria-pressed', 'true'); }); it('should enable bar when max dimension exists', () => { @@ -633,10 +632,10 @@ describe('dimension editor', () => { state: metricAccessorState, }); - expect(progressOptions.vertical).toBeChecked(); - expect(progressOptions.horizontal).not.toBeChecked(); + expect(progressOptions.vertical).toHaveAttribute('aria-pressed', 'true'); + expect(progressOptions.horizontal).toHaveAttribute('aria-pressed', 'false'); if (progressOptions.horizontal === null) { - throw new Error('horizontal radio button not found'); + throw new Error('horizontal button not found'); } userEvent.click(progressOptions.horizontal); diff --git a/x-pack/plugins/lens/public/visualizations/metric/dimension_editor.tsx b/x-pack/plugins/lens/public/visualizations/metric/dimension_editor.tsx index c84fa45834fa0..6e71d21519231 100644 --- a/x-pack/plugins/lens/public/visualizations/metric/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/visualizations/metric/dimension_editor.tsx @@ -446,7 +446,7 @@ function StaticColorControls({ onChange={(color: string) => handleColorChange(color)} color={currentColor} aria-label={colorLabel} - showAlpha={false} + showAlpha swatches={euiPaletteColorBlind()} /> diff --git a/x-pack/plugins/lens/server/expressions/expressions.ts b/x-pack/plugins/lens/server/expressions/expressions.ts index 1e80fc5bb49a3..b5e8fc2851608 100644 --- a/x-pack/plugins/lens/server/expressions/expressions.ts +++ b/x-pack/plugins/lens/server/expressions/expressions.ts @@ -13,6 +13,9 @@ import { mapToColumns, getTimeScale, getDatatable, + formulaIntervalFn, + formulaNowFn, + formulaTimeRangeFn, } from '../../common/expressions'; import { getDatatableUtilitiesFactory, getFormatFactory, getTimeZoneFactory } from './utils'; @@ -23,6 +26,9 @@ export const setupExpressions = ( expressions: ExpressionsServerSetup ) => { [ + formulaNowFn, + formulaIntervalFn, + formulaTimeRangeFn, counterRate, formatColumn, mapToColumns, diff --git a/x-pack/plugins/lens/tsconfig.json b/x-pack/plugins/lens/tsconfig.json index 8958e1db8abfd..dd714b72c998d 100644 --- a/x-pack/plugins/lens/tsconfig.json +++ b/x-pack/plugins/lens/tsconfig.json @@ -87,6 +87,7 @@ "@kbn/serverless", "@kbn/ebt-tools", "@kbn/chart-expressions-common", + "@kbn/search-errors", "@kbn/search-response-warnings", "@kbn/logging", "@kbn/core-plugins-server", diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.test.tsx b/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.test.tsx index 5c2bfb9045688..f6b8bff8c09ab 100644 --- a/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.test.tsx +++ b/x-pack/plugins/lists/public/exceptions/components/builder/entry_renderer.test.tsx @@ -264,11 +264,15 @@ describe('BuilderEntryItem', () => { /> ); - expect(wrapper.find('[data-test-subj="exceptionBuilderEntryField"]').text()).toEqual('ip'); - expect(wrapper.find('[data-test-subj="exceptionBuilderEntryOperator"]').text()).toEqual('is'); - expect(wrapper.find('[data-test-subj="exceptionBuilderEntryFieldMatch"]').text()).toEqual( - '1234' - ); + expect( + wrapper.find('[data-test-subj="exceptionBuilderEntryField"] input').props().value + ).toEqual('ip'); + expect( + wrapper.find('[data-test-subj="operatorAutocompleteComboBox"] input').props().value + ).toEqual('is'); + expect( + wrapper.find('[data-test-subj="valuesAutocompleteMatchLabel"] input').props().value + ).toEqual('1234'); }); test('it renders field values correctly when operator is "isNotOperator"', () => { @@ -299,13 +303,15 @@ describe('BuilderEntryItem', () => { /> ); - expect(wrapper.find('[data-test-subj="exceptionBuilderEntryField"]').text()).toEqual('ip'); - expect(wrapper.find('[data-test-subj="exceptionBuilderEntryOperator"]').text()).toEqual( - 'is not' - ); - expect(wrapper.find('[data-test-subj="exceptionBuilderEntryFieldMatch"]').text()).toEqual( - '1234' - ); + expect( + wrapper.find('[data-test-subj="exceptionBuilderEntryField"] input').props().value + ).toEqual('ip'); + expect( + wrapper.find('[data-test-subj="operatorAutocompleteComboBox"] input').props().value + ).toEqual('is not'); + expect( + wrapper.find('[data-test-subj="valuesAutocompleteMatchLabel"] input').props().value + ).toEqual('1234'); }); test('it renders field values correctly when operator is "isOneOfOperator"', () => { @@ -336,11 +342,13 @@ describe('BuilderEntryItem', () => { /> ); - expect(wrapper.find('[data-test-subj="exceptionBuilderEntryField"]').text()).toEqual('ip'); - expect(wrapper.find('[data-test-subj="exceptionBuilderEntryOperator"]').text()).toEqual( - 'is one of' - ); - expect(wrapper.find('[data-test-subj="exceptionBuilderEntryFieldMatchAny"]').text()).toEqual( + expect( + wrapper.find('[data-test-subj="exceptionBuilderEntryField"] input').props().value + ).toEqual('ip'); + expect( + wrapper.find('[data-test-subj="operatorAutocompleteComboBox"] input').props().value + ).toEqual('is one of'); + expect(wrapper.find('[data-test-subj="valuesAutocompleteMatchAny"]').first().text()).toEqual( '1234' ); }); @@ -373,10 +381,12 @@ describe('BuilderEntryItem', () => { /> ); - expect(wrapper.find('[data-test-subj="exceptionBuilderEntryField"]').text()).toEqual('ip'); - expect(wrapper.find('[data-test-subj="exceptionBuilderEntryOperator"]').text()).toEqual( - 'is not one of' - ); + expect( + wrapper.find('[data-test-subj="exceptionBuilderEntryField"] input').props().value + ).toEqual('ip'); + expect( + wrapper.find('[data-test-subj="operatorAutocompleteComboBox"] input').props().value + ).toEqual('is not one of'); expect(wrapper.find('[data-test-subj="exceptionBuilderEntryFieldMatchAny"]').text()).toEqual( '1234' ); @@ -411,12 +421,15 @@ describe('BuilderEntryItem', () => { /> ); - expect(wrapper.find('[data-test-subj="exceptionBuilderEntryField"]').text()).toEqual('ip'); - expect(wrapper.find('[data-test-subj="exceptionBuilderEntryOperator"]').text()).toEqual( - 'is in list' - ); expect( - wrapper.find('[data-test-subj="valuesAutocompleteComboBox listsComboxBox"]').at(1).text() + wrapper.find('[data-test-subj="exceptionBuilderEntryField"] input').props().value + ).toEqual('ip'); + expect( + wrapper.find('[data-test-subj="operatorAutocompleteComboBox"] input').props().value + ).toEqual('is in list'); + expect( + wrapper.find('[data-test-subj="valuesAutocompleteComboBox listsComboxBox"] input').props() + .value ).toEqual('some name'); }); @@ -449,12 +462,15 @@ describe('BuilderEntryItem', () => { /> ); - expect(wrapper.find('[data-test-subj="exceptionBuilderEntryField"]').text()).toEqual('ip'); - expect(wrapper.find('[data-test-subj="exceptionBuilderEntryOperator"]').text()).toEqual( - 'is not in list' - ); expect( - wrapper.find('[data-test-subj="valuesAutocompleteComboBox listsComboxBox"]').at(1).text() + wrapper.find('[data-test-subj="exceptionBuilderEntryField"] input').props().value + ).toEqual('ip'); + expect( + wrapper.find('[data-test-subj="operatorAutocompleteComboBox"] input').props().value + ).toEqual('is not in list'); + expect( + wrapper.find('[data-test-subj="valuesAutocompleteComboBox listsComboxBox"] input').props() + .value ).toEqual('some name'); }); @@ -486,11 +502,16 @@ describe('BuilderEntryItem', () => { /> ); - expect(wrapper.find('[data-test-subj="exceptionBuilderEntryField"]').text()).toEqual('ip'); - expect(wrapper.find('[data-test-subj="exceptionBuilderEntryOperator"]').text()).toEqual( - 'exists' - ); - expect(wrapper.find('[data-test-subj="exceptionBuilderEntryFieldExists"]').text()).toEqual('—'); + expect( + wrapper.find('[data-test-subj="exceptionBuilderEntryField"] input').props().value + ).toEqual('ip'); + expect( + wrapper.find('[data-test-subj="exceptionBuilderEntryOperator"] input').props().value + ).toEqual('exists'); + expect( + wrapper.find('[data-test-subj="valuesAutocompleteComboBox existsComboxBox"] input').props() + .placeholder + ).toEqual('—'); expect( wrapper.find('[data-test-subj="exceptionBuilderEntryFieldExists"] input').props().disabled ).toBeTruthy(); @@ -524,11 +545,16 @@ describe('BuilderEntryItem', () => { /> ); - expect(wrapper.find('[data-test-subj="exceptionBuilderEntryField"]').text()).toEqual('ip'); - expect(wrapper.find('[data-test-subj="exceptionBuilderEntryOperator"]').text()).toEqual( - 'does not exist' - ); - expect(wrapper.find('[data-test-subj="exceptionBuilderEntryFieldExists"]').text()).toEqual('—'); + expect( + wrapper.find('[data-test-subj="exceptionBuilderEntryField"] input').props().value + ).toEqual('ip'); + expect( + wrapper.find('[data-test-subj="exceptionBuilderEntryOperator"] input').props().value + ).toEqual('does not exist'); + expect( + wrapper.find('[data-test-subj="valuesAutocompleteComboBox existsComboxBox"] input').props() + .placeholder + ).toEqual('—'); expect( wrapper.find('[data-test-subj="exceptionBuilderEntryFieldExists"] input').props().disabled ).toBeTruthy(); @@ -562,13 +588,15 @@ describe('BuilderEntryItem', () => { /> ); - expect(wrapper.find('[data-test-subj="exceptionBuilderEntryField"]').text()).toEqual('@tags'); - expect(wrapper.find('[data-test-subj="exceptionBuilderEntryOperator"]').text()).toEqual( - 'matches' - ); - expect(wrapper.find('[data-test-subj="exceptionBuilderEntryFieldWildcard"]').text()).toEqual( - '1234*' - ); + expect( + wrapper.find('[data-test-subj="exceptionBuilderEntryField"] input').props().value + ).toEqual('@tags'); + expect( + wrapper.find('[data-test-subj="exceptionBuilderEntryOperator"] input').props().value + ).toEqual('matches'); + expect( + wrapper.find('[data-test-subj="valuesAutocompleteWildcard"] input').props().value + ).toEqual('1234*'); // doesnt show warning label for non endpoint exception items expect( wrapper.find('[data-test-subj="valuesAutocompleteWildcardLabel"] .euiFormHelpText') @@ -637,13 +665,15 @@ describe('BuilderEntryItem', () => { /> ); - expect(wrapper.find('[data-test-subj="exceptionBuilderEntryField"]').text()).toEqual('@tags'); - expect(wrapper.find('[data-test-subj="exceptionBuilderEntryOperator"]').text()).toEqual( - 'does not match' - ); - expect(wrapper.find('[data-test-subj="exceptionBuilderEntryFieldWildcard"]').text()).toEqual( - '1234*' - ); + expect( + wrapper.find('[data-test-subj="exceptionBuilderEntryField"] input').props().value + ).toEqual('@tags'); + expect( + wrapper.find('[data-test-subj="exceptionBuilderEntryOperator"] input').props().value + ).toEqual('does not match'); + expect( + wrapper.find('[data-test-subj="valuesAutocompleteWildcard"] input').props().value + ).toEqual('1234*'); }); test('it uses "correspondingKeywordField" if it exists', () => { diff --git a/x-pack/plugins/lists/public/exceptions/components/builder/exception_items_renderer.test.tsx b/x-pack/plugins/lists/public/exceptions/components/builder/exception_items_renderer.test.tsx index 37262749ce55a..2bb1ebc275b08 100644 --- a/x-pack/plugins/lists/public/exceptions/components/builder/exception_items_renderer.test.tsx +++ b/x-pack/plugins/lists/public/exceptions/components/builder/exception_items_renderer.test.tsx @@ -58,18 +58,20 @@ describe('ExceptionBuilderComponent', () => { ); + // console.log(wrapper.find('[data-test-subj="valuesAutocompleteMatch"] input').html()); + expect(wrapper.find('EuiFlexGroup[data-test-subj="exceptionItemEntryContainer"]')).toHaveLength( 1 ); - expect(wrapper.find('[data-test-subj="exceptionBuilderEntryField"]').at(0).text()).toEqual( - 'Search' - ); - expect(wrapper.find('[data-test-subj="operatorAutocompleteComboBox"]').at(0).text()).toEqual( - 'is' - ); - expect(wrapper.find('[data-test-subj="valuesAutocompleteMatch"]').at(0).text()).toEqual( - 'Please select a field first...' - ); + expect( + wrapper.find('[data-test-subj="fieldAutocompleteComboBox"] input').props().placeholder + ).toEqual('Search'); + expect( + wrapper.find('[data-test-subj="operatorAutocompleteComboBox"] input').props().value + ).toEqual('is'); + expect( + wrapper.find('[data-test-subj="valuesAutocompleteMatch"] input').props().placeholder + ).toEqual('Please select a field first...'); }); test('it displays "exceptionListItems" that are passed in', async () => { @@ -103,15 +105,16 @@ describe('ExceptionBuilderComponent', () => { /> ); + expect(wrapper.find('EuiFlexGroup[data-test-subj="exceptionItemEntryContainer"]')).toHaveLength( 1 ); - expect(wrapper.find('[data-test-subj="exceptionBuilderEntryField"]').at(0).text()).toEqual( - 'ip' - ); - expect(wrapper.find('[data-test-subj="operatorAutocompleteComboBox"]').at(0).text()).toEqual( - 'is one of' - ); + expect( + wrapper.find('[data-test-subj="fieldAutocompleteComboBox"] input').props().value + ).toEqual('ip'); + expect( + wrapper.find('[data-test-subj="operatorAutocompleteComboBox"] input').props().value + ).toEqual('is one of'); expect(wrapper.find('[data-test-subj="valuesAutocompleteMatchAny"]').at(0).text()).toEqual( 'some ip' ); @@ -263,25 +266,27 @@ describe('ExceptionBuilderComponent', () => { expect( wrapper.find('EuiFlexGroup[data-test-subj="exceptionItemEntryContainer"]') ).toHaveLength(2); - expect(wrapper.find('[data-test-subj="exceptionBuilderEntryField"]').at(0).text()).toEqual( - 'Search' - ); - expect(wrapper.find('[data-test-subj="operatorAutocompleteComboBox"]').at(0).text()).toEqual( - 'is' - ); - expect(wrapper.find('[data-test-subj="valuesAutocompleteMatch"]').at(0).text()).toEqual( - 'Please select a field first...' - ); + expect( + wrapper.find('[data-test-subj="exceptionBuilderEntryField"] input').at(0).props() + .placeholder + ).toEqual('Search'); + expect( + wrapper.find('[data-test-subj="operatorAutocompleteComboBox"] input').at(0).props().value + ).toEqual('is'); + expect( + wrapper.find('[data-test-subj="valuesAutocompleteMatch"] input').at(0).props().placeholder + ).toEqual('Please select a field first...'); - expect(wrapper.find('[data-test-subj="exceptionBuilderEntryField"]').at(1).text()).toEqual( - 'Search' - ); - expect(wrapper.find('[data-test-subj="operatorAutocompleteComboBox"]').at(1).text()).toEqual( - 'is' - ); - expect(wrapper.find('[data-test-subj="valuesAutocompleteMatch"]').at(1).text()).toEqual( - 'Please select a field first...' - ); + expect( + wrapper.find('[data-test-subj="exceptionBuilderEntryField"] input').at(1).props() + .placeholder + ).toEqual('Search'); + expect( + wrapper.find('[data-test-subj="operatorAutocompleteComboBox"] input').at(1).props().value + ).toEqual('is'); + expect( + wrapper.find('[data-test-subj="valuesAutocompleteMatch"] input').at(1).props().placeholder + ).toEqual('Please select a field first...'); }); }); @@ -324,25 +329,25 @@ describe('ExceptionBuilderComponent', () => { const item1 = wrapper.find('EuiFlexGroup[data-test-subj="exceptionEntriesContainer"]').at(0); const item2 = wrapper.find('EuiFlexGroup[data-test-subj="exceptionEntriesContainer"]').at(1); - expect(item1.find('[data-test-subj="exceptionBuilderEntryField"]').at(0).text()).toEqual( - 'Search' - ); - expect(item1.find('[data-test-subj="operatorAutocompleteComboBox"]').at(0).text()).toEqual( - 'is' - ); - expect(item1.find('[data-test-subj="valuesAutocompleteMatch"]').at(0).text()).toEqual( - 'Please select a field first...' - ); + expect( + item1.find('[data-test-subj="exceptionBuilderEntryField"] input').at(0).props().placeholder + ).toEqual('Search'); + expect( + item1.find('[data-test-subj="operatorAutocompleteComboBox"] input').at(0).props().value + ).toEqual('is'); + expect( + item1.find('[data-test-subj="valuesAutocompleteMatch"] input').at(0).props().placeholder + ).toEqual('Please select a field first...'); - expect(item2.find('[data-test-subj="exceptionBuilderEntryField"]').at(0).text()).toEqual( - 'Search' - ); - expect(item2.find('[data-test-subj="operatorAutocompleteComboBox"]').at(0).text()).toEqual( - 'is' - ); - expect(item2.find('[data-test-subj="valuesAutocompleteMatch"]').at(0).text()).toEqual( - 'Please select a field first...' - ); + expect( + item2.find('[data-test-subj="exceptionBuilderEntryField"] input').at(0).props().placeholder + ).toEqual('Search'); + expect( + item2.find('[data-test-subj="operatorAutocompleteComboBox"] input').at(0).props().value + ).toEqual('is'); + expect( + item2.find('[data-test-subj="valuesAutocompleteMatch"] input').at(0).props().placeholder + ).toEqual('Please select a field first...'); }); }); @@ -378,27 +383,27 @@ describe('ExceptionBuilderComponent', () => { ); - expect(wrapper.find('[data-test-subj="exceptionBuilderEntryField"]').at(0).text()).toEqual( - 'ip' - ); - expect(wrapper.find('[data-test-subj="operatorAutocompleteComboBox"]').at(0).text()).toEqual( - 'is one of' - ); + expect( + wrapper.find('[data-test-subj="exceptionBuilderEntryField"] input').props().value + ).toEqual('ip'); + expect( + wrapper.find('[data-test-subj="operatorAutocompleteComboBox"] input').at(0).props().value + ).toEqual('is one of'); expect(wrapper.find('[data-test-subj="valuesAutocompleteMatchAny"]').at(0).text()).toEqual( 'some ip' ); wrapper.find('[data-test-subj="firstRowBuilderDeleteButton"] button').simulate('click'); - expect(wrapper.find('[data-test-subj="exceptionBuilderEntryField"]').at(0).text()).toEqual( - 'Search' - ); - expect(wrapper.find('[data-test-subj="operatorAutocompleteComboBox"]').at(0).text()).toEqual( - 'is' - ); - expect(wrapper.find('[data-test-subj="valuesAutocompleteMatch"]').at(0).text()).toEqual( - 'Please select a field first...' - ); + expect( + wrapper.find('[data-test-subj="exceptionBuilderEntryField"] input').at(0).props().placeholder + ).toEqual('Search'); + expect( + wrapper.find('[data-test-subj="operatorAutocompleteComboBox"] input').at(0).props().value + ).toEqual('is'); + expect( + wrapper.find('[data-test-subj="valuesAutocompleteMatch"] input').at(0).props().placeholder + ).toEqual('Please select a field first...'); }); test('it displays "and" badge if at least one exception item includes more than one entry', () => { @@ -507,14 +512,17 @@ describe('ExceptionBuilderComponent', () => { const entry2 = wrapper .find('EuiFlexGroup[data-test-subj="exceptionItemEntryContainer"]') .at(1); - expect(entry2.find('[data-test-subj="exceptionBuilderEntryField"]').at(0).text()).toEqual( - 'Search nested field' - ); - expect(entry2.find('[data-test-subj="operatorAutocompleteComboBox"]').at(0).text()).toEqual( - 'is' - ); + + expect( + entry2.find('[data-test-subj="exceptionBuilderEntryField"] input').at(0).props() + .placeholder + ).toEqual('Search nested field'); + expect( + entry2.find('[data-test-subj="operatorAutocompleteComboBox"] input').at(0).props().value + ).toEqual('is'); expect( - entry2.find('[data-test-subj="exceptionBuilderEntryFieldExists"]').at(0).text() + entry2.find('[data-test-subj="exceptionBuilderEntryFieldExists"] input').at(0).props() + .placeholder ).toEqual(getEmptyValue()); }); }); diff --git a/x-pack/plugins/log_explorer/common/constants.ts b/x-pack/plugins/log_explorer/common/constants.ts index 5aeb491b5a9d2..a73f304a76a5f 100644 --- a/x-pack/plugins/log_explorer/common/constants.ts +++ b/x-pack/plugins/log_explorer/common/constants.ts @@ -32,8 +32,17 @@ export const DATA_GRID_COLUMN_WIDTH_SMALL = 240; export const DATA_GRID_COLUMN_WIDTH_MEDIUM = 320; // UI preferences -export const DATA_GRID_DEFAULT_COLUMNS = [SERVICE_NAME_FIELD, HOST_NAME_FIELD, MESSAGE_FIELD]; -export const DATA_GRID_COLUMNS_PREFERENCES = { - [HOST_NAME_FIELD]: { width: DATA_GRID_COLUMN_WIDTH_MEDIUM }, - [SERVICE_NAME_FIELD]: { width: DATA_GRID_COLUMN_WIDTH_SMALL }, -}; +export const DEFAULT_COLUMNS = [ + { + field: SERVICE_NAME_FIELD, + width: DATA_GRID_COLUMN_WIDTH_SMALL, + }, + { + field: HOST_NAME_FIELD, + width: DATA_GRID_COLUMN_WIDTH_MEDIUM, + }, + { + field: MESSAGE_FIELD, + }, +]; +export const DEFAULT_ROWS_PER_PAGE = 100; diff --git a/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/defaults.ts b/x-pack/plugins/log_explorer/common/control_panels/available_control_panels.ts similarity index 69% rename from x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/defaults.ts rename to x-pack/plugins/log_explorer/common/control_panels/available_control_panels.ts index 2a058e2dde017..865b225585486 100644 --- a/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/defaults.ts +++ b/x-pack/plugins/log_explorer/common/control_panels/available_control_panels.ts @@ -5,18 +5,13 @@ * 2.0. */ -import { AllDatasetSelection } from '../../../../common/dataset_selection'; -import { ControlPanels, DefaultLogExplorerProfileState } from './types'; - -export const DEFAULT_CONTEXT: DefaultLogExplorerProfileState = { - datasetSelection: AllDatasetSelection.create(), -}; - -export const CONTROL_PANELS_URL_KEY = 'controlPanels'; +import { ControlPanels } from './types'; export const availableControlsPanels = { NAMESPACE: 'data_stream.namespace', -}; +} as const; + +export type AvailableControlPanels = typeof availableControlsPanels; export const controlPanelConfigs: ControlPanels = { [availableControlsPanels.NAMESPACE]: { diff --git a/x-pack/plugins/log_explorer/common/control_panels/index.ts b/x-pack/plugins/log_explorer/common/control_panels/index.ts new file mode 100644 index 0000000000000..6ea70a7fd630d --- /dev/null +++ b/x-pack/plugins/log_explorer/common/control_panels/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './available_control_panels'; +export * from './types'; diff --git a/x-pack/plugins/log_explorer/common/control_panels/types.ts b/x-pack/plugins/log_explorer/common/control_panels/types.ts new file mode 100644 index 0000000000000..22381932d34b6 --- /dev/null +++ b/x-pack/plugins/log_explorer/common/control_panels/types.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import * as rt from 'io-ts'; + +const PanelRT = rt.type({ + order: rt.number, + width: rt.union([rt.literal('medium'), rt.literal('small'), rt.literal('large')]), + grow: rt.boolean, + type: rt.string, + explicitInput: rt.intersection([ + rt.type({ id: rt.string }), + rt.partial({ + dataViewId: rt.string, + exclude: rt.boolean, + existsSelected: rt.boolean, + fieldName: rt.string, + selectedOptions: rt.array(rt.string), + title: rt.union([rt.string, rt.undefined]), + }), + ]), +}); + +export const ControlPanelRT = rt.record(rt.string, PanelRT); + +export type ControlPanels = rt.TypeOf; diff --git a/x-pack/plugins/log_explorer/common/dataset_selection/all_dataset_selection.ts b/x-pack/plugins/log_explorer/common/dataset_selection/all_dataset_selection.ts index c505a07da7768..8b8ab4e1ea241 100644 --- a/x-pack/plugins/log_explorer/common/dataset_selection/all_dataset_selection.ts +++ b/x-pack/plugins/log_explorer/common/dataset_selection/all_dataset_selection.ts @@ -6,7 +6,6 @@ */ import { Dataset } from '../datasets'; -import { encodeDatasetSelection } from './encoding'; import { DatasetSelectionStrategy } from './types'; export class AllDatasetSelection implements DatasetSelectionStrategy { @@ -23,18 +22,13 @@ export class AllDatasetSelection implements DatasetSelectionStrategy { } toDataviewSpec() { - const { name, title } = this.selection.dataset.toDataviewSpec(); - return { - id: this.toURLSelectionId(), - name, - title, - }; + return this.selection.dataset.toDataviewSpec(); } - toURLSelectionId() { - return encodeDatasetSelection({ + toPlainSelection() { + return { selectionType: this.selectionType, - }); + }; } public static create() { diff --git a/x-pack/plugins/log_explorer/common/dataset_selection/encoding.test.ts b/x-pack/plugins/log_explorer/common/dataset_selection/encoding.test.ts deleted file mode 100644 index d88d939858d0e..0000000000000 --- a/x-pack/plugins/log_explorer/common/dataset_selection/encoding.test.ts +++ /dev/null @@ -1,100 +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 { IndexPattern } from '@kbn/io-ts-utils'; -import { encodeDatasetSelection, decodeDatasetSelectionId } from './encoding'; -import { DatasetEncodingError } from './errors'; -import { DatasetSelectionPlain } from './types'; - -describe('DatasetSelection', () => { - const allDatasetSelectionPlain: DatasetSelectionPlain = { - selectionType: 'all', - }; - const encodedAllDatasetSelection = 'BQZwpgNmDGAuCWB7AdgFQJ4AcwC4CGEEAlEA'; - - const singleDatasetSelectionPlain: DatasetSelectionPlain = { - selectionType: 'single', - selection: { - name: 'azure', - version: '1.5.23', - dataset: { - name: 'logs-azure.activitylogs-*' as IndexPattern, - title: 'activitylogs', - }, - }, - }; - const encodedSingleDatasetSelection = - 'BQZwpgNmDGAuCWB7AdgLmAEwIay+W6yWAtmKgOQSIDmIAtFgF4CuATmAHRZzwBu8sAJ5VadAFTkANAlhRU3BPyEiQASklFS8lu0m8wrEEjTkAjBwCsHAEwBmcuvBQeKACqCADmSPJqUVUA=='; - - const invalidDatasetSelectionPlain = { - selectionType: 'single', - selection: { - dataset: { - // Missing mandatory `name` property - title: 'activitylogs', - }, - }, - }; - const invalidCompressedId = 'random'; - const invalidEncodedDatasetSelection = 'BQZwpgNmDGAuCWB7AdgFQJ4AcwC4T2QHMoBKIA=='; - - describe('#encodeDatasetSelection', () => { - test('should encode and compress a valid DatasetSelection plain object', () => { - // Encode AllDatasetSelection plain object - expect(encodeDatasetSelection(allDatasetSelectionPlain)).toEqual(encodedAllDatasetSelection); - // Encode SingleDatasetSelection plain object - expect(encodeDatasetSelection(singleDatasetSelectionPlain)).toEqual( - encodedSingleDatasetSelection - ); - }); - - test('should throw a DatasetEncodingError if the input is an invalid DatasetSelection plain object', () => { - const encodingRunner = () => - encodeDatasetSelection(invalidDatasetSelectionPlain as DatasetSelectionPlain); - - expect(encodingRunner).toThrow(DatasetEncodingError); - expect(encodingRunner).toThrow(/^The current dataset selection is invalid/); - }); - }); - - describe('#decodeDatasetSelectionId', () => { - test('should decode and decompress a valid encoded string', () => { - // Decode AllDatasetSelection plain object - expect(decodeDatasetSelectionId(encodedAllDatasetSelection)).toEqual( - allDatasetSelectionPlain - ); - // Decode SingleDatasetSelection plain object - expect(decodeDatasetSelectionId(encodedSingleDatasetSelection)).toEqual( - singleDatasetSelectionPlain - ); - }); - - test('should throw a DatasetEncodingError if the input is an invalid compressed id', () => { - expect(() => decodeDatasetSelectionId(invalidCompressedId)).toThrow( - new DatasetEncodingError('The stored id is not a valid compressed value.') - ); - }); - - test('should throw a DatasetEncodingError if the decompressed value is an invalid DatasetSelection plain object', () => { - const decodingRunner = () => decodeDatasetSelectionId(invalidEncodedDatasetSelection); - - expect(decodingRunner).toThrow(DatasetEncodingError); - expect(decodingRunner).toThrow(/^The current dataset selection is invalid/); - }); - }); - - test('encoding and decoding should restore the original DatasetSelection plain object', () => { - // Encode/Decode AllDatasetSelection plain object - expect(decodeDatasetSelectionId(encodeDatasetSelection(allDatasetSelectionPlain))).toEqual( - allDatasetSelectionPlain - ); - // Encode/Decode SingleDatasetSelection plain object - expect(decodeDatasetSelectionId(encodeDatasetSelection(singleDatasetSelectionPlain))).toEqual( - singleDatasetSelectionPlain - ); - }); -}); diff --git a/x-pack/plugins/log_explorer/common/dataset_selection/encoding.ts b/x-pack/plugins/log_explorer/common/dataset_selection/encoding.ts deleted file mode 100644 index 83a7c5357fde5..0000000000000 --- a/x-pack/plugins/log_explorer/common/dataset_selection/encoding.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { decode, encode, RisonValue } from '@kbn/rison'; -import * as lz from 'lz-string'; -import { decodeOrThrow } from '../runtime_types'; -import { DatasetEncodingError } from './errors'; -import { DatasetSelectionPlain, datasetSelectionPlainRT } from './types'; - -export const encodeDatasetSelection = (datasetSelectionPlain: DatasetSelectionPlain) => { - const safeDatasetSelection = decodeOrThrow( - datasetSelectionPlainRT, - (message: string) => - new DatasetEncodingError(`The current dataset selection is invalid: ${message}"`) - )(datasetSelectionPlain); - - return lz.compressToBase64(encode(safeDatasetSelection)); -}; - -export const decodeDatasetSelectionId = (datasetSelectionId: string): DatasetSelectionPlain => { - const risonDatasetSelection: RisonValue = lz.decompressFromBase64(datasetSelectionId); - - if (risonDatasetSelection === null || risonDatasetSelection === '') { - throw new DatasetEncodingError('The stored id is not a valid compressed value.'); - } - - const decodedDatasetSelection = decode(risonDatasetSelection); - - const datasetSelection = decodeOrThrow( - datasetSelectionPlainRT, - (message: string) => - new DatasetEncodingError(`The current dataset selection is invalid: ${message}"`) - )(decodedDatasetSelection); - - return datasetSelection; -}; diff --git a/x-pack/plugins/log_explorer/common/dataset_selection/hydrate_dataset_selection.ts.ts b/x-pack/plugins/log_explorer/common/dataset_selection/hydrate_dataset_selection.ts.ts index 43faebc618140..f881e90723e14 100644 --- a/x-pack/plugins/log_explorer/common/dataset_selection/hydrate_dataset_selection.ts.ts +++ b/x-pack/plugins/log_explorer/common/dataset_selection/hydrate_dataset_selection.ts.ts @@ -13,11 +13,9 @@ import { UnresolvedDatasetSelection } from './unresolved_dataset_selection'; export const hydrateDatasetSelection = (datasetSelection: DatasetSelectionPlain) => { if (datasetSelection.selectionType === 'all') { return AllDatasetSelection.create(); - } - if (datasetSelection.selectionType === 'single') { + } else if (datasetSelection.selectionType === 'single') { return SingleDatasetSelection.fromSelection(datasetSelection.selection); - } - if (datasetSelection.selectionType === 'unresolved') { + } else { return UnresolvedDatasetSelection.fromSelection(datasetSelection.selection); } }; diff --git a/x-pack/plugins/log_explorer/common/dataset_selection/index.ts b/x-pack/plugins/log_explorer/common/dataset_selection/index.ts index f390f7a89f87c..26fd974d0f0ac 100644 --- a/x-pack/plugins/log_explorer/common/dataset_selection/index.ts +++ b/x-pack/plugins/log_explorer/common/dataset_selection/index.ts @@ -28,7 +28,6 @@ export const isDatasetSelection = (input: any): input is DatasetSelection => { export * from './all_dataset_selection'; export * from './single_dataset_selection'; export * from './unresolved_dataset_selection'; -export * from './encoding'; export * from './errors'; export * from './hydrate_dataset_selection.ts'; export * from './types'; diff --git a/x-pack/plugins/log_explorer/common/dataset_selection/single_dataset_selection.ts b/x-pack/plugins/log_explorer/common/dataset_selection/single_dataset_selection.ts index 21c788579ed70..6667dd55f3abe 100644 --- a/x-pack/plugins/log_explorer/common/dataset_selection/single_dataset_selection.ts +++ b/x-pack/plugins/log_explorer/common/dataset_selection/single_dataset_selection.ts @@ -6,7 +6,6 @@ */ import { Dataset } from '../datasets'; -import { encodeDatasetSelection } from './encoding'; import { DatasetSelectionStrategy, SingleDatasetSelectionPayload } from './types'; export class SingleDatasetSelection implements DatasetSelectionStrategy { @@ -29,16 +28,11 @@ export class SingleDatasetSelection implements DatasetSelectionStrategy { } toDataviewSpec() { - const { name, title } = this.selection.dataset.toDataviewSpec(); - return { - id: this.toURLSelectionId(), - name, - title, - }; + return this.selection.dataset.toDataviewSpec(); } - toURLSelectionId() { - return encodeDatasetSelection({ + toPlainSelection() { + return { selectionType: this.selectionType, selection: { name: this.selection.name, @@ -46,7 +40,7 @@ export class SingleDatasetSelection implements DatasetSelectionStrategy { version: this.selection.version, dataset: this.selection.dataset.toPlain(), }, - }); + }; } public static fromSelection(selection: SingleDatasetSelectionPayload) { diff --git a/x-pack/plugins/log_explorer/common/dataset_selection/types.ts b/x-pack/plugins/log_explorer/common/dataset_selection/types.ts index 239bbc1108a29..db3638aff6331 100644 --- a/x-pack/plugins/log_explorer/common/dataset_selection/types.ts +++ b/x-pack/plugins/log_explorer/common/dataset_selection/types.ts @@ -62,7 +62,11 @@ export type UnresolvedDatasetSelectionPayload = rt.TypeOf< >; export type DatasetSelectionPlain = rt.TypeOf; +export type DataViewSpecWithId = DataViewSpec & { + id: string; +}; + export interface DatasetSelectionStrategy { - toDataviewSpec(): DataViewSpec; - toURLSelectionId(): string; + toDataviewSpec(): DataViewSpecWithId; + toPlainSelection(): DatasetSelectionPlain; } diff --git a/x-pack/plugins/log_explorer/common/dataset_selection/unresolved_dataset_selection.ts b/x-pack/plugins/log_explorer/common/dataset_selection/unresolved_dataset_selection.ts index acfd5180f0ed3..e534403fab617 100644 --- a/x-pack/plugins/log_explorer/common/dataset_selection/unresolved_dataset_selection.ts +++ b/x-pack/plugins/log_explorer/common/dataset_selection/unresolved_dataset_selection.ts @@ -6,7 +6,6 @@ */ import { Dataset } from '../datasets'; -import { encodeDatasetSelection } from './encoding'; import { DatasetSelectionStrategy, UnresolvedDatasetSelectionPayload } from './types'; export class UnresolvedDatasetSelection implements DatasetSelectionStrategy { @@ -25,22 +24,17 @@ export class UnresolvedDatasetSelection implements DatasetSelectionStrategy { } toDataviewSpec() { - const { name, title } = this.selection.dataset.toDataviewSpec(); - return { - id: this.toURLSelectionId(), - name, - title, - }; + return this.selection.dataset.toDataviewSpec(); } - toURLSelectionId() { - return encodeDatasetSelection({ + toPlainSelection() { + return { selectionType: this.selectionType, selection: { name: this.selection.name, dataset: this.selection.dataset.toPlain(), }, - }); + }; } public static fromSelection(selection: UnresolvedDatasetSelectionPayload) { diff --git a/x-pack/plugins/log_explorer/common/datasets/models/dataset.ts b/x-pack/plugins/log_explorer/common/datasets/models/dataset.ts index 6c18f62350e41..18545b24754d8 100644 --- a/x-pack/plugins/log_explorer/common/datasets/models/dataset.ts +++ b/x-pack/plugins/log_explorer/common/datasets/models/dataset.ts @@ -6,9 +6,9 @@ */ import { IconType } from '@elastic/eui'; -import { DataViewSpec } from '@kbn/data-views-plugin/common'; import { IndexPattern } from '@kbn/io-ts-utils'; import { TIMESTAMP_FIELD } from '../../constants'; +import { DataViewSpecWithId } from '../../dataset_selection'; import { DatasetId, DatasetType, IntegrationType } from '../types'; type IntegrationBase = Partial>; @@ -49,7 +49,7 @@ export class Dataset { return `${type}-${dataset}-*` as IndexPattern; } - toDataviewSpec(): DataViewSpec { + toDataviewSpec(): DataViewSpecWithId { // Invert the property because the API returns the index pattern as `name` and a readable name as `title` return { id: this.id, diff --git a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_cloud_id/index.ts b/x-pack/plugins/log_explorer/common/display_options/index.ts similarity index 79% rename from x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_cloud_id/index.ts rename to x-pack/plugins/log_explorer/common/display_options/index.ts index 950ac3cbda25d..6cc0ccaa93a6d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/elasticsearch/components/elasticsearch_cloud_id/index.ts +++ b/x-pack/plugins/log_explorer/common/display_options/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export { ElasticsearchCloudId } from './elasticsearch_cloud_id'; +export * from './types'; diff --git a/x-pack/plugins/log_explorer/common/display_options/types.ts b/x-pack/plugins/log_explorer/common/display_options/types.ts new file mode 100644 index 0000000000000..b4c482d088a54 --- /dev/null +++ b/x-pack/plugins/log_explorer/common/display_options/types.ts @@ -0,0 +1,43 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export interface ChartDisplayOptions { + breakdownField: string | null; +} + +export type PartialChartDisplayOptions = Partial; + +export interface GridColumnDisplayOptions { + field: string; + width?: number; +} + +export interface GridRowsDisplayOptions { + rowHeight: number; + rowsPerPage: number; +} + +export type PartialGridRowsDisplayOptions = Partial; + +export interface GridDisplayOptions { + columns: GridColumnDisplayOptions[]; + rows: GridRowsDisplayOptions; +} + +export type PartialGridDisplayOptions = Partial< + Omit & { rows?: PartialGridRowsDisplayOptions } +>; + +export interface DisplayOptions { + grid: GridDisplayOptions; + chart: ChartDisplayOptions; +} + +export interface PartialDisplayOptions { + grid?: PartialGridDisplayOptions; + chart?: PartialChartDisplayOptions; +} diff --git a/x-pack/plugins/log_explorer/common/index.ts b/x-pack/plugins/log_explorer/common/index.ts index 989f981879ac0..5466a00ae0caa 100644 --- a/x-pack/plugins/log_explorer/common/index.ts +++ b/x-pack/plugins/log_explorer/common/index.ts @@ -5,4 +5,28 @@ * 2.0. */ -export { AllDatasetSelection, UnresolvedDatasetSelection } from './dataset_selection'; +export { + availableControlPanelFields, + availableControlsPanels, + controlPanelConfigs, + ControlPanelRT, +} from './control_panels'; +export type { AvailableControlPanels, ControlPanels } from './control_panels'; +export { + AllDatasetSelection, + datasetSelectionPlainRT, + hydrateDatasetSelection, + UnresolvedDatasetSelection, +} from './dataset_selection'; +export type { DatasetSelectionPlain } from './dataset_selection'; +export type { + ChartDisplayOptions, + DisplayOptions, + GridColumnDisplayOptions, + GridDisplayOptions, + GridRowsDisplayOptions, + PartialChartDisplayOptions, + PartialDisplayOptions, + PartialGridDisplayOptions, + PartialGridRowsDisplayOptions, +} from './display_options'; diff --git a/x-pack/plugins/log_explorer/kibana.jsonc b/x-pack/plugins/log_explorer/kibana.jsonc index 71781ca9cada3..9275751ca1898 100644 --- a/x-pack/plugins/log_explorer/kibana.jsonc +++ b/x-pack/plugins/log_explorer/kibana.jsonc @@ -12,16 +12,18 @@ "logExplorer" ], "requiredPlugins": [ + "controls", "data", "dataViews", "discover", + "embeddable", "fieldFormats", "fleet", "kibanaReact", "kibanaUtils", - "controls", - "embeddable", + "navigation", "share", + "unifiedSearch" ], "optionalPlugins": [], "requiredBundles": [], diff --git a/x-pack/plugins/log_explorer/public/components/dataset_selector/sub_components/esql_selector.tsx b/x-pack/plugins/log_explorer/public/components/dataset_selector/sub_components/esql_selector.tsx index fec07114d939d..65ca1f1fd22e8 100644 --- a/x-pack/plugins/log_explorer/public/components/dataset_selector/sub_components/esql_selector.tsx +++ b/x-pack/plugins/log_explorer/public/components/dataset_selector/sub_components/esql_selector.tsx @@ -7,7 +7,7 @@ import { EuiBadge, EuiButton, EuiHorizontalRule } from '@elastic/eui'; import React from 'react'; -import { getRouterLinkProps } from '../../../utils/get_router_link_props'; +import { getRouterLinkProps } from '@kbn/router-utils'; import { DiscoverEsqlUrlProps } from '../../../hooks/use_esql'; import { technicalPreview, tryEsql } from '../constants'; diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_detail.tsx b/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_detail.tsx index 66c4788a32ae2..07e6b3cc6629a 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_detail.tsx +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_detail.tsx @@ -6,22 +6,19 @@ */ import React from 'react'; -import { FlyoutProps, LogDocument } from './types'; +import { LogExplorerFlyoutContentProps } from './types'; import { useDocDetail } from './use_doc_detail'; import { FlyoutHeader } from './flyout_header'; import { FlyoutHighlights } from './flyout_highlights'; +import { DiscoverActionsProvider } from '../../hooks/use_discover_action'; -export function FlyoutDetail({ - dataView, - doc, - actions, -}: Pick) { - const parsedDoc = useDocDetail(doc as LogDocument, { dataView }); +export function FlyoutDetail({ dataView, doc, actions }: LogExplorerFlyoutContentProps) { + const parsedDoc = useDocDetail(doc, { dataView }); return ( - <> + - - + + ); } diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_header.tsx b/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_header.tsx index fd1c78787c8e4..f49e3d9003949 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_header.tsx +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_header.tsx @@ -13,6 +13,7 @@ import { LogLevel } from './sub_components/log_level'; import { Timestamp } from './sub_components/timestamp'; import * as constants from '../../../common/constants'; import { flyoutMessageLabel } from './translations'; +import { HoverActionPopover } from './sub_components/hover_popover_action'; export function FlyoutHeader({ doc }: { doc: FlyoutDoc }) { const { hasTimestamp, hasLogLevel, hasMessage, hasBadges, hasFlyoutHeader } = @@ -23,9 +24,14 @@ export function FlyoutHeader({ doc }: { doc: FlyoutDoc }) { {hasBadges && ( {hasLogLevel && ( - - - + + + + + )} {hasTimestamp && ( @@ -49,9 +55,15 @@ export function FlyoutHeader({ doc }: { doc: FlyoutDoc }) { - - {flyoutMessageLabel} - + + + {flyoutMessageLabel} + + {logLevelAndTimestamp} diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx b/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx index 97b924b00a307..43b690807e3aa 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/flyout_highlights.tsx @@ -5,7 +5,6 @@ * 2.0. */ import React from 'react'; -import { FlyoutContentActions } from '@kbn/discover-plugin/public'; import { AgentIcon, CloudProvider, CloudProviderIcon } from '@kbn/custom-icons'; import { useMeasure } from 'react-use/lib'; import { AgentName } from '@kbn/elastic-agent-utils'; @@ -34,189 +33,184 @@ import { serviceAccordionTitle, } from './translations'; import { HighlightSection } from './sub_components/highlight_section'; -import { DiscoverActionsProvider } from '../../hooks/use_discover_action'; import { HighlightContainer } from './sub_components/highlight_container'; import { useFlyoutColumnWidth } from '../../hooks/use_flyouot_column_width'; export function FlyoutHighlights({ formattedDoc, flattenedDoc, - actions, }: { formattedDoc: FlyoutDoc; flattenedDoc: LogDocument['flattened']; - actions: FlyoutContentActions; }) { const [ref, dimensions] = useMeasure(); const { columns, fieldWidth } = useFlyoutColumnWidth(dimensions.width); return ( - - - {/* Service highlight */} - - {formattedDoc[constants.SERVICE_NAME_FIELD] && ( - - } - label={flyoutServiceLabel} - value={flattenedDoc[constants.SERVICE_NAME_FIELD]} - width={fieldWidth} - /> - )} - {formattedDoc[constants.TRACE_ID_FIELD] && ( - - )} - - {/* Infrastructure highlight */} - - {formattedDoc[constants.HOST_NAME_FIELD] && ( - - )} - {formattedDoc[constants.ORCHESTRATOR_CLUSTER_NAME_FIELD] && ( - - )} - {formattedDoc[constants.ORCHESTRATOR_RESOURCE_ID_FIELD] && ( - - )} - - {/* Cloud highlight */} - - {formattedDoc[constants.CLOUD_PROVIDER_FIELD] && ( - - } - label={flyoutCloudProviderLabel} - value={flattenedDoc[constants.CLOUD_PROVIDER_FIELD]} - width={fieldWidth} - /> - )} - {formattedDoc[constants.CLOUD_REGION_FIELD] && ( - - )} - {formattedDoc[constants.CLOUD_AVAILABILITY_ZONE_FIELD] && ( - - )} - {formattedDoc[constants.CLOUD_PROJECT_ID_FIELD] && ( - - )} - {formattedDoc[constants.CLOUD_INSTANCE_ID_FIELD] && ( - - )} - - {/* Other highlights */} - - {formattedDoc[constants.LOG_FILE_PATH_FIELD] && ( - - )} - {formattedDoc[constants.DATASTREAM_NAMESPACE_FIELD] && ( - - )} - {formattedDoc[constants.DATASTREAM_DATASET_FIELD] && ( - - )} - {formattedDoc[constants.AGENT_NAME_FIELD] && ( - - )} - - - + + {/* Service highlight */} + + {formattedDoc[constants.SERVICE_NAME_FIELD] && ( + + } + label={flyoutServiceLabel} + value={flattenedDoc[constants.SERVICE_NAME_FIELD]} + width={fieldWidth} + /> + )} + {formattedDoc[constants.TRACE_ID_FIELD] && ( + + )} + + {/* Infrastructure highlight */} + + {formattedDoc[constants.HOST_NAME_FIELD] && ( + + )} + {formattedDoc[constants.ORCHESTRATOR_CLUSTER_NAME_FIELD] && ( + + )} + {formattedDoc[constants.ORCHESTRATOR_RESOURCE_ID_FIELD] && ( + + )} + + {/* Cloud highlight */} + + {formattedDoc[constants.CLOUD_PROVIDER_FIELD] && ( + + } + label={flyoutCloudProviderLabel} + value={flattenedDoc[constants.CLOUD_PROVIDER_FIELD]} + width={fieldWidth} + /> + )} + {formattedDoc[constants.CLOUD_REGION_FIELD] && ( + + )} + {formattedDoc[constants.CLOUD_AVAILABILITY_ZONE_FIELD] && ( + + )} + {formattedDoc[constants.CLOUD_PROJECT_ID_FIELD] && ( + + )} + {formattedDoc[constants.CLOUD_INSTANCE_ID_FIELD] && ( + + )} + + {/* Other highlights */} + + {formattedDoc[constants.LOG_FILE_PATH_FIELD] && ( + + )} + {formattedDoc[constants.DATASTREAM_NAMESPACE_FIELD] && ( + + )} + {formattedDoc[constants.DATASTREAM_DATASET_FIELD] && ( + + )} + {formattedDoc[constants.AGENT_NAME_FIELD] && ( + + )} + + ); } diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_field.tsx b/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_field.tsx index d6c8d81030b5b..7e039497a9bd2 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_field.tsx +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/highlight_field.tsx @@ -5,20 +5,15 @@ * 2.0. */ -import { EuiFlexGroup, EuiFlexItem, EuiText, copyToClipboard, EuiTextTruncate } from '@elastic/eui'; -import React, { ReactNode, useMemo, useState } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiText, EuiTextTruncate } from '@elastic/eui'; +import React, { ReactNode } from 'react'; import { ValuesType } from 'utility-types'; -import { - flyoutHoverActionFilterForText, - flyoutHoverActionFilterOutText, - flyoutHoverActionFilterForFieldPresentText, - flyoutHoverActionToggleColumnText, - flyoutHoverActionCopyToClipboardText, -} from '../translations'; -import { useDiscoverActionsContext } from '../../../hooks/use_discover_action'; -import { HoverActionPopover, HoverActionType } from './hover_popover_action'; +import { dynamic } from '../../../utils/dynamic'; +import { HoverActionPopover } from './hover_popover_action'; import { LogDocument } from '../types'; +const HighlightFieldDescription = dynamic(() => import('./highlight_field_description')); + interface HighlightFieldProps { field: string; formattedValue: string; @@ -37,70 +32,22 @@ export function HighlightField({ width, ...props }: HighlightFieldProps) { - const filterForText = flyoutHoverActionFilterForText(value); - const filterOutText = flyoutHoverActionFilterOutText(value); - const actions = useDiscoverActionsContext(); - const [columnAdded, setColumnAdded] = useState(false); - - const hoverActions: HoverActionType[] = useMemo( - () => [ - { - id: 'addToFilterAction', - tooltipContent: filterForText, - iconType: 'plusInCircle', - onClick: () => actions?.addFilter && actions.addFilter(field, value, '+'), - display: true, - }, - { - id: 'removeFromFilterAction', - tooltipContent: filterOutText, - iconType: 'minusInCircle', - onClick: () => actions?.addFilter && actions.addFilter(field, value, '-'), - display: true, - }, - { - id: 'filterForFieldPresentAction', - tooltipContent: flyoutHoverActionFilterForFieldPresentText, - iconType: 'filter', - onClick: () => actions?.addFilter && actions.addFilter('_exists_', field, '+'), - display: true, - }, - { - id: 'toggleColumnAction', - tooltipContent: flyoutHoverActionToggleColumnText, - iconType: 'listAdd', - onClick: () => { - if (actions) { - if (columnAdded) { - actions?.removeColumn?.(field); - } else { - actions?.addColumn?.(field); - } - setColumnAdded(!columnAdded); - } - }, - display: true, - }, - { - id: 'copyToClipboardAction', - tooltipContent: flyoutHoverActionCopyToClipboardText, - iconType: 'copyClipboard', - onClick: () => copyToClipboard(value as string), - display: true, - }, - ], - [filterForText, filterOutText, actions, field, value, columnAdded] - ); - return formattedValue ? ( - - {label} - + + + + {label} + + + + + + - + + {type && ( + + + + )} + {fieldName} + + ); + + return ; +} + +// eslint-disable-next-line import/no-default-export +export default HighlightFieldDescription; diff --git a/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/hover_popover_action.tsx b/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/hover_popover_action.tsx index 766b87d54b787..9f54e7199eec8 100644 --- a/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/hover_popover_action.tsx +++ b/x-pack/plugins/log_explorer/public/components/flyout_detail/sub_components/hover_popover_action.tsx @@ -9,29 +9,33 @@ import React, { useRef, useState } from 'react'; import { EuiFlexGroup, EuiPopover, - IconType, EuiButtonIcon, EuiPopoverTitle, EuiToolTip, + PopoverAnchorPosition, } from '@elastic/eui'; - -export interface HoverActionType { - id: string; - tooltipContent: string; - iconType: IconType; - onClick: () => void; - display: boolean; -} +import { ValuesType } from 'utility-types'; +import { useHoverActions } from '../../../hooks/use_hover_actions'; +import { LogDocument } from '..'; interface HoverPopoverActionProps { children: React.ReactChild; - actions: HoverActionType[]; - title: string; + field: string; + value: ValuesType; + title?: string; + anchorPosition?: PopoverAnchorPosition; } -export const HoverActionPopover = ({ children, actions, title }: HoverPopoverActionProps) => { +export const HoverActionPopover = ({ + children, + title, + field, + value, + anchorPosition = 'upCenter', +}: HoverPopoverActionProps) => { const [isPopoverOpen, setIsPopoverOpen] = useState(false); const leaveTimer = useRef(null); + const hoverActions = useHoverActions({ field, value }); // The timeout hack is required because we are using a Popover which ideally should be used with a mouseclick, // but we are using it as a Tooltip. Which means we now need to manually handle the open and close @@ -53,15 +57,17 @@ export const HoverActionPopover = ({ children, actions, title }: HoverPopoverAct - - {title} - + {title && ( + + {title} + + )} - {actions.map((action) => ( + {hoverActions.map((action) => ( + { dataView }: Pick ): FlyoutDoc { const { services } = useKibanaContextForPlugin(); diff --git a/x-pack/plugins/log_explorer/public/components/log_explorer/log_explorer.tsx b/x-pack/plugins/log_explorer/public/components/log_explorer/log_explorer.tsx index 57736dd4b96dd..638ca847f6549 100644 --- a/x-pack/plugins/log_explorer/public/components/log_explorer/log_explorer.tsx +++ b/x-pack/plugins/log_explorer/public/components/log_explorer/log_explorer.tsx @@ -5,100 +5,43 @@ * 2.0. */ +import type { ScopedHistory } from '@kbn/core-application-browser'; +import type { CoreStart } from '@kbn/core/public'; import React, { useMemo } from 'react'; -import { ScopedHistory } from '@kbn/core-application-browser'; -import { DataPublicPluginStart } from '@kbn/data-plugin/public'; -import { DiscoverAppState } from '@kbn/discover-plugin/public'; -import type { BehaviorSubject } from 'rxjs'; -import { CoreStart } from '@kbn/core/public'; -import { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; -import { HIDE_ANNOUNCEMENTS } from '@kbn/discover-utils'; +import type { LogExplorerController } from '../../controller'; import { createLogExplorerProfileCustomizations } from '../../customizations/log_explorer_profile'; -import { createPropertyGetProxy } from '../../utils/proxies'; -import { LogExplorerProfileContext } from '../../state_machines/log_explorer_profile'; import { LogExplorerStartDeps } from '../../types'; -import { LogExplorerCustomizations } from './types'; export interface CreateLogExplorerArgs { core: CoreStart; plugins: LogExplorerStartDeps; } -export interface LogExplorerStateContainer { - appState?: DiscoverAppState; - logExplorerState?: Partial; -} - export interface LogExplorerProps { - customizations?: LogExplorerCustomizations; scopedHistory: ScopedHistory; - state$?: BehaviorSubject; + controller: LogExplorerController; } export const createLogExplorer = ({ core, plugins }: CreateLogExplorerArgs) => { const { - data, discover: { DiscoverContainer }, } = plugins; - const overrideServices = { - data: createDataServiceProxy(data), - uiSettings: createUiSettingsServiceProxy(core.uiSettings), - }; - - return ({ customizations = {}, scopedHistory, state$ }: LogExplorerProps) => { + return ({ scopedHistory, controller }: LogExplorerProps) => { const logExplorerCustomizations = useMemo( - () => [createLogExplorerProfileCustomizations({ core, customizations, plugins, state$ })], - [customizations, state$] + () => [createLogExplorerProfileCustomizations({ controller, core, plugins })], + [controller] ); + const { urlStateStorage, ...overrideServices } = controller.discoverServices; + return ( ); }; }; - -/** - * Create proxy for the data service, in which session service enablement calls - * are no-ops. - */ -const createDataServiceProxy = (data: DataPublicPluginStart) => { - const noOpEnableStorage = () => {}; - - const sessionServiceProxy = createPropertyGetProxy(data.search.session, { - enableStorage: () => noOpEnableStorage, - }); - - const searchServiceProxy = createPropertyGetProxy(data.search, { - session: () => sessionServiceProxy, - }); - - return createPropertyGetProxy(data, { - search: () => searchServiceProxy, - }); -}; -/** - * Create proxy for the uiSettings service, in which settings preferences are overwritten - * with custom values - */ -const createUiSettingsServiceProxy = (uiSettings: IUiSettingsClient) => { - const overrides: Record = { - [HIDE_ANNOUNCEMENTS]: true, - }; - - return createPropertyGetProxy(uiSettings, { - get: - () => - (key, ...args) => { - if (key in overrides) { - return overrides[key]; - } - - return uiSettings.get(key, ...args); - }, - }); -}; diff --git a/x-pack/plugins/log_explorer/public/components/log_explorer/types.ts b/x-pack/plugins/log_explorer/public/components/log_explorer/types.ts deleted file mode 100644 index 2b366cce7c55c..0000000000000 --- a/x-pack/plugins/log_explorer/public/components/log_explorer/types.ts +++ /dev/null @@ -1,25 +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 { DataTableRecord } from '@kbn/discover-utils/types'; - -export type RenderPreviousContent = () => React.ReactNode; - -export interface LogExplorerFlyoutContentProps { - doc: DataTableRecord; -} - -export type FlyoutRenderContent = ( - renderPreviousContent: RenderPreviousContent, - props: LogExplorerFlyoutContentProps -) => React.ReactNode; - -export interface LogExplorerCustomizations { - flyout?: { - renderContent?: FlyoutRenderContent; - }; -} diff --git a/x-pack/plugins/log_explorer/public/controller/controller_customizations.ts b/x-pack/plugins/log_explorer/public/controller/controller_customizations.ts new file mode 100644 index 0000000000000..4f10b2e39be44 --- /dev/null +++ b/x-pack/plugins/log_explorer/public/controller/controller_customizations.ts @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { DataTableRecord } from '@kbn/discover-utils/types'; +import { DocViewRenderProps } from '@kbn/unified-doc-viewer/types'; + +export interface LogExplorerCustomizations { + flyout?: { + renderContent?: RenderContentCustomization; + }; +} + +export interface LogExplorerFlyoutContentProps { + actions: { + addFilter: DocViewRenderProps['filter']; + addColumn: DocViewRenderProps['onAddColumn']; + removeColumn: DocViewRenderProps['onRemoveColumn']; + }; + dataView: DocViewRenderProps['dataView']; + doc: LogDocument; +} + +export interface LogDocument extends DataTableRecord { + flattened: { + '@timestamp': string; + 'log.level'?: [string]; + message?: [string]; + + 'host.name'?: string; + 'service.name'?: string; + 'trace.id'?: string; + 'agent.name'?: string; + 'orchestrator.cluster.name'?: string; + 'orchestrator.resource.id'?: string; + 'cloud.provider'?: string; + 'cloud.region'?: string; + 'cloud.availability_zone'?: string; + 'cloud.project.id'?: string; + 'cloud.instance.id'?: string; + 'log.file.path'?: string; + 'data_stream.namespace': string; + 'data_stream.dataset': string; + }; +} + +export interface FlyoutDoc { + '@timestamp': string; + 'log.level'?: string; + message?: string; + + 'host.name'?: string; + 'service.name'?: string; + 'trace.id'?: string; + 'agent.name'?: string; + 'orchestrator.cluster.name'?: string; + 'orchestrator.resource.id'?: string; + 'cloud.provider'?: string; + 'cloud.region'?: string; + 'cloud.availability_zone'?: string; + 'cloud.project.id'?: string; + 'cloud.instance.id'?: string; + 'log.file.path'?: string; + 'data_stream.namespace': string; + 'data_stream.dataset': string; +} + +export type RenderContentCustomization = ( + renderPreviousContent: RenderPreviousContent +) => (props: Props) => React.ReactNode; + +export type RenderPreviousContent = (props: Props) => React.ReactNode; diff --git a/x-pack/plugins/log_explorer/public/controller/create_controller.ts b/x-pack/plugins/log_explorer/public/controller/create_controller.ts new file mode 100644 index 0000000000000..53260aeb97281 --- /dev/null +++ b/x-pack/plugins/log_explorer/public/controller/create_controller.ts @@ -0,0 +1,99 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CoreStart } from '@kbn/core/public'; +import { getDevToolsOptions } from '@kbn/xstate-utils'; +import equal from 'fast-deep-equal'; +import { distinctUntilChanged, EMPTY, from, map, shareReplay } from 'rxjs'; +import { interpret } from 'xstate'; +import { DatasetsService } from '../services/datasets'; +import { createLogExplorerControllerStateMachine } from '../state_machines/log_explorer_controller'; +import { LogExplorerStartDeps } from '../types'; +import { LogExplorerCustomizations } from './controller_customizations'; +import { createDataServiceProxy } from './custom_data_service'; +import { createUiSettingsServiceProxy } from './custom_ui_settings_service'; +import { + createDiscoverMemoryHistory, + createMemoryUrlStateStorage, +} from './custom_url_state_storage'; +import { getContextFromPublicState, getPublicStateFromContext } from './public_state'; +import { + LogExplorerController, + LogExplorerDiscoverServices, + LogExplorerPublicStateUpdate, +} from './types'; + +interface Dependencies { + core: CoreStart; + plugins: LogExplorerStartDeps; +} + +type InitialState = LogExplorerPublicStateUpdate; + +export const createLogExplorerControllerFactory = + ({ core, plugins: { data } }: Dependencies) => + async ({ + customizations = {}, + initialState, + }: { + customizations?: LogExplorerCustomizations; + initialState?: InitialState; + }): Promise => { + const datasetsClient = new DatasetsService().start({ + http: core.http, + }).client; + + const customMemoryHistory = createDiscoverMemoryHistory(); + const customMemoryUrlStateStorage = createMemoryUrlStateStorage(customMemoryHistory); + const customUiSettings = createUiSettingsServiceProxy(core.uiSettings); + const customData = createDataServiceProxy({ + data, + http: core.http, + uiSettings: customUiSettings, + }); + const discoverServices: LogExplorerDiscoverServices = { + data: customData, + history: () => customMemoryHistory, + uiSettings: customUiSettings, + filterManager: customData.query.filterManager, + timefilter: customData.query.timefilter.timefilter, + urlStateStorage: customMemoryUrlStateStorage, + }; + + const initialContext = getContextFromPublicState(initialState ?? {}); + + const machine = createLogExplorerControllerStateMachine({ + datasetsClient, + initialContext, + query: discoverServices.data.query, + toasts: core.notifications.toasts, + }); + + const service = interpret(machine, { + devTools: getDevToolsOptions(), + }); + + const logExplorerState$ = from(service).pipe( + map(({ context }) => getPublicStateFromContext(context)), + distinctUntilChanged(equal), + shareReplay(1) + ); + + return { + actions: {}, + customizations, + datasetsClient, + discoverServices, + event$: EMPTY, + service, + state$: logExplorerState$, + stateMachine: machine, + }; + }; + +export type CreateLogExplorerControllerFactory = typeof createLogExplorerControllerFactory; +export type CreateLogExplorerController = ReturnType; diff --git a/x-pack/plugins/log_explorer/public/controller/custom_data_service.ts b/x-pack/plugins/log_explorer/public/controller/custom_data_service.ts new file mode 100644 index 0000000000000..790fbaa04df86 --- /dev/null +++ b/x-pack/plugins/log_explorer/public/controller/custom_data_service.ts @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { HttpStart } from '@kbn/core-http-browser'; +import { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; +import { DataPublicPluginStart, NowProvider, QueryService } from '@kbn/data-plugin/public'; +import { Storage } from '@kbn/kibana-utils-plugin/public'; +import { createPropertyGetProxy } from '../utils/proxies'; + +/** + * Create proxy for the data service, in which session service enablement calls + * are no-ops. + */ +export const createDataServiceProxy = ({ + data, + http, + uiSettings, +}: { + data: DataPublicPluginStart; + http: HttpStart; + uiSettings: IUiSettingsClient; +}) => { + /** + * search session + */ + const noOpEnableStorage = () => {}; + + const sessionServiceProxy = createPropertyGetProxy(data.search.session, { + enableStorage: () => noOpEnableStorage, + }); + + const searchServiceProxy = createPropertyGetProxy(data.search, { + session: () => sessionServiceProxy, + }); + + /** + * query + */ + const customStorage = new Storage(localStorage); + const customQueryService = new QueryService(); + customQueryService.setup({ + nowProvider: new NowProvider(), + storage: customStorage, + uiSettings, + }); + const customQuery = customQueryService.start({ + http, + storage: customStorage, + uiSettings, + }); + + /** + * combined + */ + return createPropertyGetProxy(data, { + query: () => customQuery, + search: () => searchServiceProxy, + }); +}; diff --git a/x-pack/plugins/log_explorer/public/controller/custom_ui_settings_service.ts b/x-pack/plugins/log_explorer/public/controller/custom_ui_settings_service.ts new file mode 100644 index 0000000000000..bd247b91ba1f5 --- /dev/null +++ b/x-pack/plugins/log_explorer/public/controller/custom_ui_settings_service.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 { IUiSettingsClient } from '@kbn/core-ui-settings-browser'; +import { HIDE_ANNOUNCEMENTS, MODIFY_COLUMNS_ON_SWITCH } from '@kbn/discover-utils'; +import { createPropertyGetProxy } from '../utils/proxies'; + +/** + * Create proxy for the uiSettings service, in which settings preferences are overwritten + * with custom values + */ +export const createUiSettingsServiceProxy = (uiSettings: IUiSettingsClient) => { + const overrides: Record = { + [HIDE_ANNOUNCEMENTS]: true, + [MODIFY_COLUMNS_ON_SWITCH]: false, + }; + + return createPropertyGetProxy(uiSettings, { + get: + () => + (key, ...args) => { + if (key in overrides) { + return overrides[key]; + } + + return uiSettings.get(key, ...args); + }, + }); +}; diff --git a/x-pack/plugins/log_explorer/public/controller/custom_url_state_storage.ts b/x-pack/plugins/log_explorer/public/controller/custom_url_state_storage.ts new file mode 100644 index 0000000000000..bddb162963376 --- /dev/null +++ b/x-pack/plugins/log_explorer/public/controller/custom_url_state_storage.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { createKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; +import { createMemoryHistory } from 'history'; +import { LogExplorerDiscoverServices } from './types'; + +type DiscoverHistory = ReturnType; + +/** + * Create a MemoryHistory instance. It is initialized with an application state + * object, because Discover radically resets too much when the URL is "empty". + */ +export const createDiscoverMemoryHistory = (): DiscoverHistory => + createMemoryHistory({ + initialEntries: [{ search: `?_a=()` }], + }); + +/** + * Create a url state storage that's not connected to the real browser location + * to isolate the Discover component from these side-effects. + */ +export const createMemoryUrlStateStorage = (memoryHistory: DiscoverHistory) => + createKbnUrlStateStorage({ + history: memoryHistory, + useHash: false, + useHashQuery: false, + }); diff --git a/x-pack/plugins/log_explorer/public/controller/index.ts b/x-pack/plugins/log_explorer/public/controller/index.ts new file mode 100644 index 0000000000000..8c1ebbc752dbd --- /dev/null +++ b/x-pack/plugins/log_explorer/public/controller/index.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './controller_customizations'; +export * from './create_controller'; +export * from './provider'; +export * from './types'; diff --git a/x-pack/plugins/log_explorer/public/controller/lazy_create_controller.ts b/x-pack/plugins/log_explorer/public/controller/lazy_create_controller.ts new file mode 100644 index 0000000000000..46104b3960940 --- /dev/null +++ b/x-pack/plugins/log_explorer/public/controller/lazy_create_controller.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 type { CreateLogExplorerControllerFactory } from './create_controller'; + +export const createLogExplorerControllerLazyFactory: CreateLogExplorerControllerFactory = + (dependencies) => async (args) => { + const { createLogExplorerControllerFactory } = await import('./create_controller'); + + return createLogExplorerControllerFactory(dependencies)(args); + }; diff --git a/x-pack/plugins/log_explorer/public/controller/provider.ts b/x-pack/plugins/log_explorer/public/controller/provider.ts new file mode 100644 index 0000000000000..a66b03f477cb1 --- /dev/null +++ b/x-pack/plugins/log_explorer/public/controller/provider.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 createContainer from 'constate'; +import type { LogExplorerController } from './types'; + +const useLogExplorerController = ({ controller }: { controller: LogExplorerController }) => + controller; + +export const [LogExplorerControllerProvider, useLogExplorerControllerContext] = + createContainer(useLogExplorerController); diff --git a/x-pack/plugins/log_explorer/public/controller/public_state.ts b/x-pack/plugins/log_explorer/public/controller/public_state.ts new file mode 100644 index 0000000000000..5f4aad892bcb9 --- /dev/null +++ b/x-pack/plugins/log_explorer/public/controller/public_state.ts @@ -0,0 +1,130 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { + availableControlsPanels, + controlPanelConfigs, + ControlPanels, + hydrateDatasetSelection, +} from '../../common'; +import { + DEFAULT_CONTEXT, + LogExplorerControllerContext, +} from '../state_machines/log_explorer_controller'; +import { LogExplorerPublicState, LogExplorerPublicStateUpdate, OptionsListControl } from './types'; + +export const getPublicStateFromContext = ( + context: LogExplorerControllerContext +): LogExplorerPublicState => { + return { + chart: context.chart, + datasetSelection: context.datasetSelection.toPlainSelection(), + grid: context.grid, + filters: context.filters, + query: context.query, + refreshInterval: context.refreshInterval, + time: context.time, + controls: getPublicControlsStateFromControlPanels(context.controlPanels), + }; +}; + +export const getContextFromPublicState = ( + publicState: LogExplorerPublicStateUpdate +): LogExplorerControllerContext => ({ + ...DEFAULT_CONTEXT, + chart: { + ...DEFAULT_CONTEXT.chart, + ...publicState.chart, + }, + controlPanels: getControlPanelsFromPublicControlsState(publicState.controls), + datasetSelection: + publicState.datasetSelection != null + ? hydrateDatasetSelection(publicState.datasetSelection) + : DEFAULT_CONTEXT.datasetSelection, + grid: { + ...DEFAULT_CONTEXT.grid, + ...publicState.grid, + rows: { + ...DEFAULT_CONTEXT.grid.rows, + ...publicState.grid?.rows, + }, + }, + filters: publicState.filters ?? DEFAULT_CONTEXT.filters, + query: publicState.query ?? DEFAULT_CONTEXT.query, + refreshInterval: publicState.refreshInterval ?? DEFAULT_CONTEXT.refreshInterval, + time: publicState.time ?? DEFAULT_CONTEXT.time, +}); + +const getPublicControlsStateFromControlPanels = ( + controlPanels: ControlPanels | undefined +): LogExplorerPublicState['controls'] => + controlPanels != null + ? { + ...(availableControlsPanels.NAMESPACE in controlPanels + ? { + [availableControlsPanels.NAMESPACE]: getOptionsListPublicControlStateFromControlPanel( + controlPanels[availableControlsPanels.NAMESPACE] + ), + } + : {}), + } + : {}; + +const getOptionsListPublicControlStateFromControlPanel = ( + optionsListControlPanel: ControlPanels[string] +): OptionsListControl => ({ + mode: optionsListControlPanel.explicitInput.exclude ? 'exclude' : 'include', + selection: optionsListControlPanel.explicitInput.existsSelected + ? { type: 'exists' } + : { + type: 'options', + selectedOptions: optionsListControlPanel.explicitInput.selectedOptions ?? [], + }, +}); + +const getControlPanelsFromPublicControlsState = ( + publicControlsState: LogExplorerPublicStateUpdate['controls'] +): ControlPanels => { + if (publicControlsState == null) { + return {}; + } + + const namespacePublicControlState = publicControlsState[availableControlsPanels.NAMESPACE]; + + return { + ...(namespacePublicControlState + ? { + [availableControlsPanels.NAMESPACE]: getControlPanelFromOptionsListPublicControlState( + availableControlsPanels.NAMESPACE, + namespacePublicControlState + ), + } + : {}), + }; +}; + +const getControlPanelFromOptionsListPublicControlState = ( + controlId: string, + publicControlState: OptionsListControl +): ControlPanels[string] => { + const defaultControlPanelConfig = controlPanelConfigs[controlId]; + + return { + ...defaultControlPanelConfig, + explicitInput: { + ...defaultControlPanelConfig.explicitInput, + exclude: publicControlState.mode === 'exclude', + ...(publicControlState.selection.type === 'exists' + ? { + existsSelected: true, + } + : { + selectedOptions: publicControlState.selection.selectedOptions, + }), + }, + }; +}; diff --git a/x-pack/plugins/log_explorer/public/controller/types.ts b/x-pack/plugins/log_explorer/public/controller/types.ts new file mode 100644 index 0000000000000..5947051ad2518 --- /dev/null +++ b/x-pack/plugins/log_explorer/public/controller/types.ts @@ -0,0 +1,75 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { QueryState } from '@kbn/data-plugin/public'; +import { DiscoverContainerProps } from '@kbn/discover-plugin/public'; +import { IKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; +import { Observable } from 'rxjs'; +import { + availableControlsPanels, + DatasetSelectionPlain, + DisplayOptions, + PartialDisplayOptions, +} from '../../common'; +import { IDatasetsClient } from '../services/datasets'; +import { + LogExplorerControllerStateMachine, + LogExplorerControllerStateService, +} from '../state_machines/log_explorer_controller'; +import { LogExplorerCustomizations } from './controller_customizations'; + +export interface LogExplorerController { + actions: {}; + customizations: LogExplorerCustomizations; + datasetsClient: IDatasetsClient; + discoverServices: LogExplorerDiscoverServices; + event$: Observable; + service: LogExplorerControllerStateService; + state$: Observable; + stateMachine: LogExplorerControllerStateMachine; +} + +export type LogExplorerDiscoverServices = Pick< + Required, + 'data' | 'filterManager' | 'timefilter' | 'uiSettings' | 'history' +> & { + urlStateStorage: IKbnUrlStateStorage; +}; + +export interface OptionsListControlOption { + type: 'options'; + selectedOptions: string[]; +} + +export interface OptionsListControlExists { + type: 'exists'; +} + +export interface OptionsListControl { + mode: 'include' | 'exclude'; + selection: OptionsListControlOption | OptionsListControlExists; +} + +export interface ControlOptions { + [availableControlsPanels.NAMESPACE]?: OptionsListControl; +} + +// we might want to wrap this into an object that has a "state value" laster +export type LogExplorerPublicState = QueryState & + DisplayOptions & { + controls: ControlOptions; + datasetSelection: DatasetSelectionPlain; + }; + +export type LogExplorerPublicStateUpdate = QueryState & + PartialDisplayOptions & { + controls?: ControlOptions; + datasetSelection?: DatasetSelectionPlain; + }; + +// a placeholder for now +export type LogExplorerPublicEvent = never; diff --git a/x-pack/plugins/log_explorer/public/customizations/custom_dataset_filters.tsx b/x-pack/plugins/log_explorer/public/customizations/custom_dataset_filters.tsx index c315971fb23a5..e7b3dc6bbd431 100644 --- a/x-pack/plugins/log_explorer/public/customizations/custom_dataset_filters.tsx +++ b/x-pack/plugins/log_explorer/public/customizations/custom_dataset_filters.tsx @@ -10,21 +10,21 @@ import { Query } from '@kbn/es-query'; import { DataPublicPluginStart } from '@kbn/data-plugin/public'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { useControlPanels } from '../hooks/use_control_panels'; -import { LogExplorerProfileStateService } from '../state_machines/log_explorer_profile'; +import { LogExplorerControllerStateService } from '../state_machines/log_explorer_controller'; const DATASET_FILTERS_CUSTOMIZATION_ID = 'datasetFiltersCustomization'; interface CustomDatasetFiltersProps { - logExplorerProfileStateService: LogExplorerProfileStateService; + logExplorerControllerStateService: LogExplorerControllerStateService; data: DataPublicPluginStart; } const CustomDatasetFilters = ({ - logExplorerProfileStateService, + logExplorerControllerStateService, data, }: CustomDatasetFiltersProps) => { const { getInitialInput, setControlGroupAPI, query, filters, timeRange } = useControlPanels( - logExplorerProfileStateService, + logExplorerControllerStateService, data ); diff --git a/x-pack/plugins/log_explorer/public/customizations/custom_dataset_selector.tsx b/x-pack/plugins/log_explorer/public/customizations/custom_dataset_selector.tsx index 5b9fc3223fddf..a93a893bb20e9 100644 --- a/x-pack/plugins/log_explorer/public/customizations/custom_dataset_selector.tsx +++ b/x-pack/plugins/log_explorer/public/customizations/custom_dataset_selector.tsx @@ -15,15 +15,15 @@ import { DataViewsProvider, useDataViewsContext } from '../hooks/use_data_views' import { useEsql } from '../hooks/use_esql'; import { IntegrationsProvider, useIntegrationsContext } from '../hooks/use_integrations'; import { IDatasetsClient } from '../services/datasets'; -import { LogExplorerProfileStateService } from '../state_machines/log_explorer_profile'; +import { LogExplorerControllerStateService } from '../state_machines/log_explorer_controller'; interface CustomDatasetSelectorProps { - logExplorerProfileStateService: LogExplorerProfileStateService; + logExplorerControllerStateService: LogExplorerControllerStateService; } -export const CustomDatasetSelector = withProviders(({ logExplorerProfileStateService }) => { +export const CustomDatasetSelector = withProviders(({ logExplorerControllerStateService }) => { const { datasetSelection, handleDatasetSelectionChange } = useDatasetSelection( - logExplorerProfileStateService + logExplorerControllerStateService ); const { @@ -111,13 +111,13 @@ function withProviders(Component: React.FunctionComponent - + diff --git a/x-pack/plugins/log_explorer/public/customizations/custom_flyout_content.tsx b/x-pack/plugins/log_explorer/public/customizations/custom_flyout_content.tsx index 2fd71f948146f..a1461f4de5fca 100644 --- a/x-pack/plugins/log_explorer/public/customizations/custom_flyout_content.tsx +++ b/x-pack/plugins/log_explorer/public/customizations/custom_flyout_content.tsx @@ -5,46 +5,58 @@ * 2.0. */ -import React, { useCallback } from 'react'; -import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule } from '@elastic/eui'; +import React, { useMemo } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; +import { DocViewRenderProps } from '@kbn/unified-doc-viewer/types'; import { FlyoutDetail } from '../components/flyout_detail/flyout_detail'; -import { FlyoutProps } from '../components/flyout_detail'; -import { useLogExplorerCustomizationsContext } from '../hooks/use_log_explorer_customizations'; +import { LogExplorerFlyoutContentProps } from '../components/flyout_detail'; +import { LogDocument, useLogExplorerControllerContext } from '../controller'; export const CustomFlyoutContent = ({ - actions, + filter, + onAddColumn, + onRemoveColumn, dataView, - doc, - renderDefaultContent, -}: FlyoutProps) => { - const { flyout } = useLogExplorerCustomizationsContext(); + hit, +}: DocViewRenderProps) => { + const { + customizations: { flyout }, + } = useLogExplorerControllerContext(); - const renderPreviousContent = useCallback( - () => ( - <> - {/* Apply custom Log Explorer detail */} - - - - - ), - [actions, dataView, doc] + const flyoutContentProps: LogExplorerFlyoutContentProps = useMemo( + () => ({ + actions: { + addFilter: filter, + addColumn: onAddColumn, + removeColumn: onRemoveColumn, + }, + dataView, + doc: hit as LogDocument, + }), + [filter, onAddColumn, onRemoveColumn, dataView, hit] ); - const content = flyout?.renderContent - ? flyout?.renderContent(renderPreviousContent, { doc }) - : renderPreviousContent(); + const renderCustomizedContent = useMemo( + () => flyout?.renderContent?.(renderContent) ?? renderContent, + [flyout] + ); return ( - - {/* Apply custom Log Explorer detail */} - {content} - {/* Restore default content */} - - {renderDefaultContent()} - + <> + + + {/* Apply custom Log Explorer detail */} + {renderCustomizedContent(flyoutContentProps)} + + ); }; +const renderContent = ({ actions, dataView, doc }: LogExplorerFlyoutContentProps) => ( + + + +); + // eslint-disable-next-line import/no-default-export export default CustomFlyoutContent; diff --git a/x-pack/plugins/log_explorer/public/customizations/custom_search_bar.tsx b/x-pack/plugins/log_explorer/public/customizations/custom_search_bar.tsx new file mode 100644 index 0000000000000..1cc3d6bf95eec --- /dev/null +++ b/x-pack/plugins/log_explorer/public/customizations/custom_search_bar.tsx @@ -0,0 +1,41 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; +import type { NavigationPublicPluginStart } from '@kbn/navigation-plugin/public'; +import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; + +export const createCustomSearchBar = ({ + navigation, + data, + unifiedSearch, +}: { + data: DataPublicPluginStart; + navigation: NavigationPublicPluginStart; + unifiedSearch: UnifiedSearchPublicPluginStart; +}) => { + const { + ui: { createTopNavWithCustomContext }, + } = navigation; + + const { + ui: { getCustomSearchBar }, + } = unifiedSearch; + + const CustomSearchBar = getCustomSearchBar(data); + + const customUnifiedSearch = { + ...unifiedSearch, + ui: { + ...unifiedSearch.ui, + SearchBar: CustomSearchBar, + AggregateQuerySearchBar: CustomSearchBar, + }, + }; + + return createTopNavWithCustomContext(customUnifiedSearch); +}; diff --git a/x-pack/plugins/log_explorer/public/customizations/log_explorer_profile.tsx b/x-pack/plugins/log_explorer/public/customizations/log_explorer_profile.tsx index 89c585d8d201e..60f21be11f948 100644 --- a/x-pack/plugins/log_explorer/public/customizations/log_explorer_profile.tsx +++ b/x-pack/plugins/log_explorer/public/customizations/log_explorer_profile.tsx @@ -4,19 +4,20 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ + +/* eslint-disable react-hooks/rules-of-hooks */ + import React from 'react'; import type { CoreStart } from '@kbn/core/public'; -import { CustomizationCallback, DiscoverStateContainer } from '@kbn/discover-plugin/public'; +import type { CustomizationCallback } from '@kbn/discover-plugin/public'; import { i18n } from '@kbn/i18n'; -import useObservable from 'react-use/lib/useObservable'; -import { combineLatest, from, map, Subscription, type BehaviorSubject } from 'rxjs'; -import { LogExplorerStateContainer } from '../components/log_explorer'; -import { LogExplorerCustomizations } from '../components/log_explorer/types'; -import { LogExplorerCustomizationsProvider } from '../hooks/use_log_explorer_customizations'; -import { LogExplorerProfileStateService } from '../state_machines/log_explorer_profile'; -import { LogExplorerStartDeps } from '../types'; +import { waitFor } from 'xstate/lib/waitFor'; +import type { LogExplorerController } from '../controller'; +import { LogExplorerControllerProvider } from '../controller/provider'; +import type { LogExplorerStartDeps } from '../types'; import { dynamic } from '../utils/dynamic'; import { useKibanaContextForPluginProvider } from '../utils/use_kibana'; +import { createCustomSearchBar } from './custom_search_bar'; const LazyCustomDatasetFilters = dynamic(() => import('./custom_dataset_filters')); const LazyCustomDatasetSelector = dynamic(() => import('./custom_dataset_selector')); @@ -24,56 +25,31 @@ const LazyCustomFlyoutContent = dynamic(() => import('./custom_flyout_content')) export interface CreateLogExplorerProfileCustomizationsDeps { core: CoreStart; - customizations: LogExplorerCustomizations; plugins: LogExplorerStartDeps; - state$?: BehaviorSubject; + controller: LogExplorerController; } export const createLogExplorerProfileCustomizations = ({ core, - customizations: logExplorerCustomizations, plugins, - state$, + controller, }: CreateLogExplorerProfileCustomizationsDeps): CustomizationCallback => async ({ customizations, stateContainer }) => { - const { data, dataViews, discover } = plugins; - // Lazy load dependencies - const datasetServiceModuleLoadable = import('../services/datasets'); - const logExplorerMachineModuleLoadable = import('../state_machines/log_explorer_profile'); - - const [{ DatasetsService }, { initializeLogExplorerProfileStateService, waitForState }] = - await Promise.all([datasetServiceModuleLoadable, logExplorerMachineModuleLoadable]); - - const datasetsClient = new DatasetsService().start({ - http: core.http, - }).client; + const { discoverServices, service } = controller; + const pluginsWithOverrides = { + ...plugins, + ...discoverServices, + }; + const { data, dataViews, discover, navigation, unifiedSearch } = pluginsWithOverrides; - const logExplorerProfileStateService = initializeLogExplorerProfileStateService({ - datasetsClient, - stateContainer, - toasts: core.notifications.toasts, - }); + service.send('RECEIVED_STATE_CONTAINER', { discoverStateContainer: stateContainer }); /** * Wait for the machine to be fully initialized to set the restored selection * create the DataView and set it in the stateContainer from Discover */ - await waitForState(logExplorerProfileStateService, 'initialized'); - - /** - * Subscribe the state$ BehaviorSubject when the consumer app wants to react to state changes. - * It emits a combined state of: - * - log explorer state machine context - * - appState from the discover stateContainer - */ - let stateSubscription: Subscription; - if (state$) { - stateSubscription = createStateUpdater({ - logExplorerProfileStateService, - stateContainer, - }).subscribe(state$); - } + await waitFor(service, (state) => state.matches('initialized'), { timeout: 30000 }); /** * Replace the DataViewPicker with a custom `DatasetSelector` to pick integrations streams @@ -87,20 +63,22 @@ export const createLogExplorerProfileCustomizations = return ( ); }, PrependFilterBar: () => ( - + ), + CustomSearchBar: createCustomSearchBar({ + data, + navigation, + unifiedSearch, + }), }); /** @@ -133,42 +111,29 @@ export const createLogExplorerProfileCustomizations = viewSurroundingDocument: { disabled: true }, }, }, - Content: (props) => { - const KibanaContextProviderForPlugin = useKibanaContextForPluginProvider(core, plugins); - - const internalState = useObservable( - stateContainer.internalState.state$, - stateContainer.internalState.get() - ); - - return ( - - - - - - ); + docViewsRegistry: (registry) => { + registry.add({ + id: 'doc_view_log_overview', + title: i18n.translate('xpack.logExplorer.flyoutDetail.docViews.overview', { + defaultMessage: 'Overview', + }), + order: 0, + component: (props) => { + const KibanaContextProviderForPlugin = useKibanaContextForPluginProvider(core, plugins); + + return ( + + + + + + ); + }, + }); + + return registry; }, }); - return () => { - if (stateSubscription) { - stateSubscription.unsubscribe(); - } - }; + return () => {}; }; - -const createStateUpdater = ({ - logExplorerProfileStateService, - stateContainer, -}: { - logExplorerProfileStateService: LogExplorerProfileStateService; - stateContainer: DiscoverStateContainer; -}) => { - return combineLatest([from(logExplorerProfileStateService), stateContainer.appState.state$]).pipe( - map(([logExplorerState, appState]) => ({ - logExplorerState: logExplorerState.context, - appState, - })) - ); -}; diff --git a/x-pack/plugins/log_explorer/public/hooks/use_control_panels.tsx b/x-pack/plugins/log_explorer/public/hooks/use_control_panels.tsx index c10cddabbe0b1..f38b902aaa045 100644 --- a/x-pack/plugins/log_explorer/public/hooks/use_control_panels.tsx +++ b/x-pack/plugins/log_explorer/public/hooks/use_control_panels.tsx @@ -13,16 +13,16 @@ import { Query, TimeRange } from '@kbn/es-query'; import { useQuerySubscriber } from '@kbn/unified-field-list'; import { useSelector } from '@xstate/react'; import { useCallback } from 'react'; -import { LogExplorerProfileStateService } from '../state_machines/log_explorer_profile'; +import { LogExplorerControllerStateService } from '../state_machines/log_explorer_controller'; export const useControlPanels = ( - logExplorerProfileStateService: LogExplorerProfileStateService, + logExplorerControllerStateService: LogExplorerControllerStateService, data: DataPublicPluginStart ) => { const { query, filters, fromDate, toDate } = useQuerySubscriber({ data }); const timeRange: TimeRange = { from: fromDate!, to: toDate! }; - const controlPanels = useSelector(logExplorerProfileStateService, (state) => { + const controlPanels = useSelector(logExplorerControllerStateService, (state) => { if (!('controlPanels' in state.context)) return; return state.context.controlPanels; }); @@ -45,12 +45,12 @@ export const useControlPanels = ( const setControlGroupAPI = useCallback( (controlGroupAPI: ControlGroupAPI) => { - logExplorerProfileStateService.send({ + logExplorerControllerStateService.send({ type: 'INITIALIZE_CONTROL_GROUP_API', controlGroupAPI, }); }, - [logExplorerProfileStateService] + [logExplorerControllerStateService] ); return { getInitialInput, setControlGroupAPI, query, filters, timeRange }; diff --git a/x-pack/plugins/log_explorer/public/hooks/use_dataset_selection.ts b/x-pack/plugins/log_explorer/public/hooks/use_dataset_selection.ts index 6bff4055f3635..1f611da05c8cc 100644 --- a/x-pack/plugins/log_explorer/public/hooks/use_dataset_selection.ts +++ b/x-pack/plugins/log_explorer/public/hooks/use_dataset_selection.ts @@ -8,20 +8,20 @@ import { useSelector } from '@xstate/react'; import { useCallback } from 'react'; import { DatasetSelectionChange } from '../../common/dataset_selection'; -import { LogExplorerProfileStateService } from '../state_machines/log_explorer_profile'; +import { LogExplorerControllerStateService } from '../state_machines/log_explorer_controller'; export const useDatasetSelection = ( - logExplorerProfileStateService: LogExplorerProfileStateService + logExplorerControllerStateService: LogExplorerControllerStateService ) => { - const datasetSelection = useSelector(logExplorerProfileStateService, (state) => { + const datasetSelection = useSelector(logExplorerControllerStateService, (state) => { return state.context.datasetSelection; }); const handleDatasetSelectionChange: DatasetSelectionChange = useCallback( (data) => { - logExplorerProfileStateService.send({ type: 'UPDATE_DATASET_SELECTION', data }); + logExplorerControllerStateService.send({ type: 'UPDATE_DATASET_SELECTION', data }); }, - [logExplorerProfileStateService] + [logExplorerControllerStateService] ); return { datasetSelection, handleDatasetSelectionChange }; diff --git a/x-pack/plugins/log_explorer/public/hooks/use_discover_action.ts b/x-pack/plugins/log_explorer/public/hooks/use_discover_action.ts index ef1a1ae715ca6..b19afa399bb53 100644 --- a/x-pack/plugins/log_explorer/public/hooks/use_discover_action.ts +++ b/x-pack/plugins/log_explorer/public/hooks/use_discover_action.ts @@ -5,10 +5,10 @@ * 2.0. */ import createContainer from 'constate'; -import { FlyoutContentActions } from '@kbn/discover-plugin/public'; +import type { LogExplorerFlyoutContentProps } from '../components/flyout_detail/types'; interface UseFlyoutActionsDeps { - value: FlyoutContentActions; + value: LogExplorerFlyoutContentProps['actions']; } const useDiscoverActions = ({ value }: UseFlyoutActionsDeps) => value; diff --git a/x-pack/plugins/log_explorer/public/hooks/use_hover_actions.tsx b/x-pack/plugins/log_explorer/public/hooks/use_hover_actions.tsx new file mode 100644 index 0000000000000..71fd5103242c9 --- /dev/null +++ b/x-pack/plugins/log_explorer/public/hooks/use_hover_actions.tsx @@ -0,0 +1,89 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useMemo, useState } from 'react'; +import { ValuesType } from 'utility-types'; +import { copyToClipboard, IconType } from '@elastic/eui'; +import { + flyoutHoverActionCopyToClipboardText, + flyoutHoverActionFilterForFieldPresentText, + flyoutHoverActionFilterForText, + flyoutHoverActionFilterOutText, + flyoutHoverActionToggleColumnText, +} from '../components/flyout_detail/translations'; +import { useDiscoverActionsContext } from './use_discover_action'; +import { LogDocument } from '../components/flyout_detail'; + +interface HoverActionProps { + field: string; + value: ValuesType; +} + +export interface HoverActionType { + id: string; + tooltipContent: string; + iconType: IconType; + onClick: () => void; + display: boolean; +} + +export const useHoverActions = ({ field, value }: HoverActionProps): HoverActionType[] => { + const filterForText = flyoutHoverActionFilterForText(value); + const filterOutText = flyoutHoverActionFilterOutText(value); + const actions = useDiscoverActionsContext(); + const [columnAdded, setColumnAdded] = useState(false); + + return useMemo( + () => [ + { + id: 'addToFilterAction', + tooltipContent: filterForText, + iconType: 'plusInCircle', + onClick: () => actions?.addFilter && actions.addFilter(field, value, '+'), + display: true, + }, + { + id: 'removeFromFilterAction', + tooltipContent: filterOutText, + iconType: 'minusInCircle', + onClick: () => actions?.addFilter && actions.addFilter(field, value, '-'), + display: true, + }, + { + id: 'filterForFieldPresentAction', + tooltipContent: flyoutHoverActionFilterForFieldPresentText, + iconType: 'filter', + onClick: () => actions?.addFilter && actions.addFilter('_exists_', field, '+'), + display: true, + }, + { + id: 'toggleColumnAction', + tooltipContent: flyoutHoverActionToggleColumnText, + iconType: 'listAdd', + onClick: () => { + if (actions) { + if (columnAdded) { + actions?.removeColumn?.(field); + } else { + actions?.addColumn?.(field); + } + setColumnAdded(!columnAdded); + } + }, + display: true, + }, + { + id: 'copyToClipboardAction', + tooltipContent: flyoutHoverActionCopyToClipboardText, + iconType: 'copyClipboard', + onClick: () => copyToClipboard(value as string), + display: true, + }, + ], + [filterForText, filterOutText, actions, field, value, columnAdded] + ); +}; diff --git a/x-pack/plugins/log_explorer/public/hooks/use_log_explorer_customizations.ts b/x-pack/plugins/log_explorer/public/hooks/use_log_explorer_customizations.ts deleted file mode 100644 index 0557e17761cb4..0000000000000 --- a/x-pack/plugins/log_explorer/public/hooks/use_log_explorer_customizations.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import createContainer from 'constate'; -import { LogExplorerCustomizations } from '../components/log_explorer/types'; - -interface UseLogExplorerCustomizationsDeps { - value: LogExplorerCustomizations; -} - -const useLogExplorerCustomizations = ({ value }: UseLogExplorerCustomizationsDeps) => value; - -export const [LogExplorerCustomizationsProvider, useLogExplorerCustomizationsContext] = - createContainer(useLogExplorerCustomizations); diff --git a/x-pack/plugins/log_explorer/public/index.ts b/x-pack/plugins/log_explorer/public/index.ts index 1ca7f37aa4c9b..efd337234ffc6 100644 --- a/x-pack/plugins/log_explorer/public/index.ts +++ b/x-pack/plugins/log_explorer/public/index.ts @@ -8,12 +8,21 @@ import type { PluginInitializerContext } from '@kbn/core/public'; import type { LogExplorerConfig } from '../common/plugin_config'; import { LogExplorerPlugin } from './plugin'; -export type { LogExplorerPluginSetup, LogExplorerPluginStart } from './types'; -export type { LogExplorerStateContainer } from './components/log_explorer'; export type { + CreateLogExplorerController, + LogExplorerController, LogExplorerCustomizations, LogExplorerFlyoutContentProps, -} from './components/log_explorer/types'; + LogExplorerPublicState, + LogExplorerPublicStateUpdate, +} from './controller'; +export type { LogExplorerControllerContext } from './state_machines/log_explorer_controller'; +export type { LogExplorerPluginSetup, LogExplorerPluginStart } from './types'; +export { + getDiscoverColumnsFromDisplayOptions, + getDiscoverGridFromDisplayOptions, + getDiscoverFiltersFromState, +} from './utils/convert_discover_app_state'; export function plugin(context: PluginInitializerContext) { return new LogExplorerPlugin(context); diff --git a/x-pack/plugins/log_explorer/public/plugin.ts b/x-pack/plugins/log_explorer/public/plugin.ts index 3c637b6b06caf..8c527c28fb8b2 100644 --- a/x-pack/plugins/log_explorer/public/plugin.ts +++ b/x-pack/plugins/log_explorer/public/plugin.ts @@ -5,11 +5,12 @@ * 2.0. */ -import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from '@kbn/core/public'; +import type { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from '@kbn/core/public'; import { DISCOVER_APP_LOCATOR, DiscoverAppLocatorParams } from '@kbn/discover-plugin/common'; import { LogExplorerLocatorDefinition, LogExplorerLocators } from '../common/locators'; import { createLogExplorer } from './components/log_explorer'; -import { +import { createLogExplorerControllerLazyFactory } from './controller/lazy_create_controller'; +import type { LogExplorerPluginSetup, LogExplorerPluginStart, LogExplorerSetupDeps, @@ -48,8 +49,14 @@ export class LogExplorerPlugin implements Plugin => + async (context) => { + if (!('discoverStateContainer' in context)) return; + return context.controlPanels + ? constructControlPanelsWithDataViewId(context.discoverStateContainer, context.controlPanels) + : undefined; + }; + +export const subscribeControlGroup = + (): InvokeCreator => + (context) => + (send) => { + if (!('controlGroupAPI' in context)) return; + if (!('discoverStateContainer' in context)) return; + const { discoverStateContainer } = context; + + const filtersSubscription = context.controlGroupAPI.onFiltersPublished$.subscribe( + (newFilters) => { + discoverStateContainer.internalState.transitions.setCustomFilters(newFilters); + discoverStateContainer.actions.fetchData(); + } + ); + + const inputSubscription = context.controlGroupAPI.getInput$().subscribe(({ panels }) => { + if (!deepEqual(panels, context.controlPanels)) { + send({ type: 'UPDATE_CONTROL_PANELS', controlPanels: panels }); + } + }); + + return () => { + filtersSubscription.unsubscribe(); + inputSubscription.unsubscribe(); + }; + }; + +export const updateControlPanels = + (): InvokeCreator => + async (context, event) => { + if (!('controlGroupAPI' in context)) return; + if (!('discoverStateContainer' in context)) return; + const { discoverStateContainer } = context; + + const newControlPanels = + ('controlPanels' in event && event.controlPanels) || context.controlPanels; + + if (!newControlPanels) return undefined; + + const controlPanelsWithId = constructControlPanelsWithDataViewId( + discoverStateContainer, + newControlPanels! + ); + + context.controlGroupAPI.updateInput({ panels: controlPanelsWithId }); + + return controlPanelsWithId; + }; + +const constructControlPanelsWithDataViewId = ( + stateContainer: DiscoverStateContainer, + newControlPanels: ControlPanels +) => { + const dataView = stateContainer.internalState.getState().dataView!; + + const validatedControlPanels = isValidState(newControlPanels) + ? newControlPanels + : getVisibleControlPanelsConfig(dataView); + + const controlsPanelsWithId = mergeDefaultPanelsWithControlPanels( + dataView, + validatedControlPanels! + ); + + return controlsPanelsWithId; +}; + +const isValidState = (state: ControlPanels | undefined | null): boolean => { + return Object.keys(state ?? {}).length > 0 && ControlPanelRT.is(state); +}; + +const getVisibleControlPanels = (dataView: DataView | undefined) => + availableControlPanelFields.filter( + (panelKey) => dataView?.fields.getByName(panelKey) !== undefined + ); + +export const getVisibleControlPanelsConfig = (dataView?: DataView) => { + return getVisibleControlPanels(dataView).reduce((panelsMap, panelKey) => { + const config = controlPanelConfigs[panelKey]; + + return { ...panelsMap, [panelKey]: config }; + }, {} as ControlPanels); +}; + +const addDataViewIdToControlPanels = (controlPanels: ControlPanels, dataViewId: string = '') => { + return mapValues(controlPanels, (controlPanelConfig) => ({ + ...controlPanelConfig, + explicitInput: { ...controlPanelConfig.explicitInput, dataViewId }, + })); +}; + +const mergeDefaultPanelsWithControlPanels = (dataView: DataView, urlPanels: ControlPanels) => { + // Get default panel configs from existing fields in data view + const visiblePanels = getVisibleControlPanelsConfig(dataView); + + // Get list of panel which can be overridden to avoid merging additional config from url + const existingKeys = Object.keys(visiblePanels); + const controlPanelsToOverride = pick(urlPanels, existingKeys); + + // Merge default and existing configs and add dataView.id to each of them + return addDataViewIdToControlPanels( + { ...visiblePanels, ...controlPanelsToOverride }, + dataView.id + ); +}; diff --git a/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/data_view_service.ts b/x-pack/plugins/log_explorer/public/state_machines/log_explorer_controller/src/services/data_view_service.ts similarity index 64% rename from x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/data_view_service.ts rename to x-pack/plugins/log_explorer/public/state_machines/log_explorer_controller/src/services/data_view_service.ts index fa2b2992ee76b..76ac9c0c82f23 100644 --- a/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/data_view_service.ts +++ b/x-pack/plugins/log_explorer/public/state_machines/log_explorer_controller/src/services/data_view_service.ts @@ -5,23 +5,15 @@ * 2.0. */ -import { DiscoverStateContainer } from '@kbn/discover-plugin/public'; import { InvokeCreator } from 'xstate'; -import { LogExplorerProfileContext, LogExplorerProfileEvent } from './types'; - -interface LogExplorerProfileDataViewStateDependencies { - stateContainer: DiscoverStateContainer; -} +import { LogExplorerControllerContext, LogExplorerControllerEvent } from '../types'; export const createAndSetDataView = - ({ - stateContainer, - }: LogExplorerProfileDataViewStateDependencies): InvokeCreator< - LogExplorerProfileContext, - LogExplorerProfileEvent - > => + (): InvokeCreator => async (context) => { - const dataView = await stateContainer.actions.createAndAppendAdHocDataView( + if (!('discoverStateContainer' in context)) return; + const { discoverStateContainer } = context; + const dataView = await discoverStateContainer.actions.createAndAppendAdHocDataView( context.datasetSelection.toDataviewSpec() ); /** @@ -32,5 +24,5 @@ export const createAndSetDataView = * to the existing one or the default logs-*. * We set explicitly the data view here to be used when restoring the data view on the initial load. */ - stateContainer.actions.setDataView(dataView); + discoverStateContainer.actions.setDataView(dataView); }; diff --git a/x-pack/plugins/log_explorer/public/state_machines/log_explorer_controller/src/services/discover_service.ts b/x-pack/plugins/log_explorer/public/state_machines/log_explorer_controller/src/services/discover_service.ts new file mode 100644 index 0000000000000..512523c24c8f7 --- /dev/null +++ b/x-pack/plugins/log_explorer/public/state_machines/log_explorer_controller/src/services/discover_service.ts @@ -0,0 +1,83 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { isEmpty } from 'lodash'; +import { ActionFunction, actions, InvokeCallback } from 'xstate'; +import { + getChartDisplayOptionsFromDiscoverAppState, + getDiscoverAppStateFromContext, + getGridColumnDisplayOptionsFromDiscoverAppState, + getGridRowsDisplayOptionsFromDiscoverAppState, + getQueryStateFromDiscoverAppState, +} from '../../../../utils/convert_discover_app_state'; +import { LogExplorerControllerContext, LogExplorerControllerEvent } from '../types'; + +export const subscribeToDiscoverState = + () => + ( + context: LogExplorerControllerContext + ): InvokeCallback => + (send, onEvent) => { + if (!('discoverStateContainer' in context)) { + throw new Error('Failed to subscribe to the Discover state: no state container in context.'); + } + + const { appState } = context.discoverStateContainer; + + const subscription = appState.state$.subscribe({ + next: (newAppState) => { + if (isEmpty(newAppState)) { + return; + } + + send({ + type: 'RECEIVE_DISCOVER_APP_STATE', + appState: newAppState, + }); + }, + }); + + return () => { + subscription.unsubscribe(); + }; + }; + +export const updateContextFromDiscoverAppState = actions.assign< + LogExplorerControllerContext, + LogExplorerControllerEvent +>((context, event) => { + if ('appState' in event && event.type === 'RECEIVE_DISCOVER_APP_STATE') { + return { + chart: { + ...context.chart, + ...getChartDisplayOptionsFromDiscoverAppState(event.appState), + }, + grid: { + columns: + getGridColumnDisplayOptionsFromDiscoverAppState(event.appState) ?? context.grid.columns, + rows: { + ...context.grid.rows, + ...getGridRowsDisplayOptionsFromDiscoverAppState(event.appState), + }, + }, + ...getQueryStateFromDiscoverAppState(event.appState), + }; + } + + return {}; +}); + +export const updateDiscoverAppStateFromContext: ActionFunction< + LogExplorerControllerContext, + LogExplorerControllerEvent +> = (context, _event) => { + if (!('discoverStateContainer' in context)) { + return; + } + + context.discoverStateContainer.appState.update(getDiscoverAppStateFromContext(context)); +}; diff --git a/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/selection_service.ts b/x-pack/plugins/log_explorer/public/state_machines/log_explorer_controller/src/services/selection_service.ts similarity index 79% rename from x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/selection_service.ts rename to x-pack/plugins/log_explorer/public/state_machines/log_explorer_controller/src/services/selection_service.ts index 6de3d24025802..a5aeac069fc32 100644 --- a/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/selection_service.ts +++ b/x-pack/plugins/log_explorer/public/state_machines/log_explorer_controller/src/services/selection_service.ts @@ -6,21 +6,21 @@ */ import { InvokeCreator } from 'xstate'; -import { Dataset } from '../../../../common/datasets'; -import { SingleDatasetSelection } from '../../../../common/dataset_selection'; -import { IDatasetsClient } from '../../../services/datasets'; -import { LogExplorerProfileContext, LogExplorerProfileEvent } from './types'; +import { Dataset } from '../../../../../common/datasets'; +import { SingleDatasetSelection } from '../../../../../common/dataset_selection'; +import { IDatasetsClient } from '../../../../services/datasets'; +import { LogExplorerControllerContext, LogExplorerControllerEvent } from '../types'; -interface LogExplorerProfileUrlStateDependencies { +interface LogExplorerControllerUrlStateDependencies { datasetsClient: IDatasetsClient; } export const validateSelection = ({ datasetsClient, - }: LogExplorerProfileUrlStateDependencies): InvokeCreator< - LogExplorerProfileContext, - LogExplorerProfileEvent + }: LogExplorerControllerUrlStateDependencies): InvokeCreator< + LogExplorerControllerContext, + LogExplorerControllerEvent > => (context) => async (send) => { diff --git a/x-pack/plugins/log_explorer/public/state_machines/log_explorer_controller/src/services/timefilter_service.ts b/x-pack/plugins/log_explorer/public/state_machines/log_explorer_controller/src/services/timefilter_service.ts new file mode 100644 index 0000000000000..124a6778493b0 --- /dev/null +++ b/x-pack/plugins/log_explorer/public/state_machines/log_explorer_controller/src/services/timefilter_service.ts @@ -0,0 +1,68 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { QueryStart } from '@kbn/data-plugin/public'; +import { map, merge, Observable } from 'rxjs'; +import { ActionFunction, actions } from 'xstate'; +import type { LogExplorerControllerContext, LogExplorerControllerEvent } from '../types'; + +export const subscribeToTimefilterService = + (query: QueryStart) => (): Observable => { + const { + timefilter: { timefilter }, + } = query; + + const time$ = timefilter.getTimeUpdate$().pipe( + map( + (): LogExplorerControllerEvent => ({ + type: 'RECEIVE_TIMEFILTER_TIME', + time: timefilter.getTime(), + }) + ) + ); + + const refreshInterval$ = timefilter.getRefreshIntervalUpdate$().pipe( + map( + (): LogExplorerControllerEvent => ({ + type: 'RECEIVE_TIMEFILTER_REFRESH_INTERVAL', + refreshInterval: timefilter.getRefreshInterval(), + }) + ) + ); + + return merge(time$, refreshInterval$); + }; + +export const updateContextFromTimefilter = actions.assign< + LogExplorerControllerContext, + LogExplorerControllerEvent +>((context, event) => { + if (event.type === 'RECEIVE_TIMEFILTER_TIME' && 'time' in event) { + return { + time: event.time, + }; + } + + if (event.type === 'RECEIVE_TIMEFILTER_REFRESH_INTERVAL' && 'refreshInterval' in event) { + return { + refreshInterval: event.refreshInterval, + }; + } + + return {}; +}); + +export const updateTimefilterFromContext = + (query: QueryStart): ActionFunction => + (context, _event) => { + if (context.time != null) { + query.timefilter.timefilter.setTime(context.time); + } + if (context.refreshInterval != null) { + query.timefilter.timefilter.setRefreshInterval(context.refreshInterval); + } + }; diff --git a/x-pack/plugins/log_explorer/public/state_machines/log_explorer_controller/src/state_machine.ts b/x-pack/plugins/log_explorer/public/state_machines/log_explorer_controller/src/state_machine.ts new file mode 100644 index 0000000000000..ef9c87ee7ae14 --- /dev/null +++ b/x-pack/plugins/log_explorer/public/state_machines/log_explorer_controller/src/state_machine.ts @@ -0,0 +1,298 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { IToasts } from '@kbn/core/public'; +import { QueryStart } from '@kbn/data-plugin/public'; +import { actions, createMachine, interpret, InterpreterFrom, raise } from 'xstate'; +import { ControlPanelRT } from '../../../../common/control_panels'; +import { isDatasetSelection } from '../../../../common/dataset_selection'; +import { IDatasetsClient } from '../../../services/datasets'; +import { DEFAULT_CONTEXT } from './defaults'; +import { + createCreateDataViewFailedNotifier, + createDatasetSelectionRestoreFailedNotifier, +} from './notifications'; +import { + initializeControlPanels, + subscribeControlGroup, + updateControlPanels, +} from './services/control_panels'; +import { createAndSetDataView } from './services/data_view_service'; +import { + subscribeToDiscoverState, + updateContextFromDiscoverAppState, + updateDiscoverAppStateFromContext, +} from './services/discover_service'; +import { validateSelection } from './services/selection_service'; +import { + subscribeToTimefilterService, + updateContextFromTimefilter, + updateTimefilterFromContext, +} from './services/timefilter_service'; +import { + LogExplorerControllerContext, + LogExplorerControllerEvent, + LogExplorerControllerTypeState, +} from './types'; + +export const createPureLogExplorerControllerStateMachine = ( + initialContext: LogExplorerControllerContext +) => + /** @xstate-layout N4IgpgJg5mDOIC5QBkD2UCiAPADgG1QCcxCBhVAOwBdDU88SA6AVwoEt2q2BDPNgL0gBiAEoZSGAJIA1DABEA+gGUAKgEEVGBaQDyAOXWS9GEQG0ADAF1EoHKlhsulGyCyIAtACYAbIwDsAIzmAJwAzH4ArAA0IACeiJ5+AByMwZ6hnokALD6e5kneAL6FMWiYuATEZJQ0dAyEjByOPHz8HFBy3FTc0mxgAO5CEJRgjRQAbqgA1qNl2PhEJOTUtPRMTVy8Au2d3b0DCByTAMZdbJQWlpcudg5OFC5uCAGejObentFxiBHBwYyeLIBX5JfLeX7BCLFUroeaVJY1Vb1MbNLZtCgdLo9PqDEi0Br4LoAMyIAFtGHMKotqis6utOC1thjdtiDkdUKd7pdrkgQLdms5eU9sowAqEkn8IuLIUDvN4YvEEFksqE3hFQaEglkperPNCQJSFlVlrU1g0Noz0VATasAArcChgPCwIYjMaTGYU2FU42Iunmhlo9o2uj2x3Ow4TDlnC5WHm2ewCh5Cjy+YLeJJ+cGfTMS96fBWIJJJAKMULa8wZbzmPyfbxZfWG+E003Ii1BjEhvBhp0uvFERiEqgkwjkpvUrttwOtYN+7sO3uRk4xijcqw3RP3R4JPyqgJZDM14sygJywsIXf-TV+LLBEIFXc5Rveo0I2lmlGbVrCMQSGRaORJCUXRZBEBQ1FtW1lHUTR4z5TdzmTUBhVCdNRX3QEQmSbUknPCJAn8IJawiTxgm1dIoRKA0X2bSd6VRb8IFEcQpFkBQAEUAFUTAATWgjQMDg-ktxTBBMIiRhiyzcUpSzSJz0fRhvACPws0rEsIgietn3KV8WyReivwESBGAgLFYDAKglCdMBjnuRhxi2MyuAxayGDsxChGQIDND0BQVB0bQAAk1D0ABxDAlCEhDBWQxAyN8W8wkCJIfDSIFzzlLJ0KyfIwmVAIklCUIdLhCc5ynBjjIgUzzMstzbPsxy+Gc9oGo8yghE4205AEhRevUJQMBUZQMGQcQVEkfRoruRDtwQB9RXBVCIn3cwQnBc8S1VcEVQ0yIPgCAJSp9N9W0My0TOc7gLKsmyOooBynLOVz7vuIQBrUIaRqG8bSEm-QFDEVQdDEBQADE1EkZBOLEGak3m8FS1COUAmCNS5Wvc80hSYtPFS4rvDvJJNJOvS6IDKrBBq67bva+y2AgBgup6vrPu+0a-oBvR4ZEuLnh8FJCaK6t8drcstrFJSIj24EDs8I6ydoiqLrRK66ru9yGaZsAPo0L7hs5iapr84GArByHodhwT115YS5tE4FwUYFUifMdJlQid2sklnaZfFOWtIV46qPHX130qozqdq7o6bexCWBwVrmSxfZBmGR13WmWYaPKiPVcYmObvq+PKET5PMT2HEl2jLk41thNZti1xEEK4qlKBPIUfVHJQmxtCIVS9MvewopQ9z8PzspqP1djkutYT5gk5eyvWVxQh8UHPBiTJL1dOV-Pp8ummNfpxfl5c1e05rzlELXaw7ZipCW+edMUi9kIwh77xNXPIJITeGkfKy0ayniVnnKen5j6MGOHOMKtAl6wBYOwac1UhBGEkJNNQ3kABaWhdAGBEDoZACgwpEO6uBW0kheYO35nmAEgI0afGVAHXC3xnjlgkrlSUwQmHuzHjCfeECDJHzVjVWB754GoEQY0HWet1AKGkJIDAAB1BQ3UBryBoc3J4LwDwAk1Ojcih5wh-ylNlIqljEj4wopRQRZVJ4iKgWImBcCEE4CQYzZmGi+oEJUEQkhtpQpjSig3eCTdn66JRn4VImRqx+DCLwzIAQzFkRdvuDMqk-D5FvHY6iQjHH+mcYXCRpopEyKXhXLsPZnSukzuyT0YczpOPbCUtx0iPHlxXtUhcEZ2S31jFcMJ9sdGt3RuYN4GRAShG-r-dhKkMzSzvFpXcRNEgh3sadfSRTWnVVcZI9xSDKndLnDUvsG8BxDhHGOCezSdmoOjqU1Y5TOnHMvj08MsAb4rnvhuCJ80VJpKSNqLIoJ+FpBvH-QIEl1rlhJumImaRKJUQoKgCAcAXBNO2WaP5CNRLuBvIREI4QviKgSf4CUwQSYkQPOkMi4DCkflYLs6muK+YvxyH-bwMT8Z-D5fyv4Aj8kOLuR+FlOxU44jZbQl+7hSye01AWdhyoJnYV+DM7UMzUoMtFZHS0s53xnOlaMhAXgJlZCzEHJK-KCgKX3GWFSt57zcvLHqceBTdUF2qsayJPwSKpA+JWBWvwgTanPKhV4CteEkTlCEHIwQdXYr1S42m89GoypGb6hA1ZVQo2UujasmMVJ4QIoENIGZgS5MSImimxS9mps1umsuzVGYrzPs3TN81Ah7hybC3aeQ-CZV+ACJhqUf5BoKDWlWojC4NvbY9LxYAfUAu1DyksCSSb1nMAeNhioso5TyuWIERUSrupFUmr10c52l0em8iVVcBjLsdqeGJwJULig3WRIq-dspVgjcCI8B4p2HzrY89piCn382rBM9MXdg1kX3KS1unxX0oyBO+9U6M8lYtrSykyTy6AvKOSgqmkBIMv3rCkWDQbgQIbDfMlGKQxTZlPGkfhwHIF4fEeBzpi7yO6KlP8UiylbzZLyPjP+R6lLdwVms9jZ6tm4YefhnjRyL4GtNEax+-zHZYQBH8EInxNLrsHfMt2bxKygt+HKP4nxijFCAA */ + createMachine< + LogExplorerControllerContext, + LogExplorerControllerEvent, + LogExplorerControllerTypeState + >( + { + context: initialContext, + predictableActionArguments: true, + id: 'LogExplorerController', + initial: 'uninitialized', + states: { + uninitialized: { + on: { + RECEIVED_STATE_CONTAINER: { + target: 'initializingDataView', + actions: ['storeDiscoverStateContainer'], + }, + }, + }, + initializingDataView: { + invoke: { + src: 'createDataView', + onDone: { + target: 'initializingControlPanels', + actions: ['updateDiscoverAppStateFromContext', 'updateTimefilterFromContext'], + }, + onError: { + target: 'initialized', + actions: [ + 'notifyCreateDataViewFailed', + 'updateDiscoverAppStateFromContext', + 'updateTimefilterFromContext', + ], + }, + }, + }, + initializingControlPanels: { + invoke: { + src: 'initializeControlPanels', + onDone: { + target: 'initialized', + actions: ['storeControlPanels'], + }, + onError: { + target: 'initialized', + }, + }, + }, + initialized: { + type: 'parallel', + invoke: [ + { + src: 'discoverStateService', + id: 'discoverStateService', + }, + { + src: 'timefilterService', + id: 'timefilterService', + }, + ], + states: { + datasetSelection: { + initial: 'validatingSelection', + states: { + validatingSelection: { + invoke: { + src: 'validateSelection', + }, + on: { + LISTEN_TO_CHANGES: { + target: 'idle', + }, + UPDATE_DATASET_SELECTION: { + target: 'updatingDataView', + actions: ['storeDatasetSelection'], + }, + DATASET_SELECTION_RESTORE_FAILURE: { + target: 'updatingDataView', + actions: ['notifyDatasetSelectionRestoreFailed'], + }, + }, + }, + idle: { + on: { + UPDATE_DATASET_SELECTION: { + target: 'updatingDataView', + actions: ['storeDatasetSelection'], + }, + DATASET_SELECTION_RESTORE_FAILURE: { + target: 'updatingDataView', + actions: ['notifyDatasetSelectionRestoreFailed'], + }, + }, + }, + updatingDataView: { + invoke: { + src: 'createDataView', + onDone: { + target: 'idle', + actions: ['notifyDataViewUpdate'], + }, + onError: { + target: 'idle', + actions: ['notifyCreateDataViewFailed'], + }, + }, + }, + }, + }, + controlGroups: { + initial: 'uninitialized', + states: { + uninitialized: { + on: { + INITIALIZE_CONTROL_GROUP_API: { + target: 'idle', + cond: 'controlGroupAPIExists', + actions: ['storeControlGroupAPI'], + }, + }, + }, + idle: { + invoke: { + src: 'subscribeControlGroup', + }, + on: { + DATA_VIEW_UPDATED: { + target: 'updatingControlPanels', + }, + UPDATE_CONTROL_PANELS: { + target: 'updatingControlPanels', + }, + }, + }, + updatingControlPanels: { + invoke: { + src: 'updateControlPanels', + onDone: { + target: 'idle', + actions: ['storeControlPanels'], + }, + onError: { + target: 'idle', + }, + }, + }, + }, + }, + }, + on: { + RECEIVE_DISCOVER_APP_STATE: { + actions: ['updateContextFromDiscoverAppState'], + }, + RECEIVE_QUERY_STATE: { + actions: ['updateQueryStateFromQueryServiceState'], + }, + RECEIVE_TIMEFILTER_TIME: { + actions: ['updateContextFromTimefilter'], + }, + RECEIVE_TIMEFILTER_REFRESH_INTERVAL: { + actions: ['updateContextFromTimefilter'], + }, + }, + }, + }, + }, + { + actions: { + storeDatasetSelection: actions.assign((_context, event) => + 'data' in event && isDatasetSelection(event.data) + ? { + datasetSelection: event.data, + } + : {} + ), + storeDiscoverStateContainer: actions.assign((_context, event) => + 'discoverStateContainer' in event + ? { + discoverStateContainer: event.discoverStateContainer, + } + : {} + ), + storeControlGroupAPI: actions.assign((_context, event) => + 'controlGroupAPI' in event + ? { + controlGroupAPI: event.controlGroupAPI, + } + : {} + ), + storeControlPanels: actions.assign((_context, event) => + 'data' in event && ControlPanelRT.is(event.data) + ? { + controlPanels: event.data, + } + : {} + ), + notifyDataViewUpdate: raise('DATA_VIEW_UPDATED'), + updateContextFromDiscoverAppState, + updateDiscoverAppStateFromContext, + updateContextFromTimefilter, + }, + guards: { + controlGroupAPIExists: (_context, event) => { + return 'controlGroupAPI' in event && event.controlGroupAPI != null; + }, + }, + } + ); + +export interface LogExplorerControllerStateMachineDependencies { + datasetsClient: IDatasetsClient; + initialContext?: LogExplorerControllerContext; + query: QueryStart; + toasts: IToasts; +} + +export const createLogExplorerControllerStateMachine = ({ + datasetsClient, + initialContext = DEFAULT_CONTEXT, + query, + toasts, +}: LogExplorerControllerStateMachineDependencies) => + createPureLogExplorerControllerStateMachine(initialContext).withConfig({ + actions: { + notifyCreateDataViewFailed: createCreateDataViewFailedNotifier(toasts), + notifyDatasetSelectionRestoreFailed: createDatasetSelectionRestoreFailedNotifier(toasts), + updateTimefilterFromContext: updateTimefilterFromContext(query), + }, + services: { + createDataView: createAndSetDataView(), + initializeControlPanels: initializeControlPanels(), + subscribeControlGroup: subscribeControlGroup(), + updateControlPanels: updateControlPanels(), + validateSelection: validateSelection({ datasetsClient }), + discoverStateService: subscribeToDiscoverState(), + timefilterService: subscribeToTimefilterService(query), + }, + }); + +export const initializeLogExplorerControllerStateService = ( + deps: LogExplorerControllerStateMachineDependencies +) => { + const machine = createLogExplorerControllerStateMachine(deps); + return interpret(machine).start(); +}; + +export type LogExplorerControllerStateService = InterpreterFrom< + typeof createLogExplorerControllerStateMachine +>; + +export type LogExplorerControllerStateMachine = ReturnType< + typeof createLogExplorerControllerStateMachine +>; diff --git a/x-pack/plugins/log_explorer/public/state_machines/log_explorer_controller/src/types.ts b/x-pack/plugins/log_explorer/public/state_machines/log_explorer_controller/src/types.ts new file mode 100644 index 0000000000000..73d87913a8150 --- /dev/null +++ b/x-pack/plugins/log_explorer/public/state_machines/log_explorer_controller/src/types.ts @@ -0,0 +1,166 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ControlGroupAPI } from '@kbn/controls-plugin/public'; +import { QueryState, RefreshInterval, TimeRange } from '@kbn/data-plugin/common'; +import { DiscoverAppState, DiscoverStateContainer } from '@kbn/discover-plugin/public'; +import { DoneInvokeEvent } from 'xstate'; +import { ControlPanels, DisplayOptions } from '../../../../common'; +import type { DatasetEncodingError, DatasetSelection } from '../../../../common/dataset_selection'; + +export interface WithDatasetSelection { + datasetSelection: DatasetSelection; +} + +export interface WithControlPanelGroupAPI { + controlGroupAPI: ControlGroupAPI; +} + +export interface WithControlPanels { + controlPanels?: ControlPanels; +} + +export type WithQueryState = QueryState; + +export type WithDisplayOptions = DisplayOptions; + +export interface WithDiscoverStateContainer { + discoverStateContainer: DiscoverStateContainer; +} + +export type DefaultLogExplorerControllerState = WithDatasetSelection & + WithQueryState & + WithDisplayOptions; + +export type LogExplorerControllerTypeState = + | { + value: 'uninitialized'; + context: WithDatasetSelection & WithControlPanels & WithQueryState & WithDisplayOptions; + } + | { + value: 'initializingDataView'; + context: WithDatasetSelection & WithControlPanels & WithQueryState & WithDisplayOptions; + } + | { + value: 'initializingControlPanels'; + context: WithDatasetSelection & WithControlPanels & WithQueryState & WithDisplayOptions; + } + | { + value: 'initializingStateContainer'; + context: WithDatasetSelection & WithControlPanels & WithQueryState & WithDisplayOptions; + } + | { + value: 'initialized'; + context: WithDatasetSelection & + WithControlPanels & + WithQueryState & + WithDisplayOptions & + WithDiscoverStateContainer; + } + | { + value: 'initialized.datasetSelection.validatingSelection'; + context: WithDatasetSelection & + WithControlPanels & + WithQueryState & + WithDisplayOptions & + WithDiscoverStateContainer; + } + | { + value: 'initialized.datasetSelection.idle'; + context: WithDatasetSelection & + WithControlPanels & + WithQueryState & + WithDisplayOptions & + WithDiscoverStateContainer; + } + | { + value: 'initialized.datasetSelection.updatingDataView'; + context: WithDatasetSelection & + WithControlPanels & + WithQueryState & + WithDisplayOptions & + WithDiscoverStateContainer; + } + | { + value: 'initialized.datasetSelection.updatingStateContainer'; + context: WithDatasetSelection & + WithControlPanels & + WithQueryState & + WithDisplayOptions & + WithDiscoverStateContainer; + } + | { + value: 'initialized.controlGroups.uninitialized'; + context: WithDatasetSelection & + WithControlPanels & + WithQueryState & + WithDisplayOptions & + WithDiscoverStateContainer; + } + | { + value: 'initialized.controlGroups.idle'; + context: WithDatasetSelection & + WithControlPanelGroupAPI & + WithControlPanels & + WithQueryState & + WithDisplayOptions & + WithDiscoverStateContainer; + } + | { + value: 'initialized.controlGroups.updatingControlPanels'; + context: WithDatasetSelection & + WithControlPanelGroupAPI & + WithControlPanels & + WithQueryState & + WithDisplayOptions & + WithDiscoverStateContainer; + }; + +export type LogExplorerControllerContext = LogExplorerControllerTypeState['context']; + +export type LogExplorerControllerStateValue = LogExplorerControllerTypeState['value']; + +export type LogExplorerControllerEvent = + | { + type: 'RECEIVED_STATE_CONTAINER'; + discoverStateContainer: DiscoverStateContainer; + } + | { + type: 'LISTEN_TO_CHANGES'; + } + | { + type: 'UPDATE_DATASET_SELECTION'; + data: DatasetSelection; + } + | { + type: 'DATASET_SELECTION_RESTORE_FAILURE'; + } + | { + type: 'INITIALIZE_CONTROL_GROUP_API'; + controlGroupAPI: ControlGroupAPI | undefined; + } + | { + type: 'UPDATE_CONTROL_PANELS'; + controlPanels: ControlPanels | null; + } + | { + type: 'RECEIVE_DISCOVER_APP_STATE'; + appState: DiscoverAppState; + } + | { + type: 'RECEIVE_TIMEFILTER_TIME'; + time: TimeRange; + } + | { + type: 'RECEIVE_TIMEFILTER_REFRESH_INTERVAL'; + refreshInterval: RefreshInterval; + } + | DoneInvokeEvent + | DoneInvokeEvent + | DoneInvokeEvent + | DoneInvokeEvent + | DoneInvokeEvent; diff --git a/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/state_machine.ts b/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/state_machine.ts deleted file mode 100644 index 4fa1673b5879d..0000000000000 --- a/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/state_machine.ts +++ /dev/null @@ -1,279 +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 { IToasts } from '@kbn/core/public'; -import { DiscoverStateContainer } from '@kbn/discover-plugin/public'; -import { actions, createMachine, interpret, InterpreterFrom, raise } from 'xstate'; -import { IDatasetsClient } from '../../../services/datasets'; -import { isDatasetSelection } from '../../../../common/dataset_selection'; -import { createAndSetDataView } from './data_view_service'; -import { validateSelection } from './selection_service'; -import { DEFAULT_CONTEXT } from './defaults'; -import { - createCreateDataViewFailedNotifier, - createDatasetSelectionRestoreFailedNotifier, -} from './notifications'; -import { - ControlPanelRT, - LogExplorerProfileContext, - LogExplorerProfileEvent, - LogExplorerProfileTypeState, -} from './types'; -import { - initializeControlPanels, - initializeFromUrl, - listenUrlChange, - subscribeControlGroup, - updateControlPanels, - updateStateContainer, -} from './url_state_storage_service'; - -export const createPureLogExplorerProfileStateMachine = ( - initialContext: LogExplorerProfileContext -) => - /** @xstate-layout N4IgpgJg5mDOIC5QBkD2UCiAPADgG1QCcxCAFQ1AMwEs8wA6AVwDtrWAXagQz2oC9IAYgDaABgC6iUDlSxqnVMykgsiAIwAOAMz0NagEwBWUdoBsWgOxaAnEYA0IAJ7rt9UxesWD+2xf2WAXwCHNExcAmIyCho6ejZ5bl4+NigAMQoAWwBVQjxBCEUGNgA3VABrBlDsfCIScipaIo5E-hT01GzchBLUAGMuBWYxcWHlGTlB5VUEYw1dfQ1RQ30LC0NrbX0HZwQ1CwAWemtDLTV991FTPWWtIJD0aoi66Ma45p5W5jTMnLySCkI9HwA0oRAy9Cq4VqUQasXinA+yS+7U6eG6zFK-UGw1GSBA4wSiimiFm80Wy1W60220QenoxlEjP2J32alEmmsdxAkJqkXqMSaCURKQAIgMuAA1ahgADu+UKb1KFQhDyhfJecPeSVF4qlsvRmIG1EUOIkY1khKUeOmPkM9D8pkMFm0bMpFhpM3Obi0tqdxk0Fi5PKeMIFbyF2q+YvYkulcv+RCBeBBYJVYV5z1hgoRkag0dj+p6WONQwkuOkFsm1sQtvt+kdztOojdHquonoah91h9WkWS1MQdVGdDr3hLSRUAAwop2BQ8KQuMwwHhYPKl4rypUhyH+aOtZ8pzO5wulyuDX0jSay2a8QSq6BphZTNZ6Po1O+jMy++YPScLEdLi0UwDG7Z9jkHdMdw1bNxxSadmFnVB50XZdVwTQFgXYUFCHBYNoV3TUIwPeDEOQ09YHPYsrxGG8KwmEtiQQJ8XzfD9DC-RkfycRB9msdt9kuBYNA0Dxlg0Adgm5bd8Og8McwPABlGN2DAEiuDYEg1yaJUt0gmSszk2CviUgZVJndSl0ISjL1LGjJFvSsGOrBAtC0Q4tFEc5DHE-YPI0XjTA9NRDCdI4rhEvR9HrEKIMefSwzHYVjOUsyEIszT0KTFMcLTOL1QMxLcxMlS1I0qyixs017Loy1GNc9zPMdHy-ICoLDDZehzg0Wx3z4vtbkkvD8oS-cBAgegIHFWAwHYBTlzAXpBnoYoPkmzhjPmxaS0EZAAEkFIAFQwAA5AB9A6AHlTsnAAJABBY6AHEMAU8t8UcolnOE-9fLWZ9NEsAwgtc9ttG6p0fQsQCNFitVMxGoixomqaZrmugtsUZbVqNDb0cGQQslIEU7qO07iYOu6FIwA7Tqp5AMEnA7dou463rvJyH1pETOssQx-u0Lwtm43Yzjtbz1jZGwDg2Ab7j04a90RyBkZjabZs2paVt4NaUjRhb8fJynqdpjB6cZ5mzoAJRey7rdO1I7t25AsmttmPqtTmEG+nm-usAHBba9rOp67trGffz-Nh4cCJgxFlbWrg1b1jHmDiCA6AJomSYwMmSaNmm6YZpmWbd+jPs9s5PFff19jWZsDH2IWdk7MP7UdUx-GbdxTGbKOoIK0b45R9W8ZLNOM8NqmC9NouLdO63Douu2Hadl2MFL2rnMr-8jHZWvjEFxvgcZehTn0fZIr48WYcG6SFcI+SkYTpONbHxgcB1qNdTjLSN2VIb4aK0fkPVWqNX6Y3fp-PM39CwYgvNia81V3plw9ioGsbI1CvnfJ5SwtdTB4OBmoF83k+KmHcCcNyN85Z5UAQ-ccIDE5gNHhAj+ONoExj1PGQgAIspYVTAAkcdC47jWfkw-Wb9WHrXYQWGU1kEF2XNCgxib52RYLZL9PBBDhadncPQEwfF-BXGWKIBYfd4pAPoSI4eyclqQLYcVVKMYyq-x6P-O+tDY5JAYS-Zhqc7FSIcaVSyciSxVUUZvT2KjMGsRwQcJ8Wjm7sUwdYEOlgnxOk5LfeWHjDLCJVowke4iWFQMCeZZxmVMLYVwu4wRnj+DeLESnJgkjdYpSCSQEJ1EN73jQQgFR+h6TnGsL5TQQlz5BWCnMEwEsTGeSipk6hcNam5K8eNXoR4kKPQoO-WATBWCDwgIIXax1dpMzuntAAWjnScLMDqWwusgU6j17mE1OndUgu1ukc16bYQ4ngyHuDDs2ICgVtFrB+syICfse5QxsGY++dSkbrIQnOLZqAdnjzAIIQ2p0JS7QwAAdVOoTcmGARRfPLr098T5T6+T4s6RYWhvIen0J5O0TLgpkPWN3FY8KcmFXqWsjZeA0UYuoOnLFJLs7XVufcx5pAHqm1erRZBESqWdh0EsWu-kiG1zWEFDQJxdAbHaqIYZVxnQSUWdHWSAqkXCtFTgXZ-i4LCpPKhFxcC3HZOWXa5WyLSKOudS0r4JFjwoTPBVeRFLUHTF+UcJ8ZCPAwpBUFPi7kNjnGlm+c1fLfUHPoAG1F2ynXNKgWGpC7qVyCAqcmPhOUBExxWYKwtDqS3BvLW6iNFEo2hMQeEnpcbz4JoBcm4FZC01V0NWoHuYd8F9SCJJZgqAIBwGUI26CA7vnTAALSgp2Duu0KTj0npPVFPNTaWB+ogFuyl0wj7aI0AMwFBxOx8ydG+C9trRptB+LkW9saazOmNf5aZ+CfTMjai+PYngjCdgOJ5bQX6B6Ix1BwuMAHGLuFbPg0+6xTV6EuMyK1UkfVNrta6lFlbu2YecsYAZjciGms8qyVlhhfxRQ7F2KW58Fh+2QwjR+rTTLtMILRz22HtHtRYhyN87gpZMoExY4R4nensR0E6PQz5WTsm+h6Tw7ZvIwuZBSZ8stSM0PzUrKxoDCkp1U9MCW9JnQzuGRgvT2ilhTMNSk8+axOz7CU0I1Z+SfFFNTlrcV9jwGoPZnexA5hiEue0+5kSx9-xgxScJXiWqFkWaWeRgtoi7NLXFXQBz6gjDPq8H7KkAkbDpd0DYfs6wTGdiC4ihpJWJFQPzJwiruwRIMf8rxXyzZmSdmPi+VyfFJawfEoYDrzan7WJi2W+xbSymWQG4Y0wnURvNYg0yiZ-ksHn20LxXsIUSMbpQ8AoVVGRXtoG05zTrmdOaDS9oshL5hkUL9D3KKVD8s2ru5Y1tj2g17OvQNtYcwZ2smy+Qp8WggrOnbCHYCLmNX6CW9eiHgb22YoG2cDqrJWR+Dwf6DQaaTG6GluJNy13nR44LUWzZROXWhq7eRAbiXnNabc7pr7zdDV7cQ+1CbthnyBkXUAA */ - createMachine( - { - context: initialContext, - predictableActionArguments: true, - id: 'LogExplorerProfile', - initial: 'uninitialized', - states: { - uninitialized: { - always: 'initializingFromUrl', - }, - initializingFromUrl: { - invoke: { - src: 'initializeFromUrl', - onDone: { - target: 'initializingDataView', - actions: ['storeDatasetSelection'], - }, - onError: { - target: 'initializingDataView', - actions: ['notifyDatasetSelectionRestoreFailed'], - }, - }, - }, - initializingDataView: { - invoke: { - src: 'createDataView', - onDone: { - target: 'initializingControlPanels', - }, - onError: { - target: 'initialized', - actions: ['notifyCreateDataViewFailed'], - }, - }, - }, - initializingControlPanels: { - invoke: { - src: 'initializeControlPanels', - onDone: { - target: 'initializingStateContainer', - actions: ['storeControlPanels'], - }, - onError: { - target: 'initializingStateContainer', - }, - }, - }, - initializingStateContainer: { - invoke: { - src: 'updateStateContainer', - onDone: { - target: 'initialized', - }, - onError: { - target: 'initialized', - }, - }, - }, - initialized: { - type: 'parallel', - states: { - datasetSelection: { - initial: 'validatingSelection', - states: { - validatingSelection: { - invoke: { - src: 'validateSelection', - }, - on: { - LISTEN_TO_CHANGES: { - target: 'idle', - }, - UPDATE_DATASET_SELECTION: { - target: 'updatingDataView', - actions: ['storeDatasetSelection'], - }, - DATASET_SELECTION_RESTORE_FAILURE: { - target: 'updatingDataView', - actions: ['notifyDatasetSelectionRestoreFailed'], - }, - }, - }, - idle: { - invoke: { - src: 'listenUrlChange', - }, - on: { - UPDATE_DATASET_SELECTION: { - target: 'updatingDataView', - actions: ['storeDatasetSelection'], - }, - DATASET_SELECTION_RESTORE_FAILURE: { - target: 'updatingDataView', - actions: ['notifyDatasetSelectionRestoreFailed'], - }, - }, - }, - updatingDataView: { - invoke: { - src: 'createDataView', - onDone: { - target: 'updatingStateContainer', - }, - onError: { - target: 'updatingStateContainer', - actions: ['notifyCreateDataViewFailed'], - }, - }, - }, - updatingStateContainer: { - invoke: { - src: 'updateStateContainer', - onDone: { - target: 'idle', - actions: ['notifyDataViewUpdate'], - }, - onError: { - target: 'idle', - actions: ['notifyCreateDataViewFailed'], - }, - }, - }, - }, - }, - controlGroups: { - initial: 'uninitialized', - states: { - uninitialized: { - on: { - INITIALIZE_CONTROL_GROUP_API: { - target: 'idle', - cond: 'controlGroupAPIExists', - actions: ['storeControlGroupAPI'], - }, - }, - }, - idle: { - invoke: { - src: 'subscribeControlGroup', - }, - on: { - DATA_VIEW_UPDATED: { - target: 'updatingControlPanels', - }, - UPDATE_CONTROL_PANELS: { - target: 'updatingControlPanels', - }, - }, - }, - updatingControlPanels: { - invoke: { - src: 'updateControlPanels', - onDone: { - target: 'idle', - actions: ['storeControlPanels'], - }, - onError: { - target: 'idle', - }, - }, - }, - }, - }, - }, - }, - }, - }, - { - actions: { - storeDatasetSelection: actions.assign((_context, event) => - 'data' in event && isDatasetSelection(event.data) - ? { - datasetSelection: event.data, - } - : {} - ), - storeControlGroupAPI: actions.assign((_context, event) => - 'controlGroupAPI' in event - ? { - controlGroupAPI: event.controlGroupAPI, - } - : {} - ), - storeControlPanels: actions.assign((_context, event) => - 'data' in event && ControlPanelRT.is(event.data) - ? { - controlPanels: event.data, - } - : {} - ), - notifyDataViewUpdate: raise('DATA_VIEW_UPDATED'), - }, - guards: { - controlGroupAPIExists: (_context, event) => { - return 'controlGroupAPI' in event && event.controlGroupAPI != null; - }, - }, - } - ); - -export interface LogExplorerProfileStateMachineDependencies { - initialContext?: LogExplorerProfileContext; - datasetsClient: IDatasetsClient; - stateContainer: DiscoverStateContainer; - toasts: IToasts; -} - -export const createLogExplorerProfileStateMachine = ({ - initialContext = DEFAULT_CONTEXT, - datasetsClient, - stateContainer, - toasts, -}: LogExplorerProfileStateMachineDependencies) => - createPureLogExplorerProfileStateMachine(initialContext).withConfig({ - actions: { - notifyCreateDataViewFailed: createCreateDataViewFailedNotifier(toasts), - notifyDatasetSelectionRestoreFailed: createDatasetSelectionRestoreFailedNotifier(toasts), - }, - services: { - createDataView: createAndSetDataView({ stateContainer }), - initializeFromUrl: initializeFromUrl({ stateContainer }), - initializeControlPanels: initializeControlPanels({ stateContainer }), - listenUrlChange: listenUrlChange({ stateContainer }), - subscribeControlGroup: subscribeControlGroup({ stateContainer }), - updateControlPanels: updateControlPanels({ stateContainer }), - updateStateContainer: updateStateContainer({ stateContainer }), - validateSelection: validateSelection({ datasetsClient }), - }, - }); - -export const initializeLogExplorerProfileStateService = ( - deps: LogExplorerProfileStateMachineDependencies -) => { - const machine = createLogExplorerProfileStateMachine(deps); - - return interpret(machine).start(); -}; - -export type LogExplorerProfileStateService = InterpreterFrom< - typeof createLogExplorerProfileStateMachine ->; diff --git a/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/types.ts b/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/types.ts deleted file mode 100644 index fe4323fac0cd4..0000000000000 --- a/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/types.ts +++ /dev/null @@ -1,128 +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 * as rt from 'io-ts'; -import { ControlGroupAPI } from '@kbn/controls-plugin/public'; -import { DoneInvokeEvent } from 'xstate'; -import type { DatasetEncodingError, DatasetSelection } from '../../../../common/dataset_selection'; - -export interface WithDatasetSelection { - datasetSelection: DatasetSelection; -} - -export interface WithControlPanelGroupAPI { - controlGroupAPI: ControlGroupAPI; -} - -export interface WithControlPanels { - controlPanels: ControlPanels; -} - -export type DefaultLogExplorerProfileState = WithDatasetSelection; - -export type LogExplorerProfileTypeState = - | { - value: 'uninitialized'; - context: WithDatasetSelection; - } - | { - value: 'initializingFromUrl'; - context: WithDatasetSelection; - } - | { - value: 'initializingDataView'; - context: WithDatasetSelection; - } - | { - value: 'initializingControlPanels'; - context: WithDatasetSelection; - } - | { - value: 'initializingStateContainer'; - context: WithDatasetSelection & WithControlPanels; - } - | { - value: 'initialized'; - context: WithDatasetSelection & WithControlPanels; - } - | { - value: 'initialized.datasetSelection.validatingSelection'; - context: WithDatasetSelection & WithControlPanels; - } - | { - value: 'initialized.datasetSelection.idle'; - context: WithDatasetSelection & WithControlPanels; - } - | { - value: 'initialized.datasetSelection.updatingDataView'; - context: WithDatasetSelection & WithControlPanels; - } - | { - value: 'initialized.datasetSelection.updatingStateContainer'; - context: WithDatasetSelection & WithControlPanels; - } - | { - value: 'initialized.controlGroups.uninitialized'; - context: WithDatasetSelection & WithControlPanels; - } - | { - value: 'initialized.controlGroups.idle'; - context: WithDatasetSelection & WithControlPanelGroupAPI & WithControlPanels; - } - | { - value: 'initialized.controlGroups.updatingControlPanels'; - context: WithDatasetSelection & WithControlPanelGroupAPI & WithControlPanels; - }; - -export type LogExplorerProfileContext = LogExplorerProfileTypeState['context']; - -export type LogExplorerProfileStateValue = LogExplorerProfileTypeState['value']; - -export type LogExplorerProfileEvent = - | { - type: 'LISTEN_TO_CHANGES'; - } - | { - type: 'UPDATE_DATASET_SELECTION'; - data: DatasetSelection; - } - | { - type: 'DATASET_SELECTION_RESTORE_FAILURE'; - } - | { - type: 'INITIALIZE_CONTROL_GROUP_API'; - controlGroupAPI: ControlGroupAPI | undefined; - } - | { - type: 'UPDATE_CONTROL_PANELS'; - controlPanels: ControlPanels | null; - } - | DoneInvokeEvent - | DoneInvokeEvent - | DoneInvokeEvent - | DoneInvokeEvent - | DoneInvokeEvent; - -const PanelRT = rt.type({ - order: rt.number, - width: rt.union([rt.literal('medium'), rt.literal('small'), rt.literal('large')]), - grow: rt.boolean, - type: rt.string, - explicitInput: rt.intersection([ - rt.type({ id: rt.string }), - rt.partial({ - dataViewId: rt.string, - fieldName: rt.string, - title: rt.union([rt.string, rt.undefined]), - selectedOptions: rt.array(rt.string), - }), - ]), -}); - -export const ControlPanelRT = rt.record(rt.string, PanelRT); - -export type ControlPanels = rt.TypeOf; diff --git a/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/url_state_storage_service.ts b/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/url_state_storage_service.ts deleted file mode 100644 index b84cce4dbf2cf..0000000000000 --- a/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/url_state_storage_service.ts +++ /dev/null @@ -1,284 +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 { InvokeCreator } from 'xstate'; -import { pick, mapValues } from 'lodash'; -import deepEqual from 'fast-deep-equal'; -import { DiscoverAppState, DiscoverStateContainer } from '@kbn/discover-plugin/public'; -import type { DataView } from '@kbn/data-views-plugin/public'; -import { ROWS_HEIGHT_OPTIONS } from '@kbn/unified-data-table'; -import { - AllDatasetSelection, - decodeDatasetSelectionId, - hydrateDatasetSelection, - isDatasetSelection, -} from '../../../../common/dataset_selection'; -import { - DATA_GRID_COLUMNS_PREFERENCES, - DATA_GRID_DEFAULT_COLUMNS, - LOG_LEVEL_FIELD, -} from '../../../../common/constants'; -import { - ControlPanelRT, - ControlPanels, - LogExplorerProfileContext, - LogExplorerProfileEvent, -} from './types'; -import { - availableControlPanelFields, - controlPanelConfigs, - CONTROL_PANELS_URL_KEY, -} from './defaults'; - -interface LogExplorerProfileUrlStateDependencies { - stateContainer: DiscoverStateContainer; -} - -export const listenUrlChange = - ({ - stateContainer, - }: LogExplorerProfileUrlStateDependencies): InvokeCreator< - LogExplorerProfileContext, - LogExplorerProfileEvent - > => - (context) => - (send) => { - const unsubscribe = stateContainer.appState.subscribe((nextState) => { - const { index } = nextState; - const prevIndex = stateContainer.appState.getPrevious().index; - - // Preventing update if the index didn't change - if (prevIndex === index) return; - - try { - const datasetSelection = extractDatasetSelectionFromIndex({ index, context }); - - if (isDatasetSelection(datasetSelection)) { - send({ type: 'UPDATE_DATASET_SELECTION', data: datasetSelection }); - } - } catch (error) { - send({ type: 'DATASET_SELECTION_RESTORE_FAILURE' }); - } - }); - - return () => unsubscribe(); - }; - -export const initializeFromUrl = - ({ - stateContainer, - }: LogExplorerProfileUrlStateDependencies): InvokeCreator< - LogExplorerProfileContext, - LogExplorerProfileEvent - > => - async (context) => { - const { index } = stateContainer.appState.getState(); - - return extractDatasetSelectionFromIndex({ index, context }); - }; - -export const initializeControlPanels = - ({ - stateContainer, - }: LogExplorerProfileUrlStateDependencies): InvokeCreator< - LogExplorerProfileContext, - LogExplorerProfileEvent - > => - async (context) => { - const urlPanels = stateContainer.stateStorage.get(CONTROL_PANELS_URL_KEY); - const controlPanelsWithId = constructControlPanelsWithDataViewId(stateContainer, urlPanels!); - - return controlPanelsWithId; - }; - -const extractDatasetSelectionFromIndex = ({ - index, - context, -}: { - index?: string; - context: LogExplorerProfileContext; -}) => { - // If the index parameter doesn't exists, use initialContext value or fallback to AllDatasetSelection - if (!index) { - return context.datasetSelection ?? AllDatasetSelection.create(); - } - - const rawDatasetSelection = decodeDatasetSelectionId(index); - const datasetSelection = hydrateDatasetSelection(rawDatasetSelection); - - return datasetSelection; -}; - -export const subscribeControlGroup = - ({ - stateContainer, - }: LogExplorerProfileUrlStateDependencies): InvokeCreator< - LogExplorerProfileContext, - LogExplorerProfileEvent - > => - (context) => - (send) => { - if (!('controlGroupAPI' in context)) return; - - const filtersSubscription = context.controlGroupAPI.onFiltersPublished$.subscribe( - (newFilters) => { - stateContainer.internalState.transitions.setCustomFilters(newFilters); - stateContainer.actions.fetchData(); - } - ); - - // Keeps our state in sync with the url changes and makes sure it adheres to correct schema - const urlSubscription = stateContainer.stateStorage - .change$(CONTROL_PANELS_URL_KEY) - .subscribe((controlPanels) => { - if (!deepEqual(controlPanels, context.controlPanels)) { - send({ type: 'UPDATE_CONTROL_PANELS', controlPanels }); - } - }); - - // Keeps the url in sync with the controls state after change - const inputSubscription = context.controlGroupAPI.getInput$().subscribe(({ panels }) => { - if (!deepEqual(panels, context.controlPanels)) { - send({ type: 'UPDATE_CONTROL_PANELS', controlPanels: panels }); - } - }); - - return () => { - filtersSubscription.unsubscribe(); - urlSubscription.unsubscribe(); - inputSubscription.unsubscribe(); - }; - }; - -export const updateControlPanels = - ({ - stateContainer, - }: LogExplorerProfileUrlStateDependencies): InvokeCreator< - LogExplorerProfileContext, - LogExplorerProfileEvent - > => - async (context, event) => { - if (!('controlGroupAPI' in context)) return; - - const newControlPanels = - ('controlPanels' in event && event.controlPanels) || context.controlPanels; - const controlPanelsWithId = constructControlPanelsWithDataViewId( - stateContainer, - newControlPanels! - ); - - context.controlGroupAPI.updateInput({ panels: controlPanelsWithId }); - - return controlPanelsWithId; - }; - -export const updateStateContainer = - ({ - stateContainer, - }: LogExplorerProfileUrlStateDependencies): InvokeCreator< - LogExplorerProfileContext, - LogExplorerProfileEvent - > => - async () => { - const { breakdownField, columns, grid, rowHeight } = stateContainer.appState.getState(); - const stateUpdates: DiscoverAppState = {}; - - // Update data grid columns list - const shouldSetDefaultColumns = - stateContainer.appState.isEmptyURL() || !columns || columns.length === 0; - if (shouldSetDefaultColumns) { - stateUpdates.columns = DATA_GRID_DEFAULT_COLUMNS; - } - - // Configure DataGrid columns preferences - const initialColumnsPreferences = grid?.columns ?? {}; - stateUpdates.grid = { - columns: { ...DATA_GRID_COLUMNS_PREFERENCES, ...initialColumnsPreferences }, - }; - - // Configure rowHeight preference - stateUpdates.rowHeight = rowHeight ?? ROWS_HEIGHT_OPTIONS.single; - - // Configure breakdown field preference - stateUpdates.breakdownField = breakdownField ?? LOG_LEVEL_FIELD; - - // Finally batch update state app state - stateContainer.appState.update(stateUpdates, true); - }; - -/** - * Utils - */ - -const constructControlPanelsWithDataViewId = ( - stateContainer: DiscoverStateContainer, - newControlPanels: ControlPanels -) => { - const dataView = stateContainer.internalState.getState().dataView!; - - const validatedControlPanels = isValidState(newControlPanels) - ? newControlPanels - : getVisibleControlPanelsConfig(dataView); - - const controlsPanelsWithId = mergeDefaultPanelsWithUrlConfig(dataView, validatedControlPanels!); - - if (!deepEqual(controlsPanelsWithId, stateContainer.stateStorage.get(CONTROL_PANELS_URL_KEY))) { - stateContainer.stateStorage.set( - CONTROL_PANELS_URL_KEY, - cleanControlPanels(controlsPanelsWithId), - { replace: true } - ); - } - - return controlsPanelsWithId; -}; - -const isValidState = (state: ControlPanels | undefined | null): boolean => { - return Object.keys(state ?? {}).length > 0 && ControlPanelRT.is(state); -}; - -const getVisibleControlPanels = (dataView: DataView | undefined) => - availableControlPanelFields.filter( - (panelKey) => dataView?.fields.getByName(panelKey) !== undefined - ); - -export const getVisibleControlPanelsConfig = (dataView?: DataView) => { - return getVisibleControlPanels(dataView).reduce((panelsMap, panelKey) => { - const config = controlPanelConfigs[panelKey]; - - return { ...panelsMap, [panelKey]: config }; - }, {} as ControlPanels); -}; - -const addDataViewIdToControlPanels = (controlPanels: ControlPanels, dataViewId: string = '') => { - return mapValues(controlPanels, (controlPanelConfig) => ({ - ...controlPanelConfig, - explicitInput: { ...controlPanelConfig.explicitInput, dataViewId }, - })); -}; - -const cleanControlPanels = (controlPanels: ControlPanels) => { - return mapValues(controlPanels, (controlPanelConfig) => { - const { explicitInput } = controlPanelConfig; - const { dataViewId, ...rest } = explicitInput; - return { ...controlPanelConfig, explicitInput: rest }; - }); -}; - -const mergeDefaultPanelsWithUrlConfig = (dataView: DataView, urlPanels: ControlPanels) => { - // Get default panel configs from existing fields in data view - const visiblePanels = getVisibleControlPanelsConfig(dataView); - - // Get list of panel which can be overridden to avoid merging additional config from url - const existingKeys = Object.keys(visiblePanels); - const controlPanelsToOverride = pick(urlPanels, existingKeys); - - // Merge default and existing configs and add dataView.id to each of them - return addDataViewIdToControlPanels( - { ...visiblePanels, ...controlPanelsToOverride }, - dataView.id - ); -}; diff --git a/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/utils.ts b/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/utils.ts deleted file mode 100644 index c7907cb358e0c..0000000000000 --- a/x-pack/plugins/log_explorer/public/state_machines/log_explorer_profile/src/utils.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 { LogExplorerProfileStateService } from './state_machine'; -import { LogExplorerProfileStateValue } from './types'; - -export const waitForState = ( - service: LogExplorerProfileStateService, - targetState: LogExplorerProfileStateValue -) => { - return new Promise((resolve) => { - const { unsubscribe } = service.subscribe((state) => { - if (state.matches(targetState)) { - resolve(state); - unsubscribe(); - } - }); - }); -}; diff --git a/x-pack/plugins/log_explorer/public/types.ts b/x-pack/plugins/log_explorer/public/types.ts index e07b8fdb14b3f..32fcf5315e27c 100644 --- a/x-pack/plugins/log_explorer/public/types.ts +++ b/x-pack/plugins/log_explorer/public/types.ts @@ -6,18 +6,22 @@ */ import type { ComponentType } from 'react'; import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; -import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; +import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import type { DiscoverSetup, DiscoverStart } from '@kbn/discover-plugin/public'; -import { SharePluginSetup } from '@kbn/share-plugin/public'; -import { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; -import { LogExplorerLocators } from '../common/locators'; +import type { SharePluginSetup } from '@kbn/share-plugin/public'; +import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public'; +import type { NavigationPublicPluginStart } from '@kbn/navigation-plugin/public'; +import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; +import type { LogExplorerLocators } from '../common/locators'; import type { LogExplorerProps } from './components/log_explorer'; +import type { CreateLogExplorerController } from './controller'; export interface LogExplorerPluginSetup { locators: LogExplorerLocators; } export interface LogExplorerPluginStart { LogExplorer: ComponentType; + createLogExplorerController: CreateLogExplorerController; } export interface LogExplorerSetupDeps { @@ -30,4 +34,6 @@ export interface LogExplorerStartDeps { dataViews: DataViewsPublicPluginStart; discover: DiscoverStart; fieldFormats: FieldFormatsStart; + navigation: NavigationPublicPluginStart; + unifiedSearch: UnifiedSearchPublicPluginStart; } diff --git a/x-pack/plugins/log_explorer/public/utils/convert_discover_app_state.ts b/x-pack/plugins/log_explorer/public/utils/convert_discover_app_state.ts new file mode 100644 index 0000000000000..90d51f75e8c7c --- /dev/null +++ b/x-pack/plugins/log_explorer/public/utils/convert_discover_app_state.ts @@ -0,0 +1,133 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { QueryState } from '@kbn/data-plugin/public'; +import { DiscoverAppState } from '@kbn/discover-plugin/public'; +import { ExistsFilter, Filter, FILTERS, PhrasesFilter } from '@kbn/es-query'; +import { PhraseFilterValue } from '@kbn/es-query/src/filters/build_filters'; +import { cloneDeep } from 'lodash'; +import { + ChartDisplayOptions, + DisplayOptions, + GridColumnDisplayOptions, + GridRowsDisplayOptions, +} from '../../common'; +import { ControlOptions, OptionsListControlOption } from '../controller'; + +export const getGridColumnDisplayOptionsFromDiscoverAppState = ( + discoverAppState: DiscoverAppState +): GridColumnDisplayOptions[] | undefined => + discoverAppState.columns?.map((field) => ({ + field, + width: discoverAppState.grid?.columns?.[field]?.width, + })); + +export const getGridRowsDisplayOptionsFromDiscoverAppState = ( + discoverAppState: DiscoverAppState +): Partial => ({ + ...(discoverAppState.rowHeight != null ? { rowHeight: discoverAppState.rowHeight } : {}), + ...(discoverAppState.rowsPerPage != null ? { rowsPerPage: discoverAppState.rowsPerPage } : {}), +}); + +export const getChartDisplayOptionsFromDiscoverAppState = ( + discoverAppState: DiscoverAppState +): Partial => ({ + breakdownField: discoverAppState.breakdownField ?? null, +}); + +export const getQueryStateFromDiscoverAppState = ( + discoverAppState: DiscoverAppState +): QueryState => ({ + query: discoverAppState.query, + filters: discoverAppState.filters, +}); + +export const getDiscoverAppStateFromContext = ( + displayOptions: DisplayOptions & QueryState +): Partial => ({ + breakdownField: displayOptions.chart.breakdownField ?? undefined, + columns: getDiscoverColumnsFromDisplayOptions(displayOptions), + grid: getDiscoverGridFromDisplayOptions(displayOptions), + rowHeight: displayOptions.grid.rows.rowHeight, + rowsPerPage: displayOptions.grid.rows.rowsPerPage, + query: cloneDeep(displayOptions.query), + filters: cloneDeep(displayOptions.filters), +}); + +export const getDiscoverColumnsFromDisplayOptions = ( + displayOptions: DisplayOptions +): DiscoverAppState['columns'] => displayOptions.grid.columns.map(({ field }) => field); + +export const getDiscoverGridFromDisplayOptions = ( + displayOptions: DisplayOptions +): DiscoverAppState['grid'] => ({ + columns: displayOptions.grid.columns.reduce< + NonNullable['columns']> + >((gridColumns, { field, width }) => { + if (width != null) { + gridColumns[field] = { width }; + } + return gridColumns; + }, {}), +}); + +const createDiscoverPhrasesFilter = ({ + key, + values, + negate, +}: { + values: PhraseFilterValue[]; + key: string; + negate?: boolean; +}): PhrasesFilter => + ({ + meta: { + key, + negate, + type: FILTERS.PHRASES, + params: values, + }, + query: { + bool: { + should: values.map((value) => ({ match_phrase: { [key]: value.toString() } })), + minimum_should_match: 1, + }, + }, + } as PhrasesFilter); + +const createDiscoverExistsFilter = ({ + key, + negate, +}: { + key: string; + negate?: boolean; +}): ExistsFilter => ({ + meta: { + key, + negate, + type: FILTERS.EXISTS, + }, + query: { exists: { field: key } }, +}); + +export const getDiscoverFiltersFromState = (filters: Filter[] = [], controls?: ControlOptions) => [ + ...filters, + ...(controls + ? (Object.keys(controls) as Array).map((key) => + controls[key as keyof ControlOptions]?.selection.type === 'exists' + ? createDiscoverExistsFilter({ + key, + negate: controls[key]?.mode === 'exclude', + }) + : createDiscoverPhrasesFilter({ + key, + values: (controls[key]?.selection as OptionsListControlOption).selectedOptions, + negate: controls[key]?.mode === 'exclude', + }) + ) + : []), +]; diff --git a/x-pack/plugins/log_explorer/tsconfig.json b/x-pack/plugins/log_explorer/tsconfig.json index a36d764cbee56..05cdd888eba1f 100644 --- a/x-pack/plugins/log_explorer/tsconfig.json +++ b/x-pack/plugins/log_explorer/tsconfig.json @@ -3,31 +3,47 @@ "compilerOptions": { "outDir": "target/types" }, - "include": ["../../../typings/**/*", "common/**/*", "public/**/*", "server/**/*", ".storybook/**/*.tsx"], + "include": [ + "../../../typings/**/*", + "common/**/*", + "public/**/*", + "server/**/*", + ".storybook/**/*.tsx" + ], "kbn_references": [ + "@kbn/controls-plugin", "@kbn/core", + "@kbn/core-application-browser", + "@kbn/core-http-browser", + "@kbn/core-ui-settings-browser", + "@kbn/custom-icons", + "@kbn/data-plugin", + "@kbn/data-views-plugin", + "@kbn/deeplinks-observability", "@kbn/discover-plugin", + "@kbn/discover-utils", + "@kbn/ecs", + "@kbn/elastic-agent-utils", + "@kbn/embeddable-plugin", + "@kbn/es-query", + "@kbn/field-formats-plugin", + "@kbn/fleet-plugin", "@kbn/i18n", "@kbn/i18n-react", - "@kbn/fleet-plugin", "@kbn/io-ts-utils", - "@kbn/data-views-plugin", - "@kbn/rison", - "@kbn/controls-plugin", - "@kbn/embeddable-plugin", - "@kbn/es-query", "@kbn/kibana-react-plugin", - "@kbn/data-plugin", - "@kbn/unified-field-list", - "@kbn/core-application-browser", + "@kbn/kibana-utils-plugin", + "@kbn/navigation-plugin", + "@kbn/react-field", + "@kbn/router-utils", "@kbn/share-plugin", "@kbn/unified-data-table", - "@kbn/core-ui-settings-browser", - "@kbn/discover-utils", - "@kbn/deeplinks-observability", - "@kbn/field-formats-plugin", - "@kbn/custom-icons", - "@kbn/elastic-agent-utils" + "@kbn/unified-doc-viewer", + "@kbn/unified-field-list", + "@kbn/unified-search-plugin", + "@kbn/xstate-utils", ], - "exclude": ["target/**/*"] + "exclude": [ + "target/**/*" + ] } diff --git a/x-pack/plugins/logs_shared/public/components/log_ai_assistant/index.tsx b/x-pack/plugins/logs_shared/public/components/log_ai_assistant/index.tsx index 0bfb9f516209c..78b696388e165 100644 --- a/x-pack/plugins/logs_shared/public/components/log_ai_assistant/index.tsx +++ b/x-pack/plugins/logs_shared/public/components/log_ai_assistant/index.tsx @@ -4,25 +4,18 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { ComponentType } from 'react'; -import { Optional } from '@kbn/utility-types'; +import { ObservabilityAIAssistantPluginStart } from '@kbn/observability-ai-assistant-plugin/public'; +import React from 'react'; import { dynamic } from '../../../common/dynamic'; -import type { LogAIAssistantDeps } from './log_ai_assistant'; export const LogAIAssistant = dynamic(() => import('./log_ai_assistant')); -interface LogAIAssistantFactoryDeps { - observabilityAIAssistant: LogAIAssistantDeps['observabilityAIAssistant']; -} - -export type LogAIAssistantComponent = ComponentType< - Optional ->; - export function createLogAIAssistant({ - observabilityAIAssistant: aiAssistantService, -}: LogAIAssistantFactoryDeps): LogAIAssistantComponent { - return ({ observabilityAIAssistant = aiAssistantService, ...props }) => ( + observabilityAIAssistant, +}: { + observabilityAIAssistant: ObservabilityAIAssistantPluginStart; +}) { + return ({ ...props }) => ( ); } diff --git a/x-pack/plugins/logs_shared/public/components/log_ai_assistant/log_ai_assistant.tsx b/x-pack/plugins/logs_shared/public/components/log_ai_assistant/log_ai_assistant.tsx index 8a8c755d70ff7..79adfde85540e 100644 --- a/x-pack/plugins/logs_shared/public/components/log_ai_assistant/log_ai_assistant.tsx +++ b/x-pack/plugins/logs_shared/public/components/log_ai_assistant/log_ai_assistant.tsx @@ -8,12 +8,9 @@ import React, { useMemo } from 'react'; import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { - ContextualInsight, type Message, - ObservabilityAIAssistantPluginStart, MessageRole, - ObservabilityAIAssistantProvider, - useObservabilityAIAssistant, + type ObservabilityAIAssistantPluginStart, } from '@kbn/observability-ai-assistant-plugin/public'; import { LogEntryField } from '../../../common'; import { explainLogMessageTitle, similarLogMessagesTitle } from './translations'; @@ -23,16 +20,14 @@ export interface LogAIAssistantDocument { } export interface LogAIAssistantProps { + observabilityAIAssistant: ObservabilityAIAssistantPluginStart; doc: LogAIAssistantDocument | undefined; } -export interface LogAIAssistantDeps extends LogAIAssistantProps { - observabilityAIAssistant: ObservabilityAIAssistantPluginStart['service']; -} - -export const LogAIAssistant = withProviders(({ doc }: LogAIAssistantProps) => { - const aiAssistant = useObservabilityAIAssistant(); - +export const LogAIAssistant = ({ + doc, + observabilityAIAssistant: { ObservabilityAIAssistantContextualInsight }, +}: LogAIAssistantProps) => { const explainLogMessageMessages = useMemo(() => { if (!doc) { return undefined; @@ -75,18 +70,18 @@ export const LogAIAssistant = withProviders(({ doc }: LogAIAssistantProps) => { return ( - {aiAssistant.isEnabled() && explainLogMessageMessages ? ( + {ObservabilityAIAssistantContextualInsight && explainLogMessageMessages ? ( - ) : null} - {aiAssistant.isEnabled() && similarLogMessageMessages ? ( + {ObservabilityAIAssistantContextualInsight && similarLogMessageMessages ? ( - { ) : null} ); -}); +}; // eslint-disable-next-line import/no-default-export export default LogAIAssistant; - -function withProviders(Component: React.FunctionComponent) { - return function ComponentWithProviders({ - observabilityAIAssistant: observabilityAIAssistantService, - ...props - }: LogAIAssistantDeps) { - return ( - - - - ); - }; -} diff --git a/x-pack/plugins/logs_shared/public/components/logging/log_entry_flyout/log_entry_flyout.tsx b/x-pack/plugins/logs_shared/public/components/logging/log_entry_flyout/log_entry_flyout.tsx index 2c5913d282e1d..aca2638a6120c 100644 --- a/x-pack/plugins/logs_shared/public/components/logging/log_entry_flyout/log_entry_flyout.tsx +++ b/x-pack/plugins/logs_shared/public/components/logging/log_entry_flyout/log_entry_flyout.tsx @@ -100,12 +100,6 @@ export const LogEntryFlyout = ({ onSetFieldFilter, logViewReference, }: LogEntryFlyoutProps) => { - const { - services: { - observabilityAIAssistant: { service: observabilityAIAssistantService }, - }, - } = useKibanaContextForPlugin(); - const { cancelRequest: cancelLogEntryRequest, errors: logEntryErrors, @@ -119,6 +113,8 @@ export const LogEntryFlyout = ({ logEntryId, }); + const { observabilityAIAssistant } = useKibanaContextForPlugin().services; + useEffect(() => { if (logViewReference && logEntryId) { fetchLogEntry(); @@ -186,10 +182,7 @@ export const LogEntryFlyout = ({ > - + diff --git a/x-pack/plugins/logs_shared/public/plugin.ts b/x-pack/plugins/logs_shared/public/plugin.ts index 372ca2c124bcc..092e95570db7f 100644 --- a/x-pack/plugins/logs_shared/public/plugin.ts +++ b/x-pack/plugins/logs_shared/public/plugin.ts @@ -33,9 +33,7 @@ export class LogsSharedPlugin implements LogsSharedClientPluginClass { search: data.search, }); - const LogAIAssistant = createLogAIAssistant({ - observabilityAIAssistant: observabilityAIAssistant.service, - }); + const LogAIAssistant = createLogAIAssistant({ observabilityAIAssistant }); return { logViews, diff --git a/x-pack/plugins/logs_shared/public/types.ts b/x-pack/plugins/logs_shared/public/types.ts index c0379c6fc21fb..2a2e9c1cf742d 100644 --- a/x-pack/plugins/logs_shared/public/types.ts +++ b/x-pack/plugins/logs_shared/public/types.ts @@ -17,7 +17,7 @@ import type { DataPublicPluginStart } from '@kbn/data-plugin/public'; import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public'; import { ObservabilityAIAssistantPluginStart } from '@kbn/observability-ai-assistant-plugin/public'; import { UiActionsStart } from '@kbn/ui-actions-plugin/public'; -import { LogAIAssistantComponent } from './components/log_ai_assistant'; +import type { LogAIAssistantProps } from './components/log_ai_assistant/log_ai_assistant'; // import type { OsqueryPluginStart } from '../../osquery/public'; import { LogViewsServiceSetup, LogViewsServiceStart } from './services/log_views'; @@ -28,7 +28,7 @@ export interface LogsSharedClientSetupExports { export interface LogsSharedClientStartExports { logViews: LogViewsServiceStart; - LogAIAssistant: LogAIAssistantComponent; + LogAIAssistant: (props: Omit) => JSX.Element; } // eslint-disable-next-line @typescript-eslint/no-empty-interface diff --git a/x-pack/plugins/maps/public/actions/layer_actions.ts b/x-pack/plugins/maps/public/actions/layer_actions.ts index 4202032964e86..e24da30482d66 100644 --- a/x-pack/plugins/maps/public/actions/layer_actions.ts +++ b/x-pack/plugins/maps/public/actions/layer_actions.ts @@ -826,6 +826,14 @@ export function setTileState( newValue: tileErrors, }); + if (!isLayerGroup(layer) && layer.getSource().isESSource()) { + getInspectorAdapters(getState()).vectorTiles.setTileResults( + layerId, + tileMetaFeatures, + tileErrors + ); + } + if (!tileMetaFeatures && !layer.getDescriptor().__tileMetaFeatures) { return; } diff --git a/x-pack/plugins/maps/public/classes/layers/layer.tsx b/x-pack/plugins/maps/public/classes/layers/layer.tsx index 70cdc8ace4cf1..1031faaf06963 100644 --- a/x-pack/plugins/maps/public/classes/layers/layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/layer.tsx @@ -9,6 +9,7 @@ import { i18n } from '@kbn/i18n'; import type { Map as MbMap } from '@kbn/mapbox-gl'; +import { Adapters } from '@kbn/inspector-plugin/common/adapters'; import type { Query } from '@kbn/es-query'; import { getWarningsTitle, @@ -46,6 +47,7 @@ import { IStyle } from '../styles/style'; import { LICENSED_FEATURES } from '../../licensed_features'; import { IESSource } from '../sources/es_source'; import { TileErrorsList } from './tile_errors_list'; +import { isLayerGroup } from './layer_group'; export const INCOMPLETE_RESULTS_WARNING = i18n.translate( 'xpack.maps.layer.incompleteResultsWarning', @@ -90,7 +92,7 @@ export interface ILayer { isLayerLoading(zoom: number): boolean; isFilteredByGlobalTime(): Promise; hasErrors(): boolean; - getErrors(): LayerMessage[]; + getErrors(inspectorAdapters: Adapters): LayerMessage[]; hasWarnings(): boolean; getWarnings(): LayerMessage[]; @@ -411,7 +413,8 @@ export class AbstractLayer implements ILayer { } hasErrors(): boolean { - return this.getErrors().length > 0; + const inspectorAdapters = {}; // errors are not interacted with so empty Adapters can be passed to getErrors + return this.getErrors(inspectorAdapters).length > 0; } _getSourceErrorTitle() { @@ -420,7 +423,7 @@ export class AbstractLayer implements ILayer { }); } - getErrors(): LayerMessage[] { + getErrors(inspectorAdapters: Adapters): LayerMessage[] { const errors: LayerMessage[] = []; const sourceError = this.getSourceDataRequest()?.renderError(); @@ -436,7 +439,14 @@ export class AbstractLayer implements ILayer { title: i18n.translate('xpack.maps.layer.tileErrorTitle', { defaultMessage: `An error occurred when loading layer tiles`, }), - body: , + body: ( + + ), }); } diff --git a/x-pack/plugins/maps/public/classes/layers/tile_errors_list.tsx b/x-pack/plugins/maps/public/classes/layers/tile_errors_list.tsx index 40a427dd29837..38bb078cd98d0 100644 --- a/x-pack/plugins/maps/public/classes/layers/tile_errors_list.tsx +++ b/x-pack/plugins/maps/public/classes/layers/tile_errors_list.tsx @@ -6,11 +6,17 @@ */ import React, { useEffect, useState } from 'react'; +import { Adapters } from '@kbn/inspector-plugin/common/adapters'; import { i18n } from '@kbn/i18n'; -import { EuiButtonEmpty, EuiContextMenu, EuiPopover } from '@elastic/eui'; +import { EuiButton, EuiButtonEmpty, EuiCodeBlock, EuiContextMenu, EuiPopover } from '@elastic/eui'; import type { TileError } from '../../../common/descriptor_types'; +import { getInspector } from '../../kibana_services'; +import { RESPONSE_VIEW_ID } from '../../inspector/vector_tile_adapter/components/vector_tile_inspector'; interface Props { + inspectorAdapters: Adapters; + isESSource: boolean; + layerId: string; tileErrors: TileError[]; } @@ -75,7 +81,28 @@ export function TileErrorsList(props: Props) { > -

{getDescription(selectedTileError)}

+ + {getDescription(selectedTileError)} + + {props.isESSource && ( + { + getInspector().open(props.inspectorAdapters, { + options: { + initialLayerId: props.layerId, + initialTileKey: selectedTileError?.tileKey, + initialTab: [RESPONSE_VIEW_ID], + }, + }); + }} + size="s" + > + {i18n.translate('xpack.maps.tileError.viewDetailsButtonLabel', { + defaultMessage: 'View details', + })} + + )} ); } diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/geojson_vector_layer.tsx b/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/geojson_vector_layer.tsx index dfcdeecb42430..4d41807f511d0 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/geojson_vector_layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/geojson_vector_layer/geojson_vector_layer.tsx @@ -7,6 +7,7 @@ import _ from 'lodash'; import React from 'react'; +import { Adapters } from '@kbn/inspector-plugin/common/adapters'; import { i18n } from '@kbn/i18n'; import { EuiIcon } from '@elastic/eui'; import { Feature, FeatureCollection } from 'geojson'; @@ -157,8 +158,8 @@ export class GeoJsonVectorLayer extends AbstractVectorLayer { ); } - getErrors(): LayerMessage[] { - const errors = super.getErrors(); + getErrors(inspectorAdapters: Adapters): LayerMessage[] { + const errors = super.getErrors(inspectorAdapters); this.getValidJoins().forEach((join) => { const joinDescriptor = join.toDescriptor(); diff --git a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx index 0d60b2dc1b6bd..fa246f899639c 100644 --- a/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx +++ b/x-pack/plugins/maps/public/classes/layers/vector_layer/vector_layer.tsx @@ -7,6 +7,7 @@ import React from 'react'; import { v4 as uuidv4 } from 'uuid'; +import { Adapters } from '@kbn/inspector-plugin/common/adapters'; import { asyncForEach } from '@kbn/std'; import type { FilterSpecification, Map as MbMap, LayerSpecification } from '@kbn/mapbox-gl'; import type { KibanaExecutionContext } from '@kbn/core/public'; @@ -274,8 +275,8 @@ export class AbstractVectorLayer extends AbstractLayer implements IVectorLayer { }); } - getErrors(): LayerMessage[] { - const errors = super.getErrors(); + getErrors(inspectorAdapters: Adapters): LayerMessage[] { + const errors = super.getErrors(inspectorAdapters); this.getValidJoins().forEach((join) => { const joinDataRequest = this.getDataRequest(join.getSourceDataRequestId()); diff --git a/x-pack/plugins/maps/public/classes/sources/source_registry.ts b/x-pack/plugins/maps/public/classes/sources/source_registry.ts index f1dd02148cd19..970e5a9e17fe2 100644 --- a/x-pack/plugins/maps/public/classes/sources/source_registry.ts +++ b/x-pack/plugins/maps/public/classes/sources/source_registry.ts @@ -7,13 +7,11 @@ /* eslint-disable @typescript-eslint/consistent-type-definitions */ -import { Adapters } from '@kbn/inspector-plugin/common/adapters'; import type { ISource } from './source'; export type SourceRegistryEntry = { ConstructorFunction: new ( - sourceDescriptor: any, // this is the source-descriptor that corresponds specifically to the particular ISource instance - inspectorAdapters?: Adapters + sourceDescriptor: any // this is the source-descriptor that corresponds specifically to the particular ISource instance ) => ISource; type: string; }; diff --git a/x-pack/plugins/maps/public/classes/util/data_request.tsx b/x-pack/plugins/maps/public/classes/util/data_request.tsx index 3cb493f138441..62fd17cc44277 100644 --- a/x-pack/plugins/maps/public/classes/util/data_request.tsx +++ b/x-pack/plugins/maps/public/classes/util/data_request.tsx @@ -8,7 +8,7 @@ /* eslint-disable max-classes-per-file */ import React, { ReactNode } from 'react'; -import { getSearchErrorOverrideDisplay } from '@kbn/data-plugin/public'; +import { renderSearchError } from '@kbn/search-errors'; import { getApplication } from '../../kibana_services'; import type { DataRequestDescriptor, DataRequestMeta } from '../../../common/descriptor_types'; @@ -60,21 +60,21 @@ export class DataRequest { return null; } - const overrideDisplay = getSearchErrorOverrideDisplay({ + const searchErrorDisplay = renderSearchError({ error: this._descriptor.error, application: getApplication(), }); - const body = overrideDisplay?.body ? ( - overrideDisplay.body + const body = searchErrorDisplay?.body ? ( + searchErrorDisplay.body ) : (

{this._descriptor.error.message}

); - return overrideDisplay?.actions ? ( + return searchErrorDisplay?.actions ? ( <> {body} - {overrideDisplay.actions} + {searchErrorDisplay.actions} ) : ( body diff --git a/x-pack/plugins/maps/public/classes/util/geo_tile_utils.ts b/x-pack/plugins/maps/public/classes/util/geo_tile_utils.ts index 32343e7275841..5ed8bc64f600f 100644 --- a/x-pack/plugins/maps/public/classes/util/geo_tile_utils.ts +++ b/x-pack/plugins/maps/public/classes/util/geo_tile_utils.ts @@ -174,3 +174,13 @@ export function expandToTileBoundaries(extent: MapExtent, zoom: number): MapExte maxLat: tileToLatitude(upperLeftY, tileCount), }; } + +export function isPointInTile(lat: number, lon: number, x: number, y: number, z: number) { + const tileCount = getTileCount(z); + const lonX = longitudeToTile(lon, tileCount); + if (lonX !== x) { + return false; + } + const latY = latitudeToTile(lat, tileCount); + return latY === y; +} diff --git a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/__snapshots__/toc_entry.test.tsx.snap b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/__snapshots__/toc_entry.test.tsx.snap index f770aeed87494..63edb4bff381c 100644 --- a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/__snapshots__/toc_entry.test.tsx.snap +++ b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/__snapshots__/toc_entry.test.tsx.snap @@ -479,6 +479,7 @@ exports[`TOCEntry props should display layer details when isLegendDetailsOpen is data-test-subj="mapLayerTOCDetailslayer_1" > { } as unknown as ILayer; test('Should only render errors when layer contains errors', () => { - render(); + render(); screen.getByTestId('layer-error'); const error = screen.queryByTestId('layer-error'); expect(error).not.toBeNull(); @@ -47,6 +47,7 @@ describe('LegendDetails', () => { test('Should render warnings and legend when layer contains warnings', () => { render( { diff --git a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/legend_details.tsx b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/legend_details.tsx index 1561d47aa4945..3f9e5c8f11627 100644 --- a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/legend_details.tsx +++ b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/legend_details.tsx @@ -6,15 +6,17 @@ */ import React from 'react'; +import { Adapters } from '@kbn/inspector-plugin/common/adapters'; import { EuiCallOut, EuiSpacer } from '@elastic/eui'; import type { ILayer } from '../../../../../classes/layers/layer'; interface Props { + inspectorAdapters: Adapters; layer: ILayer; } -export function LegendDetails({ layer }: Props) { - const errors = layer.getErrors(); +export function LegendDetails({ inspectorAdapters, layer }: Props) { + const errors = layer.getErrors(inspectorAdapters); if (errors.length) { return ( <> diff --git a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry.test.tsx b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry.test.tsx index 76fe008efbc73..81c7933fe4f72 100644 --- a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry.test.tsx +++ b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry.test.tsx @@ -63,6 +63,7 @@ const mockLayer = { const defaultProps = { depth: 0, + inspectorAdapters: {}, layer: mockLayer, selectedLayer: undefined, openLayerPanel: async () => {}, diff --git a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry.tsx b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry.tsx index 488f9a64083db..1798d063d4243 100644 --- a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry.tsx +++ b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry.tsx @@ -8,6 +8,7 @@ import React, { Component } from 'react'; import classNames from 'classnames'; import type { DraggableProvidedDragHandleProps } from '@hello-pangea/dnd'; +import { Adapters } from '@kbn/inspector-plugin/common/adapters'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiIcon, EuiButtonIcon, EuiConfirmModal, EuiButtonEmpty } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -27,6 +28,7 @@ function escapeLayerName(name: string) { } export interface ReduxStateProps { + inspectorAdapters: Adapters; isReadOnly: boolean; zoom: number; selectedLayer: ILayer | undefined; @@ -337,7 +339,10 @@ export class TOCEntry extends Component { className="mapTocEntry__layerDetails" data-test-subj={`mapLayerTOCDetails${escapeLayerName(this.state.displayName)}`} > - +
) : null} diff --git a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/__snapshots__/toc_entry_actions_popover.test.tsx.snap b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/__snapshots__/toc_entry_actions_popover.test.tsx.snap index dfa4200c8508e..47171e8bbe871 100644 --- a/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/__snapshots__/toc_entry_actions_popover.test.tsx.snap +++ b/x-pack/plugins/maps/public/connected_components/right_side_controls/layer_control/layer_toc/toc_entry/toc_entry_actions_popover/__snapshots__/toc_entry_actions_popover.test.tsx.snap @@ -3,7 +3,6 @@ exports[`TOCEntryActionsPopover is rendered 1`] = ` { closePopover={this._closePopover} panelPaddingSize="none" anchorPosition="leftUp" - anchorClassName="mapLayTocActions__popoverAnchor" > { footnotes: Footnote[]; postScript?: string; } { - const errors = this.props.layer.getErrors(); + const errors = this.props.layer.getErrors(this.props.inspectorAdapters); if (errors.length) { const errorIcon = ( { : { icon: errorIcon, tooltipContent: this.props.layer - .getErrors() + .getErrors(this.props.inspectorAdapters) .map(({ title }) =>
{title}
), footnotes: [], }; diff --git a/x-pack/plugins/maps/public/inspector/vector_tile_adapter/components/vector_tile_inspector.test.tsx b/x-pack/plugins/maps/public/inspector/vector_tile_adapter/components/vector_tile_inspector.test.tsx new file mode 100644 index 0000000000000..6d3e78bbe2aaf --- /dev/null +++ b/x-pack/plugins/maps/public/inspector/vector_tile_adapter/components/vector_tile_inspector.test.tsx @@ -0,0 +1,83 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +jest.mock('./tile_request_tab', () => ({ + TileRequestTab: () => { + return
mockTileRequestTab
; + }, +})); + +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import VectorTileInspector, { RESPONSE_VIEW_ID } from './vector_tile_inspector'; +import { VectorTileAdapter } from '../vector_tile_adapter'; + +describe('VectorTileInspector', () => { + let vectorTileAdapter: VectorTileAdapter | undefined; + beforeEach(() => { + vectorTileAdapter = new VectorTileAdapter(); + vectorTileAdapter.addLayer( + 'layer1', + 'layer1 label', + '/pof/internal/maps/mvt/getGridTile/{z}/{x}/{y}.pbf?geometryFieldName=geo.coordinates&hasLabels=false&buffer=7&index=kibana_sample_data_logs&gridPrecision=8&requestBody=()&renderAs=heatmap&token=1' + ); + vectorTileAdapter.addLayer( + 'layer2', + 'layer2 label', + '/pof/internal/maps/mvt/getGridTile/{z}/{x}/{y}.pbf?geometryFieldName=geo.coordinates&hasLabels=false&buffer=7&index=kibana_sample_data_logs&gridPrecision=8&requestBody=()&renderAs=heatmap&token=1' + ); + vectorTileAdapter.setTiles([ + { + x: 0, + y: 0, + z: 1, + }, + { + x: 1, + y: 0, + z: 1, + }, + { + x: 0, + y: 1, + z: 1, + }, + { + x: 1, + y: 1, + z: 1, + }, + ]); + }); + + test('should show first layer, first tile, and request tab when options not provided', () => { + render( + + ); + screen.getByText('layer1 label'); + screen.getByText('1/0/0'); + screen.getByText('mockTileRequestTab'); + }); + + test('should show layer, tile, and tab specified by options', () => { + const options = { + initialLayerId: 'layer2', + initialTileKey: '1/1/1', + initialTab: RESPONSE_VIEW_ID, + }; + render( + + ); + screen.getByText('layer2 label'); + screen.getByText('1/1/1'); + screen.getByText('Not available'); // response is not available because tileMetaFeature or tileError where not provided + }); +}); diff --git a/x-pack/plugins/maps/public/inspector/vector_tile_adapter/components/vector_tile_inspector.tsx b/x-pack/plugins/maps/public/inspector/vector_tile_adapter/components/vector_tile_inspector.tsx index 9a9356ad1a6d2..32f16f0f18346 100644 --- a/x-pack/plugins/maps/public/inspector/vector_tile_adapter/components/vector_tile_inspector.tsx +++ b/x-pack/plugins/maps/public/inspector/vector_tile_adapter/components/vector_tile_inspector.tsx @@ -8,33 +8,56 @@ import _ from 'lodash'; import React, { Component } from 'react'; import { i18n } from '@kbn/i18n'; -import type { Adapters } from '@kbn/inspector-plugin/public'; -import { EuiComboBox, EuiComboBoxOptionOption, EuiSpacer, EuiTabs, EuiTab } from '@elastic/eui'; +import type { InspectorViewProps } from '@kbn/inspector-plugin/public'; +import { XJsonLang } from '@kbn/monaco'; +import { + EuiComboBox, + EuiComboBoxOptionOption, + EuiFormRow, + EuiSpacer, + EuiTabs, + EuiTab, + EuiText, +} from '@elastic/eui'; +import { CodeEditor } from '@kbn/kibana-react-plugin/public'; import { EmptyPrompt } from './empty_prompt'; import type { TileRequest } from '../types'; import { TileRequestTab } from './tile_request_tab'; import { RequestsViewCallout } from './requests_view_callout'; -interface Props { - adapters: Adapters; +const REQUEST_VIEW_ID = 'request_view'; +export const RESPONSE_VIEW_ID = 'response_view'; + +interface Options { + initialLayerId?: string; + initialTileKey?: string; + initialTab?: typeof REQUEST_VIEW_ID | typeof RESPONSE_VIEW_ID; } interface State { selectedLayer: EuiComboBoxOptionOption | null; selectedTileRequest: TileRequest | null; + selectedView: typeof REQUEST_VIEW_ID | typeof RESPONSE_VIEW_ID; tileRequests: TileRequest[]; layerOptions: Array>; } -class VectorTileInspector extends Component { +class VectorTileInspector extends Component { private _isMounted = false; - state: State = { - selectedLayer: null, - selectedTileRequest: null, - tileRequests: [], - layerOptions: [], - }; + constructor(props: InspectorViewProps) { + super(props); + this.state = { + selectedLayer: null, + selectedTileRequest: null, + selectedView: + props.options && (props.options as Options).initialTab + ? (props.options as Options).initialTab! + : REQUEST_VIEW_ID, + tileRequests: [], + layerOptions: [], + }; + } componentDidMount() { this._isMounted = true; @@ -47,6 +70,62 @@ class VectorTileInspector extends Component { this.props.adapters.vectorTiles.removeListener('change', this._debouncedOnAdapterChange); } + _getDefaultLayer(layerOptions: Array>) { + if ( + this.state.selectedLayer && + layerOptions.some((layerOption) => { + return this.state.selectedLayer?.value === layerOption.value; + }) + ) { + return this.state.selectedLayer; + } + + if (this.props.options && (this.props.options as Options).initialLayerId) { + const initialLayer = layerOptions.find((layerOption) => { + return (this.props.options as Options).initialLayerId === layerOption.value; + }); + if (initialLayer) { + return initialLayer; + } + } + + return layerOptions[0]; + } + + _getDefaultTileRequest(tileRequests: TileRequest[]) { + if ( + this.state.selectedTileRequest && + tileRequests.some((tileRequest: TileRequest) => { + return ( + this.state.selectedTileRequest?.layerId === tileRequest.layerId && + this.state.selectedTileRequest?.x === tileRequest.x && + this.state.selectedTileRequest?.y === tileRequest.y && + this.state.selectedTileRequest?.z === tileRequest.z + ); + }) + ) { + return this.state.selectedTileRequest; + } + + if (tileRequests.length === 0) { + return null; + } + + if (this.props.options && (this.props.options as Options).initialTileKey) { + const initialTileRequest = tileRequests.find((tileRequest) => { + return ( + (this.props.options as Options).initialTileKey === + `${tileRequest.z}/${tileRequest.x}/${tileRequest.y}` + ); + }); + if (initialTileRequest) { + return initialTileRequest; + } + } + + return tileRequests[0]; + } + _onAdapterChange = () => { const layerOptions = this.props.adapters.vectorTiles.getLayerOptions() as Array< EuiComboBoxOptionOption @@ -61,32 +140,11 @@ class VectorTileInspector extends Component { return; } - const selectedLayer = - this.state.selectedLayer && - layerOptions.some((layerOption) => { - return this.state.selectedLayer?.value === layerOption.value; - }) - ? this.state.selectedLayer - : layerOptions[0]; + const selectedLayer = this._getDefaultLayer(layerOptions); const tileRequests = this.props.adapters.vectorTiles.getTileRequests(selectedLayer.value); - const selectedTileRequest = - this.state.selectedTileRequest && - tileRequests.some((tileRequest: TileRequest) => { - return ( - this.state.selectedTileRequest?.layerId === tileRequest.layerId && - this.state.selectedTileRequest?.x === tileRequest.x && - this.state.selectedTileRequest?.y === tileRequest.y && - this.state.selectedTileRequest?.z === tileRequest.z - ); - }) - ? this.state.selectedTileRequest - : tileRequests.length - ? tileRequests[0] - : null; - this.setState({ selectedLayer, - selectedTileRequest, + selectedTileRequest: this._getDefaultTileRequest(tileRequests), tileRequests, layerOptions, }); @@ -117,26 +175,60 @@ class VectorTileInspector extends Component { }); }; - renderTabs() { - return this.state.tileRequests.map((tileRequest) => { - const tileLabel = `${tileRequest.z}/${tileRequest.x}/${tileRequest.y}`; + _onTileSelect = (selectedOptions: Array>) => { + if (selectedOptions.length === 0) { + this.setState({ selectedTileRequest: null }); + return; + } + + this.setState({ + selectedTileRequest: selectedOptions[0].value ? selectedOptions[0].value : null, + }); + }; + + _renderTileRequest() { + if (!this.state.selectedTileRequest) { + return null; + } + + if (this.state.selectedView === REQUEST_VIEW_ID) { return ( - { - this.setState({ selectedTileRequest: tileRequest }); - }} - isSelected={ - tileRequest.layerId === this.state.selectedTileRequest?.layerId && - tileRequest.x === this.state.selectedTileRequest?.x && - tileRequest.y === this.state.selectedTileRequest?.y && - tileRequest.z === this.state.selectedTileRequest?.z - } - > - {tileLabel} - + ); - }); + } + + const tileResponse = getTileResponse(this.state.selectedTileRequest); + + return tileResponse ? ( + + ) : ( + +

+ {i18n.translate('xpack.maps.inspector.vectorTile.tileMetaFeatureNotAvailable', { + defaultMessage: 'Not available', + })} +

+
+ ); } render() { @@ -148,31 +240,102 @@ class VectorTileInspector extends Component { ) : ( <> + - - - {this.renderTabs()} - - {this.state.selectedTileRequest && ( - + - )} + + + + { + return { + label: `${tileRequest.z}/${tileRequest.x}/${tileRequest.y}`, + value: tileRequest, + }; + })} + selectedOptions={ + this.state.selectedTileRequest + ? [ + { + label: `${this.state.selectedTileRequest.z}/${this.state.selectedTileRequest.x}/${this.state.selectedTileRequest.y}`, + value: this.state.selectedTileRequest, + }, + ] + : [] + } + onChange={this._onTileSelect} + isClearable={false} + /> + + + + <> + { + this.setState({ selectedView: REQUEST_VIEW_ID }); + }} + isSelected={this.state.selectedView === REQUEST_VIEW_ID} + > + {i18n.translate('xpack.maps.inspector.vectorTile.requestTabLabel', { + defaultMessage: 'Request', + })} + + { + this.setState({ selectedView: RESPONSE_VIEW_ID }); + }} + isSelected={this.state.selectedView === RESPONSE_VIEW_ID} + > + {i18n.translate('xpack.maps.inspector.vectorTile.responseTabLabel', { + defaultMessage: 'Response', + })} + + + + + {this._renderTileRequest()} ); } } +function getTileResponse(tileRequest: TileRequest) { + if (tileRequest.tileError) { + return { + error: tileRequest.tileError.error + ? tileRequest.tileError.error + : tileRequest.tileError.message, + }; + } + + return tileRequest.tileMetaFeature + ? { + meta: tileRequest.tileMetaFeature.properties, + } + : undefined; +} + // default export required for React.Lazy // eslint-disable-next-line import/no-default-export export default VectorTileInspector; diff --git a/x-pack/plugins/maps/public/inspector/vector_tile_adapter/types.ts b/x-pack/plugins/maps/public/inspector/vector_tile_adapter/types.ts index 5fead09165fad..1d25a52bfb6ad 100644 --- a/x-pack/plugins/maps/public/inspector/vector_tile_adapter/types.ts +++ b/x-pack/plugins/maps/public/inspector/vector_tile_adapter/types.ts @@ -5,9 +5,13 @@ * 2.0. */ +import type { TileError, TileMetaFeature } from '../../../common/descriptor_types'; + export interface TileRequest { layerId: string; tileUrl: string; + tileError?: TileError; + tileMetaFeature?: TileMetaFeature; x: number; y: number; z: number; diff --git a/x-pack/plugins/maps/public/inspector/vector_tile_adapter/vector_tile_adapter.test.ts b/x-pack/plugins/maps/public/inspector/vector_tile_adapter/vector_tile_adapter.test.ts new file mode 100644 index 0000000000000..5daad610d323d --- /dev/null +++ b/x-pack/plugins/maps/public/inspector/vector_tile_adapter/vector_tile_adapter.test.ts @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { TileMetaFeature } from '../../../common/descriptor_types'; +import { getTileError, getTileMetaFeature } from './vector_tile_adapter'; + +describe('getTileError', () => { + test('should find tileError for tile', () => { + const tileErrors = [ + { + message: 'simulated failure 1', + tileKey: '1/0/0', + }, + { + message: 'simulated failure 2', + tileKey: '1/1/0', + }, + ]; + const tileError = getTileError(0, 0, 1, tileErrors); + expect(tileError).not.toBeUndefined(); + expect(tileError!.message).toBe('simulated failure 1'); + }); +}); + +describe('getTileMetaFeature', () => { + test('should find tileMetaFeature for tile', () => { + const tileMetaFeatures = [ + { + geometry: { + coordinates: [ + [ + [0, -85.05112877980659], + [0, 0], + [180, 0], + [180, -85.05112877980659], + [0, -85.05112877980659], + ], + ], + type: 'Polygon', + }, + properties: { + 'hits.total.value': 0, + }, + type: 'Feature', + } as TileMetaFeature, + { + geometry: { + coordinates: [ + [ + [-180, 0], + [-180, 85.05112877980659], + [0, 85.05112877980659], + [0, 0], + [-180, 0], + ], + ], + type: 'Polygon', + }, + properties: { + 'hits.total.value': 182, + }, + type: 'Feature', + } as TileMetaFeature, + ]; + const tileMetaFeature = getTileMetaFeature(0, 0, 1, tileMetaFeatures); + expect(tileMetaFeature).not.toBeUndefined(); + expect(tileMetaFeature!.properties['hits.total.value']).toBe(182); + }); +}); diff --git a/x-pack/plugins/maps/public/inspector/vector_tile_adapter/vector_tile_adapter.ts b/x-pack/plugins/maps/public/inspector/vector_tile_adapter/vector_tile_adapter.ts index 51a12368d6ad4..05f256bd67e33 100644 --- a/x-pack/plugins/maps/public/inspector/vector_tile_adapter/vector_tile_adapter.ts +++ b/x-pack/plugins/maps/public/inspector/vector_tile_adapter/vector_tile_adapter.ts @@ -5,33 +5,62 @@ * 2.0. */ +// @ts-expect-error +import turfCenterOfMass from '@turf/center-of-mass'; import { EventEmitter } from 'events'; +import { LAT_INDEX, LON_INDEX } from '../../../common/constants'; +import type { TileError, TileMetaFeature } from '../../../common/descriptor_types'; import { TileRequest } from './types'; +import { isPointInTile } from '../../classes/util/geo_tile_utils'; + +interface LayerState { + label: string; + tileErrors?: TileError[]; + tileMetaFeatures?: TileMetaFeature[]; + tileUrl: string; +} export class VectorTileAdapter extends EventEmitter { - private _layers: Record = {}; + private _layers: Record = {}; private _tiles: Array<{ x: number; y: number; z: number }> = []; - addLayer(layerId: string, label: string, tileUrl: string) { + public addLayer(layerId: string, label: string, tileUrl: string) { this._layers[layerId] = { label, tileUrl }; this._onChange(); } - removeLayer(layerId: string) { + public removeLayer(layerId: string) { delete this._layers[layerId]; this._onChange(); } - hasLayers() { + public hasLayers() { return Object.keys(this._layers).length > 0; } - setTiles(tiles: Array<{ x: number; y: number; z: number }>) { + public setTiles(tiles: Array<{ x: number; y: number; z: number }>) { this._tiles = tiles; this._onChange(); } - getLayerOptions(): Array<{ value: string; label: string }> { + public setTileResults( + layerId: string, + tileMetaFeatures?: TileMetaFeature[], + tileErrors?: TileError[] + ) { + if (!this._layers[layerId]) { + return; + } + + this._layers[layerId] = { + ...this._layers[layerId], + tileErrors, + tileMetaFeatures, + }; + this._onChange(); + } + + public getLayerOptions(): Array<{ value: string; label: string }> { return Object.keys(this._layers).map((layerId) => { return { value: layerId, @@ -40,22 +69,58 @@ export class VectorTileAdapter extends EventEmitter { }); } - getTileRequests(layerId: string): TileRequest[] { + public getTileRequests(layerId: string): TileRequest[] { if (!this._layers[layerId]) { return []; } - const { tileUrl } = this._layers[layerId]; + const { tileErrors, tileMetaFeatures, tileUrl } = this._layers[layerId]; return this._tiles.map((tile) => { return { layerId, tileUrl, + tileError: getTileError(tile.x, tile.y, tile.z, tileErrors), + tileMetaFeature: getTileMetaFeature(tile.x, tile.y, tile.z, tileMetaFeatures), ...tile, }; }); } - _onChange() { + private _onChange() { this.emit('change'); } } + +export function getTileMetaFeature( + x: number, + y: number, + z: number, + tileMetaFeatures?: TileMetaFeature[] +) { + if (!tileMetaFeatures || tileMetaFeatures.length === 0) { + return; + } + + return tileMetaFeatures.find((tileMetaFeature) => { + const centerGeometry = turfCenterOfMass(tileMetaFeature).geometry; + return isPointInTile( + centerGeometry.coordinates[LAT_INDEX], + centerGeometry.coordinates[LON_INDEX], + x, + y, + z + ); + }); +} + +export function getTileError(x: number, y: number, z: number, tileErrors?: TileError[]) { + if (!tileErrors || tileErrors.length === 0) { + return; + } + + const tileKey = `${z}/${x}/${y}`; + + return tileErrors.find((tileError) => { + return tileError.tileKey === tileKey; + }); +} diff --git a/x-pack/plugins/maps/public/inspector/vector_tile_adapter/vector_tile_inspector_view.tsx b/x-pack/plugins/maps/public/inspector/vector_tile_adapter/vector_tile_inspector_view.tsx index 560b2789d5a74..ffae36cafba0f 100644 --- a/x-pack/plugins/maps/public/inspector/vector_tile_adapter/vector_tile_inspector_view.tsx +++ b/x-pack/plugins/maps/public/inspector/vector_tile_adapter/vector_tile_inspector_view.tsx @@ -6,7 +6,7 @@ */ import React, { lazy } from 'react'; -import type { Adapters } from '@kbn/inspector-plugin/public'; +import type { Adapters, InspectorViewProps } from '@kbn/inspector-plugin/public'; import { i18n } from '@kbn/i18n'; import { LazyWrapper } from '../../lazy_wrapper'; @@ -25,7 +25,7 @@ export const VectorTileInspectorView = { shouldShow(adapters: Adapters) { return Boolean(adapters.vectorTiles?.hasLayers()); }, - component: (props: { adapters: Adapters }) => { + component: (props: InspectorViewProps) => { return ; }, }; diff --git a/x-pack/plugins/maps/tsconfig.json b/x-pack/plugins/maps/tsconfig.json index a8a75988ba5b1..d2972dcd3e6f3 100644 --- a/x-pack/plugins/maps/tsconfig.json +++ b/x-pack/plugins/maps/tsconfig.json @@ -74,6 +74,7 @@ "@kbn/content-management-table-list-view", "@kbn/serverless", "@kbn/logging", + "@kbn/search-errors", "@kbn/search-response-warnings", "@kbn/calculate-width-from-char-count", "@kbn/content-management-table-list-view-common", diff --git a/x-pack/plugins/ml/public/alerting/beta_badge.tsx b/x-pack/plugins/ml/public/alerting/beta_badge.tsx deleted file mode 100644 index 1f03fa5c6c8bf..0000000000000 --- a/x-pack/plugins/ml/public/alerting/beta_badge.tsx +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { FC } from 'react'; -import { EuiBetaBadge, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; - -export const BetaBadge: FC<{ message: string }> = ({ message }) => { - return ( - - - - - - ); -}; diff --git a/x-pack/plugins/ml/public/alerting/jobs_health_rule/anomaly_detection_jobs_health_rule_trigger.tsx b/x-pack/plugins/ml/public/alerting/jobs_health_rule/anomaly_detection_jobs_health_rule_trigger.tsx index daab200ff0709..b47c74cfef227 100644 --- a/x-pack/plugins/ml/public/alerting/jobs_health_rule/anomaly_detection_jobs_health_rule_trigger.tsx +++ b/x-pack/plugins/ml/public/alerting/jobs_health_rule/anomaly_detection_jobs_health_rule_trigger.tsx @@ -20,7 +20,6 @@ import { HttpService } from '../../application/services/http_service'; import { useMlKibana } from '../../application/contexts/kibana'; import { TestsSelectionControl } from './tests_selection_control'; import { ALL_JOBS_SELECTION } from '../../../common/constants/alerts'; -import { BetaBadge } from '../beta_badge'; export type MlAnomalyAlertTriggerProps = RuleTypeParamsExpressionProps; @@ -108,15 +107,6 @@ const AnomalyDetectionJobsHealthRuleTrigger: FC = ({ error={formErrors} isInvalid={isFormInvalid} > - - ; @@ -158,12 +157,6 @@ const MlAnomalyAlertTrigger: FC = ({ return ( - - { return ( <> - - - - - - - - + {dataView && ( { if (record.influencers) { kqlQuery = record.influencers - .map( - (influencer) => - `"${influencer.influencer_field_name}":"${ - influencer.influencer_field_values[0] ?? '' - }"` - ) + .filter((influencer) => isDefined(influencer)) + .map((influencer) => { + const values = influencer.influencer_field_values; + + if (values.length > 0) { + const fieldName = escapeQuotes(influencer.influencer_field_name); + const escapedVals = values + .filter((value) => isDefined(value)) + .map((value) => `"${fieldName}":"${escapeQuotes(value)}"`); + // Ensure there's enclosing () if there are multiple field values, + return escapedVals.length > 1 ? `(${escapedVals.join(' OR ')})` : escapedVals[0]; + } + }) .join(' AND '); } diff --git a/x-pack/plugins/ml/public/application/components/anomaly_results_view_selector/anomaly_results_view_selector.test.tsx b/x-pack/plugins/ml/public/application/components/anomaly_results_view_selector/anomaly_results_view_selector.test.tsx index c2c23eb4453f7..69369dfa0f853 100644 --- a/x-pack/plugins/ml/public/application/components/anomaly_results_view_selector/anomaly_results_view_selector.test.tsx +++ b/x-pack/plugins/ml/public/application/components/anomaly_results_view_selector/anomaly_results_view_selector.test.tsx @@ -37,10 +37,9 @@ describe('AnomalyResultsViewSelector', () => { // Check the Single Metric Viewer element exists in the selector, and that it is checked. expect(getByTestId('mlAnomalyResultsViewSelectorSingleMetricViewer')).toBeInTheDocument(); - expect( - getByTestId('mlAnomalyResultsViewSelectorSingleMetricViewer') - .querySelector('input')! - .hasAttribute('checked') - ).toBe(true); + expect(getByTestId('mlAnomalyResultsViewSelectorSingleMetricViewer')).toHaveAttribute( + 'aria-pressed', + 'true' + ); }); }); diff --git a/x-pack/plugins/ml/public/application/components/field_stats_flyout/field_stats_info_button.tsx b/x-pack/plugins/ml/public/application/components/field_stats_flyout/field_stats_info_button.tsx index f8a626c55e0aa..9903c6fb26655 100644 --- a/x-pack/plugins/ml/public/application/components/field_stats_flyout/field_stats_info_button.tsx +++ b/x-pack/plugins/ml/public/application/components/field_stats_flyout/field_stats_info_button.tsx @@ -80,18 +80,29 @@ export const FieldStatsInfoButton = ({ ) : null} - + - - - {label} - - + + {label} +
); }; diff --git a/x-pack/plugins/ml/public/application/components/ml_inference/add_inference_pipeline_flyout.tsx b/x-pack/plugins/ml/public/application/components/ml_inference/add_inference_pipeline_flyout.tsx index c1e612e3b08d1..e1b4cc2710257 100644 --- a/x-pack/plugins/ml/public/application/components/ml_inference/add_inference_pipeline_flyout.tsx +++ b/x-pack/plugins/ml/public/application/components/ml_inference/add_inference_pipeline_flyout.tsx @@ -22,14 +22,14 @@ import { extractErrorProperties } from '@kbn/ml-error-utils'; import { ModelItem } from '../../model_management/models_list'; import type { AddInferencePipelineSteps } from './types'; import { ADD_INFERENCE_PIPELINE_STEPS } from './constants'; -import { AddInferencePipelineFooter } from './components/add_inference_pipeline_footer'; -import { AddInferencePipelineHorizontalSteps } from './components/add_inference_pipeline_horizontal_steps'; +import { AddInferencePipelineFooter } from '../shared'; +import { AddInferencePipelineHorizontalSteps } from '../shared'; import { getInitialState, getModelType } from './state'; import { PipelineDetails } from './components/pipeline_details'; import { ProcessorConfiguration } from './components/processor_configuration'; -import { OnFailureConfiguration } from './components/on_failure_configuration'; +import { OnFailureConfiguration } from '../shared'; import { TestPipeline } from './components/test_pipeline'; -import { ReviewAndCreatePipeline } from './components/review_and_create_pipeline'; +import { ReviewAndCreatePipeline } from '../shared'; import { useMlApiContext } from '../../contexts/kibana'; import { getPipelineConfig } from './get_pipeline_config'; import { validateInferencePipelineConfigurationStep } from './validation'; @@ -122,6 +122,8 @@ export const AddInferencePipelineFlyout: FC = ( setStep={setStep} isDetailsStepValid={pipelineNameError === undefined && targetFieldError === undefined} isConfigureProcessorStepValid={hasUnsavedChanges === false} + hasProcessorStep + pipelineCreated={formState.pipelineCreated} /> {step === ADD_INFERENCE_PIPELINE_STEPS.DETAILS && ( @@ -184,6 +186,7 @@ export const AddInferencePipelineFlyout: FC = ( isConfigureProcessorStepValid={hasUnsavedChanges === false} pipelineCreated={formState.pipelineCreated} creatingPipeline={formState.creatingPipeline} + hasProcessorStep /> diff --git a/x-pack/plugins/ml/public/application/components/ml_inference/components/pipeline_details.tsx b/x-pack/plugins/ml/public/application/components/ml_inference/components/pipeline_details.tsx index bd36ee4bf6c49..034c6ed5468e6 100644 --- a/x-pack/plugins/ml/public/application/components/ml_inference/components/pipeline_details.tsx +++ b/x-pack/plugins/ml/public/application/components/ml_inference/components/pipeline_details.tsx @@ -14,18 +14,14 @@ import { EuiFlexItem, EuiForm, EuiFormRow, - EuiLink, - EuiSpacer, - EuiTitle, - EuiText, - EuiTextArea, EuiPanel, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { useMlKibana } from '../../../contexts/kibana'; import type { MlInferenceState } from '../types'; +import { PipelineDetailsTitle } from '../../shared'; +import { PipelineNameAndDescription } from '../../shared'; interface Props { handlePipelineConfigUpdate: (configUpdate: Partial) => void; @@ -47,12 +43,6 @@ export const PipelineDetails: FC = memo( targetField, targetFieldError, }) => { - const { - services: { - docLinks: { links }, - }, - } = useMlKibana(); - const handleConfigChange = (value: string, type: string) => { handlePipelineConfigUpdate({ [type]: value }); }; @@ -60,133 +50,18 @@ export const PipelineDetails: FC = memo( return ( - -

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

-
- - -

- {modelId}, - pipeline: ( - - pipeline - - ), - }} - /> -

-

- - _reindex API - - ), - pipelineSimulateLink: ( - - pipeline/_simulate - - ), - }} - /> -

-
+
- {/* NAME */} - - {i18n.translate( - 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.steps.configure.name.helpText', - { - defaultMessage: - 'Pipeline names are unique within a deployment and can only contain letters, numbers, underscores, and hyphens.', - } - )} - - ) - } - error={pipelineNameError} - isInvalid={pipelineNameError !== undefined} - > - ) => - handleConfigChange(e.target.value, 'pipelineName') - } - /> - - {/* DESCRIPTION */} - - {i18n.translate( - 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.steps.configure.description.helpText', - { - defaultMessage: 'A description of what this pipeline does.', - } - )} - - } - > - ) => - handleConfigChange(e.target.value, 'pipelineDescription') - } - /> - + {/* NAME and DESCRIPTION */} + {/* TARGET FIELD */} = ({ pipelineName, sourceIndex }) => { - const [selectedIndex, setSelectedIndex] = useState([ - { label: sourceIndex }, - ]); + const [selectedIndex, setSelectedIndex] = useState( + sourceIndex ? [{ label: sourceIndex }] : [] + ); const [options, setOptions] = useState([]); const [destinationIndex, setDestinationIndex] = useState(''); const [destinationIndexExists, setDestinationIndexExists] = useState(false); @@ -205,7 +205,7 @@ export const ReindexWithPipeline: FC = ({ pipelineName, sourceIndex }) => setCanReindexError(errorMessage); } } - if (hasPrivileges !== undefined) { + if (hasPrivileges !== undefined && selectedIndex.length) { checkPrivileges(); } }, @@ -264,6 +264,7 @@ export const ReindexWithPipeline: FC = ({ pipelineName, sourceIndex }) => 0) || !canReindex || destinationIndexExists @@ -395,7 +396,7 @@ export const ReindexWithPipeline: FC = ({ pipelineName, sourceIndex }) => 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.steps.review.reindexStartedMessage', { defaultMessage: 'Reindexing of {sourceIndex} to {destinationIndex} has started.', - values: { sourceIndex, destinationIndex }, + values: { sourceIndex: selectedIndex[0].label, destinationIndex }, } )} color="success" diff --git a/x-pack/plugins/ml/public/application/components/ml_inference/get_steps.ts b/x-pack/plugins/ml/public/application/components/ml_inference/get_steps.ts index a7d3ea17de099..3f7c9ff2255fe 100644 --- a/x-pack/plugins/ml/public/application/components/ml_inference/get_steps.ts +++ b/x-pack/plugins/ml/public/application/components/ml_inference/get_steps.ts @@ -11,7 +11,8 @@ import { ADD_INFERENCE_PIPELINE_STEPS } from './constants'; export function getSteps( step: AddInferencePipelineSteps, isConfigureStepValid: boolean, - isPipelineDataValid: boolean + isPipelineDataValid: boolean, + hasProcessorStep: boolean ) { let nextStep: AddInferencePipelineSteps | undefined; let previousStep: AddInferencePipelineSteps | undefined; @@ -19,7 +20,9 @@ export function getSteps( switch (step) { case ADD_INFERENCE_PIPELINE_STEPS.DETAILS: - nextStep = ADD_INFERENCE_PIPELINE_STEPS.CONFIGURE_PROCESSOR; + nextStep = hasProcessorStep + ? ADD_INFERENCE_PIPELINE_STEPS.CONFIGURE_PROCESSOR + : ADD_INFERENCE_PIPELINE_STEPS.ON_FAILURE; isContinueButtonEnabled = isConfigureStepValid; break; case ADD_INFERENCE_PIPELINE_STEPS.CONFIGURE_PROCESSOR: @@ -29,7 +32,9 @@ export function getSteps( break; case ADD_INFERENCE_PIPELINE_STEPS.ON_FAILURE: nextStep = ADD_INFERENCE_PIPELINE_STEPS.TEST; - previousStep = ADD_INFERENCE_PIPELINE_STEPS.CONFIGURE_PROCESSOR; + previousStep = hasProcessorStep + ? ADD_INFERENCE_PIPELINE_STEPS.CONFIGURE_PROCESSOR + : ADD_INFERENCE_PIPELINE_STEPS.DETAILS; isContinueButtonEnabled = isPipelineDataValid; break; case ADD_INFERENCE_PIPELINE_STEPS.TEST: diff --git a/x-pack/plugins/ml/public/application/components/ml_inference/validation.ts b/x-pack/plugins/ml/public/application/components/ml_inference/validation.ts index f6326669cf55b..8c2e567b1c4f1 100644 --- a/x-pack/plugins/ml/public/application/components/ml_inference/validation.ts +++ b/x-pack/plugins/ml/public/application/components/ml_inference/validation.ts @@ -5,8 +5,10 @@ * 2.0. */ +import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { i18n } from '@kbn/i18n'; import { IngestInferenceProcessor } from '@elastic/elasticsearch/lib/api/types'; +import type { SupportedPytorchTasksType } from '@kbn/ml-trained-models-utils'; import { InferenceModelTypes } from './types'; import type { AddInferencePipelineFormErrors } from './types'; @@ -46,6 +48,18 @@ const INFERENCE_CONFIG_MODEL_TYPE_ERROR = i18n.translate( defaultMessage: 'Inference configuration inference type must match model type.', } ); +const PROCESSOR_REQUIRED = i18n.translate( + 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.steps.configure.processorRequiredError', + { + defaultMessage: 'At least one processor is required to create the pipeline.', + } +); +const INFERENCE_PROCESSOR_REQUIRED = i18n.translate( + 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.steps.configure.inferenceProcessorRequiredError', + { + defaultMessage: "An inference processor specifying 'model_id' is required.", + } +); const VALID_PIPELINE_NAME_REGEX = /^[\w\-]+$/; export const isValidPipelineName = (input: string): boolean => { @@ -75,7 +89,7 @@ export const validateInferencePipelineConfigurationStep = ( export const validateInferenceConfig = ( inferenceConfig: IngestInferenceProcessor['inference_config'], - modelType?: InferenceModelTypes + modelType?: InferenceModelTypes | SupportedPytorchTasksType ) => { const inferenceConfigKeys = Object.keys(inferenceConfig ?? {}); let error; @@ -116,3 +130,31 @@ export const validateFieldMap = ( return error; }; + +export const validatePipelineProcessors = ( + pipelineProcessors: estypes.IngestPipeline, + taskType?: SupportedPytorchTasksType +) => { + const { processors } = pipelineProcessors; + let error; + // Must have at least one processor + if (!Array.isArray(processors) || (Array.isArray(processors) && processors.length < 1)) { + error = PROCESSOR_REQUIRED; + } + + const inferenceProcessor = processors?.find( + (processor) => processor.inference && processor.inference.model_id + ); + + if (inferenceProcessor === undefined) { + error = INFERENCE_PROCESSOR_REQUIRED; + } else { + // If populated, inference config must have the correct model type + const inferenceConfig = inferenceProcessor.inference?.inference_config; + if (taskType && inferenceConfig) { + error = validateInferenceConfig(inferenceConfig, taskType); + } + } + + return error; +}; diff --git a/x-pack/plugins/ml/public/application/components/ml_inference/components/add_inference_pipeline_footer.tsx b/x-pack/plugins/ml/public/application/components/shared/add_inference_pipeline_footer.tsx similarity index 88% rename from x-pack/plugins/ml/public/application/components/ml_inference/components/add_inference_pipeline_footer.tsx rename to x-pack/plugins/ml/public/application/components/shared/add_inference_pipeline_footer.tsx index 04ea2ea217375..e6d000c4e9531 100644 --- a/x-pack/plugins/ml/public/application/components/ml_inference/components/add_inference_pipeline_footer.tsx +++ b/x-pack/plugins/ml/public/application/components/shared/add_inference_pipeline_footer.tsx @@ -9,14 +9,14 @@ import React, { FC, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; import { EuiButton, EuiButtonEmpty, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { AddInferencePipelineSteps } from '../types'; +import type { AddInferencePipelineSteps } from '../ml_inference/types'; import { BACK_BUTTON_LABEL, CANCEL_BUTTON_LABEL, CLOSE_BUTTON_LABEL, CONTINUE_BUTTON_LABEL, -} from '../constants'; -import { getSteps } from '../get_steps'; +} from '../ml_inference/constants'; +import { getSteps } from '../ml_inference/get_steps'; interface Props { isDetailsStepValid: boolean; @@ -26,7 +26,8 @@ interface Props { step: AddInferencePipelineSteps; onClose: () => void; onCreate: () => void; - setStep: React.Dispatch>; + setStep: (step: AddInferencePipelineSteps) => void; + hasProcessorStep: boolean; } export const AddInferencePipelineFooter: FC = ({ @@ -38,10 +39,11 @@ export const AddInferencePipelineFooter: FC = ({ onCreate, step, setStep, + hasProcessorStep, }) => { const { nextStep, previousStep, isContinueButtonEnabled } = useMemo( - () => getSteps(step, isDetailsStepValid, isConfigureProcessorStepValid), - [isDetailsStepValid, isConfigureProcessorStepValid, step] + () => getSteps(step, isDetailsStepValid, isConfigureProcessorStepValid, hasProcessorStep), + [isDetailsStepValid, isConfigureProcessorStepValid, step, hasProcessorStep] ); return ( diff --git a/x-pack/plugins/ml/public/application/components/ml_inference/components/add_inference_pipeline_horizontal_steps.tsx b/x-pack/plugins/ml/public/application/components/shared/add_inference_pipeline_horizontal_steps.tsx similarity index 58% rename from x-pack/plugins/ml/public/application/components/ml_inference/components/add_inference_pipeline_horizontal_steps.tsx rename to x-pack/plugins/ml/public/application/components/shared/add_inference_pipeline_horizontal_steps.tsx index 2a34f6483c24d..c8cdf387c84c2 100644 --- a/x-pack/plugins/ml/public/application/components/ml_inference/components/add_inference_pipeline_horizontal_steps.tsx +++ b/x-pack/plugins/ml/public/application/components/shared/add_inference_pipeline_horizontal_steps.tsx @@ -8,58 +8,58 @@ import React, { FC, memo } from 'react'; import { i18n } from '@kbn/i18n'; -import { EuiStepsHorizontal, EuiStepsHorizontalProps } from '@elastic/eui'; -import type { AddInferencePipelineSteps } from '../types'; -import { ADD_INFERENCE_PIPELINE_STEPS } from '../constants'; +import { EuiStepsHorizontal, type EuiStepsHorizontalProps } from '@elastic/eui'; +import type { AddInferencePipelineSteps } from '../ml_inference/types'; +import { ADD_INFERENCE_PIPELINE_STEPS } from '../ml_inference/constants'; const steps = Object.values(ADD_INFERENCE_PIPELINE_STEPS); interface Props { step: AddInferencePipelineSteps; - setStep: React.Dispatch>; + setStep: (step: AddInferencePipelineSteps) => void; isDetailsStepValid: boolean; - isConfigureProcessorStepValid: boolean; + isConfigureProcessorStepValid?: boolean; + hasProcessorStep: boolean; + pipelineCreated: boolean; } +const DISABLED = 'disabled'; +const COMPLETE = 'complete'; +const INCOMPLETE = 'incomplete'; + export const AddInferencePipelineHorizontalSteps: FC = memo( - ({ step, setStep, isDetailsStepValid, isConfigureProcessorStepValid }) => { + ({ + step, + setStep, + isDetailsStepValid, + isConfigureProcessorStepValid, + hasProcessorStep, + pipelineCreated, + }) => { const currentStepIndex = steps.findIndex((s) => s === step); + const navSteps: EuiStepsHorizontalProps['steps'] = [ { // Details - onClick: () => setStep(ADD_INFERENCE_PIPELINE_STEPS.DETAILS), - status: isDetailsStepValid ? 'complete' : 'incomplete', - title: i18n.translate( - 'xpack.ml.inferencePipeline.content.indices.transforms.addInferencePipelineModal.steps.details.title', - { - defaultMessage: 'Details', - } - ), - }, - { - // Processor configuration onClick: () => { - if (!isDetailsStepValid) return; - setStep(ADD_INFERENCE_PIPELINE_STEPS.CONFIGURE_PROCESSOR); + if (pipelineCreated) return; + setStep(ADD_INFERENCE_PIPELINE_STEPS.DETAILS); }, - status: - isDetailsStepValid && isConfigureProcessorStepValid && currentStepIndex > 1 - ? 'complete' - : 'incomplete', + status: isDetailsStepValid ? COMPLETE : INCOMPLETE, title: i18n.translate( - 'xpack.ml.inferencePipeline.content.indices.transforms.addInferencePipelineModal.steps.configureProcessor.title', + 'xpack.ml.inferencePipeline.content.indices.transforms.addInferencePipelineModal.steps.details.title', { - defaultMessage: 'Configure processor', + defaultMessage: 'Details', } ), }, { - // handle failures + // Handle failures onClick: () => { - if (!isDetailsStepValid) return; + if (!isDetailsStepValid || pipelineCreated) return; setStep(ADD_INFERENCE_PIPELINE_STEPS.ON_FAILURE); }, - status: currentStepIndex > 2 ? 'complete' : 'incomplete', + status: currentStepIndex > 2 ? COMPLETE : INCOMPLETE, title: i18n.translate( 'xpack.ml.inferencePipeline.content.indices.transforms.addInferencePipelineModal.steps.handleFailures.title', { @@ -70,10 +70,10 @@ export const AddInferencePipelineHorizontalSteps: FC = memo( { // Test onClick: () => { - if (!isConfigureProcessorStepValid || !isDetailsStepValid) return; + if (!isConfigureProcessorStepValid || !isDetailsStepValid || pipelineCreated) return; setStep(ADD_INFERENCE_PIPELINE_STEPS.TEST); }, - status: currentStepIndex > 3 ? 'complete' : 'incomplete', + status: currentStepIndex > 3 ? COMPLETE : INCOMPLETE, title: i18n.translate( 'xpack.ml.trainedModels.content.indices.transforms.addInferencePipelineModal.steps.test.title', { @@ -84,10 +84,10 @@ export const AddInferencePipelineHorizontalSteps: FC = memo( { // Review and Create onClick: () => { - if (!isConfigureProcessorStepValid) return; + if (!isConfigureProcessorStepValid || pipelineCreated) return; setStep(ADD_INFERENCE_PIPELINE_STEPS.CREATE); }, - status: isDetailsStepValid && isConfigureProcessorStepValid ? 'incomplete' : 'disabled', + status: isDetailsStepValid && isConfigureProcessorStepValid ? INCOMPLETE : DISABLED, title: i18n.translate( 'xpack.ml.inferencePipeline.content.indices.transforms.addInferencePipelineModal.steps.create.title', { @@ -96,23 +96,60 @@ export const AddInferencePipelineHorizontalSteps: FC = memo( ), }, ]; + + if (hasProcessorStep === true) { + navSteps.splice(1, 0, { + // Processor configuration + onClick: () => { + if (!isDetailsStepValid || pipelineCreated) return; + setStep(ADD_INFERENCE_PIPELINE_STEPS.CONFIGURE_PROCESSOR); + }, + status: + isDetailsStepValid && isConfigureProcessorStepValid && currentStepIndex > 1 + ? COMPLETE + : INCOMPLETE, + title: i18n.translate( + 'xpack.ml.inferencePipeline.content.indices.transforms.addInferencePipelineModal.steps.configureProcessor.title', + { + defaultMessage: 'Configure processor', + } + ), + }); + } + let DETAILS_INDEX: number; + let CONFIGURE_INDEX: number | undefined; + let ON_FAILURE_INDEX: number; + let TEST_INDEX: number; + let CREATE_INDEX: number; + + if (hasProcessorStep) { + [DETAILS_INDEX, CONFIGURE_INDEX, ON_FAILURE_INDEX, TEST_INDEX, CREATE_INDEX] = [ + 0, 1, 2, 3, 4, 5, + ]; + } else { + [DETAILS_INDEX, ON_FAILURE_INDEX, TEST_INDEX, CREATE_INDEX] = [0, 1, 2, 3, 4]; + } + switch (step) { case ADD_INFERENCE_PIPELINE_STEPS.DETAILS: - navSteps[0].status = 'current'; + navSteps[DETAILS_INDEX].status = 'current'; break; case ADD_INFERENCE_PIPELINE_STEPS.CONFIGURE_PROCESSOR: - navSteps[1].status = 'current'; + if (CONFIGURE_INDEX !== undefined) { + navSteps[CONFIGURE_INDEX].status = 'current'; + } break; case ADD_INFERENCE_PIPELINE_STEPS.ON_FAILURE: - navSteps[2].status = 'current'; + navSteps[ON_FAILURE_INDEX].status = 'current'; break; case ADD_INFERENCE_PIPELINE_STEPS.TEST: - navSteps[3].status = 'current'; + navSteps[TEST_INDEX].status = 'current'; break; case ADD_INFERENCE_PIPELINE_STEPS.CREATE: - navSteps[4].status = 'current'; + navSteps[CREATE_INDEX].status = 'current'; break; } + return ; } ); diff --git a/x-pack/plugins/ml/public/application/components/shared/index.ts b/x-pack/plugins/ml/public/application/components/shared/index.ts new file mode 100644 index 0000000000000..573c13257a465 --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/shared/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { AddInferencePipelineHorizontalSteps } from './add_inference_pipeline_horizontal_steps'; +export { AddInferencePipelineFooter } from './add_inference_pipeline_footer'; +export { ReviewAndCreatePipeline } from './review_and_create_pipeline'; +export { OnFailureConfiguration } from './on_failure_configuration'; +export { PipelineDetailsTitle } from './pipeline_details_title'; +export { PipelineNameAndDescription } from './pipeline_name_and_description'; diff --git a/x-pack/plugins/ml/public/application/components/ml_inference/components/on_failure_configuration.tsx b/x-pack/plugins/ml/public/application/components/shared/on_failure_configuration.tsx similarity index 96% rename from x-pack/plugins/ml/public/application/components/ml_inference/components/on_failure_configuration.tsx rename to x-pack/plugins/ml/public/application/components/shared/on_failure_configuration.tsx index f53e80b122f53..10ab5a46a6327 100644 --- a/x-pack/plugins/ml/public/application/components/ml_inference/components/on_failure_configuration.tsx +++ b/x-pack/plugins/ml/public/application/components/shared/on_failure_configuration.tsx @@ -25,12 +25,12 @@ import { CodeEditor } from '@kbn/kibana-react-plugin/public'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; -import { SaveChangesButton } from './save_changes_button'; -import type { MlInferenceState } from '../types'; -import { getDefaultOnFailureConfiguration } from '../state'; -import { CANCEL_EDIT_MESSAGE, EDIT_MESSAGE } from '../constants'; -import { useMlKibana } from '../../../contexts/kibana'; -import { isValidJson } from '../../../../../common/util/validation_utils'; +import { SaveChangesButton } from '../ml_inference/components/save_changes_button'; +import type { MlInferenceState } from '../ml_inference/types'; +import { getDefaultOnFailureConfiguration } from '../ml_inference/state'; +import { CANCEL_EDIT_MESSAGE, EDIT_MESSAGE } from '../ml_inference/constants'; +import { useMlKibana } from '../../contexts/kibana'; +import { isValidJson } from '../../../../common/util/validation_utils'; interface Props { handleAdvancedConfigUpdate: (configUpdate: Partial) => void; diff --git a/x-pack/plugins/ml/public/application/components/shared/pipeline_details_title.tsx b/x-pack/plugins/ml/public/application/components/shared/pipeline_details_title.tsx new file mode 100644 index 0000000000000..ce7ef231256c5 --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/shared/pipeline_details_title.tsx @@ -0,0 +1,73 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC } from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { EuiCode, EuiLink, EuiSpacer, EuiTitle, EuiText } from '@elastic/eui'; + +import { useMlKibana } from '../../contexts/kibana'; + +interface Props { + modelId: string; +} + +export const PipelineDetailsTitle: FC = ({ modelId }) => { + const { + services: { + docLinks: { links }, + }, + } = useMlKibana(); + + return ( + <> + +

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

+
+ + +

+ {modelId}, + pipeline: ( + + pipeline + + ), + }} + /> +

+

+ + _reindex API + + ), + pipelineSimulateLink: ( + + pipeline/_simulate + + ), + }} + /> +

+
+ + ); +}; diff --git a/x-pack/plugins/ml/public/application/components/shared/pipeline_name_and_description.tsx b/x-pack/plugins/ml/public/application/components/shared/pipeline_name_and_description.tsx new file mode 100644 index 0000000000000..c64fbca939034 --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/shared/pipeline_name_and_description.tsx @@ -0,0 +1,115 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiFieldText, EuiFormRow, EuiText, EuiTextArea } from '@elastic/eui'; + +interface Props { + handlePipelineConfigUpdate: (configUpdate: Partial) => void; + pipelineNameError: string | undefined; + pipelineDescription: string; + pipelineName: string; +} + +export const PipelineNameAndDescription: FC = ({ + pipelineName, + pipelineNameError, + pipelineDescription, + handlePipelineConfigUpdate, +}) => { + const handleConfigChange = (value: string, type: string) => { + handlePipelineConfigUpdate({ [type]: value }); + }; + + return ( + <> + + {i18n.translate( + 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.steps.configure.name.helpText', + { + defaultMessage: + 'Pipeline names are unique within a deployment and can only contain letters, numbers, underscores, and hyphens.', + } + )} + + ) + } + error={pipelineNameError} + isInvalid={pipelineNameError !== undefined} + > + ) => + handleConfigChange(e.target.value, 'pipelineName') + } + /> + + {/* DESCRIPTION */} + + {i18n.translate( + 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.steps.configure.description.helpText', + { + defaultMessage: 'A description of the pipeline.', + } + )} + + } + > + ) => + handleConfigChange(e.target.value, 'pipelineDescription') + } + /> + + + ); +}; diff --git a/x-pack/plugins/ml/public/application/components/ml_inference/components/review_and_create_pipeline.tsx b/x-pack/plugins/ml/public/application/components/shared/review_and_create_pipeline.tsx similarity index 86% rename from x-pack/plugins/ml/public/application/components/ml_inference/components/review_and_create_pipeline.tsx rename to x-pack/plugins/ml/public/application/components/shared/review_and_create_pipeline.tsx index d80e678bafb06..7cb3066436af9 100644 --- a/x-pack/plugins/ml/public/application/components/ml_inference/components/review_and_create_pipeline.tsx +++ b/x-pack/plugins/ml/public/application/components/shared/review_and_create_pipeline.tsx @@ -7,6 +7,7 @@ import React, { FC, useMemo, useState } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; +import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { EuiAccordion, @@ -18,17 +19,28 @@ import { EuiSpacer, EuiTitle, EuiText, + EuiTextColor, htmlIdGenerator, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { IngestPipeline } from '@elastic/elasticsearch/lib/api/types'; -import { useMlKibana } from '../../../contexts/kibana'; -import { ReindexWithPipeline } from './reindex_with_pipeline'; +import { useMlKibana } from '../../contexts/kibana'; +import { ReindexWithPipeline } from '../ml_inference/components/reindex_with_pipeline'; const MANAGEMENT_APP_ID = 'management'; +function getFieldFromPipelineConfig(config: estypes.IngestPipeline) { + const { processors } = config; + let field = ''; + if (processors?.length) { + field = Object.keys(processors[0].inference?.field_map ?? {})[0]; + } + return field; +} + interface Props { + highlightTargetField?: boolean; inferencePipeline: IngestPipeline; modelType?: string; pipelineName: string; @@ -38,6 +50,7 @@ interface Props { } export const ReviewAndCreatePipeline: FC = ({ + highlightTargetField = false, inferencePipeline, modelType, pipelineName, @@ -62,6 +75,10 @@ export const ReviewAndCreatePipeline: FC = ({ : links.ingest.inferenceClassification; const accordionId = useMemo(() => htmlIdGenerator()(), []); + const targetedField = useMemo( + () => getFieldFromPipelineConfig(inferencePipeline), + [inferencePipeline] + ); const configCodeBlock = useMemo( () => ( @@ -84,7 +101,7 @@ export const ReviewAndCreatePipeline: FC = ({ gutterSize="s" data-test-subj="mlTrainedModelsInferenceReviewAndCreateStep" > - + {pipelineCreated === false ? (

@@ -189,20 +206,19 @@ export const ReviewAndCreatePipeline: FC = ({ ) : null} - - -

- {!pipelineCreated ? ( - - ) : null} -

-
-
+ {highlightTargetField ? ( + + + {targetedField} }} + /> + + + ) : null} - {pipelineCreated && sourceIndex ? ( + {pipelineCreated ? ( <> { - return { - crosshair: { - line: { - stroke: 'black', - strokeWidth: 1, - dash: [4, 4], - }, - }, - }; - }), + useChartsBaseTheme: jest.fn(() => LIGHT_THEME), }, activeCursor: { activeCursor$: new BehaviorSubject({ diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/feature_importance/decision_path_chart.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/feature_importance/decision_path_chart.tsx index 1eb4fc62b472d..7d6a2755348ae 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/feature_importance/decision_path_chart.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/feature_importance/decision_path_chart.tsx @@ -19,6 +19,7 @@ import { RecursivePartial, ScaleType, Settings, + LEGACY_LIGHT_THEME, } from '@elastic/charts'; import { EuiIcon } from '@elastic/eui'; @@ -127,8 +128,9 @@ export const DecisionPathChart = ({ size={{ height: DECISION_PATH_MARGIN + decisionPathData.length * DECISION_PATH_ROW_HEIGHT }} > diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/total_feature_importance_summary/feature_importance_summary.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/total_feature_importance_summary/feature_importance_summary.tsx index 00e52934b0627..a3e55a0e09064 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/total_feature_importance_summary/feature_importance_summary.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_exploration/components/total_feature_importance_summary/feature_importance_summary.tsx @@ -19,6 +19,7 @@ import { AxisStyle, PartialTheme, BarSeriesProps, + LEGACY_LIGHT_THEME, } from '@elastic/charts'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -295,8 +296,9 @@ export const FeatureImportanceSummaryPanel: FC diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js index 169f739de41cd..175b58a72c0a5 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.js @@ -326,9 +326,9 @@ export class ExplorerChartDistribution extends React.Component { return `M${xPosition},${chartHeight} ${xPosition},0`; }) // Use elastic chart's cursor line style if possible - .style('stroke', `${chartTheme.crosshair.line.stroke ?? 'black'}`) - .style('stroke-width', `${chartTheme.crosshair.line.strokeWidth ?? '1'}px`) - .style('stroke-dasharray', chartTheme.crosshair.line.dash ?? '4,4'); + .style('stroke', chartTheme.crosshair.line.stroke) + .style('stroke-width', `${chartTheme.crosshair.line.strokeWidth}px`) + .style('stroke-dasharray', chartTheme.crosshair.line.dash?.join(',') ?? '4,4'); cursorMouseLine.exit().remove(); } diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.test.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.test.js index d77b66b960625..5a146c51b61c1 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.test.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_distribution.test.js @@ -22,7 +22,7 @@ import { kibanaContextMock } from '../../contexts/kibana/__mocks__/kibana_contex const utilityProps = { timeBuckets: timeBucketsMock, - chartTheme: kibanaContextMock.services.charts.theme.useChartsTheme(), + chartTheme: kibanaContextMock.services.charts.theme.useChartsBaseTheme(), onPointerUpdate: jest.fn(), cursor: { x: 10432423, diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.js index b93555e8c2ff9..95933451d6f47 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.js @@ -257,9 +257,9 @@ export class ExplorerChartSingleMetric extends React.Component { return `M${xPosition},${chartHeight} ${xPosition},0`; }) // Use elastic chart's cursor line style if possible - .style('stroke', `${chartTheme.crosshair.line.stroke ?? 'black'}`) - .style('stroke-width', `${chartTheme.crosshair.line.strokeWidth ?? '1'}px`) - .style('stroke-dasharray', chartTheme.crosshair.line.dash ?? '4,4'); + .style('stroke', chartTheme.crosshair.line.stroke) + .style('stroke-width', `${chartTheme.crosshair.line.strokeWidth}px`) + .style('stroke-dasharray', chartTheme.crosshair.line.dash?.join(',') ?? '4,4'); cursorMouseLine.exit().remove(); } diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.test.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.test.js index abe8c0c991cc4..3bc38c5754e88 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.test.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_chart_single_metric.test.js @@ -22,7 +22,7 @@ import { kibanaContextMock } from '../../contexts/kibana/__mocks__/kibana_contex const utilityProps = { timeBuckets: timeBucketsMock, - chartTheme: kibanaContextMock.services.charts.theme.useChartsTheme(), + chartTheme: kibanaContextMock.services.charts.theme.useChartsBaseTheme(), onPointerUpdate: jest.fn(), cursor: { x: 10432423, diff --git a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container.js b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container.js index fb340977dfae8..9e84a1f546b04 100644 --- a/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container.js +++ b/x-pack/plugins/ml/public/application/explorer/explorer_charts/explorer_charts_container.js @@ -43,7 +43,7 @@ import { ExplorerChartsErrorCallOuts } from './explorer_charts_error_callouts'; import { addItemToRecentlyAccessed } from '../../util/recently_accessed'; import { EmbeddedMapComponentWrapper } from './explorer_chart_embedded_map'; import { useActiveCursor } from '@kbn/charts-plugin/public'; -import { BarSeries, Chart, Settings } from '@elastic/charts'; +import { BarSeries, Chart, Settings, LEGACY_LIGHT_THEME } from '@elastic/charts'; import useObservable from 'react-use/lib/useObservable'; import { escapeKueryForFieldValuePair } from '../../util/string_utils'; @@ -188,7 +188,7 @@ function ExplorerChartContainer({ const chartRef = useRef(null); const { euiTheme } = useEuiTheme(); - const chartTheme = chartsService.theme.useChartsTheme(); + const chartTheme = chartsService.theme.useChartsBaseTheme(); const handleCursorUpdate = useActiveCursor(chartsService.activeCursor, chartRef, { isDateHistogram: true, @@ -238,7 +238,14 @@ function ExplorerChartContainer({ {/* so that we can use chart's ref which controls the activeCursor api */}
- } width={0} height={0} locale={i18n.getLocale()} /> + } + width={0} + height={0} + locale={i18n.getLocale()} + /> {/* Just need an empty chart to access cursor service */} diff --git a/x-pack/plugins/ml/public/application/explorer/swimlane_container.tsx b/x-pack/plugins/ml/public/application/explorer/swimlane_container.tsx index 65ec5c9350269..7051060b6a255 100644 --- a/x-pack/plugins/ml/public/application/explorer/swimlane_container.tsx +++ b/x-pack/plugins/ml/public/application/explorer/swimlane_container.tsx @@ -31,6 +31,7 @@ import { TooltipProps, TooltipValue, Tooltip, + LEGACY_LIGHT_THEME, } from '@elastic/charts'; import moment from 'moment'; import { i18n } from '@kbn/i18n'; @@ -449,8 +450,9 @@ export const SwimlaneContainer: FC = ({ = ({ annotations.lines[0].datum.modelSnapshot ); }} - // TODO use the EUI charts theme see src/plugins/charts/public/services/theme/README.md theme={{ lineSeriesStyle: { point: { @@ -427,6 +427,8 @@ export const DatafeedChartFlyout: FC = ({ }, }, }} + // TODO connect to charts.theme service see src/plugins/charts/public/services/theme/README.md + baseTheme={LEGACY_LIGHT_THEME} locale={i18n.getLocale()} /> = ({ diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/charts/event_rate_chart/event_rate_chart.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/charts/event_rate_chart/event_rate_chart.tsx index 0afee34e406d7..04553dda99255 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/charts/event_rate_chart/event_rate_chart.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/charts/event_rate_chart/event_rate_chart.tsx @@ -15,6 +15,7 @@ import { BrushEndListener, PartialTheme, Tooltip, + LEGACY_LIGHT_THEME, } from '@elastic/charts'; import { css } from '@emotion/react'; import { i18n } from '@kbn/i18n'; @@ -77,8 +78,9 @@ export const EventRateChart: FC = ({ diff --git a/x-pack/plugins/ml/public/application/memory_usage/memory_tree_map/tree_map.tsx b/x-pack/plugins/ml/public/application/memory_usage/memory_tree_map/tree_map.tsx index 0cf65ec6d0949..ebbae3fff924f 100644 --- a/x-pack/plugins/ml/public/application/memory_usage/memory_tree_map/tree_map.tsx +++ b/x-pack/plugins/ml/public/application/memory_usage/memory_tree_map/tree_map.tsx @@ -11,10 +11,9 @@ import { Settings, Partition, PartitionLayout, - LIGHT_THEME, DARK_THEME, + LIGHT_THEME, } from '@elastic/charts'; -import { EUI_CHARTS_THEME_DARK, EUI_CHARTS_THEME_LIGHT } from '@elastic/eui/dist/eui_charts_theme'; import { FIELD_FORMAT_IDS } from '@kbn/field-formats-plugin/common'; import { EuiComboBox, EuiComboBoxOptionOption, EuiEmptyPrompt, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -65,13 +64,7 @@ export const JobMemoryTreeMap: FC = ({ node, type, height }) => { } = useMlKibana(); const isDarkTheme = useIsDarkTheme(themeService); - const { theme, baseTheme } = useMemo( - () => - isDarkTheme - ? { theme: EUI_CHARTS_THEME_DARK, baseTheme: DARK_THEME } - : { theme: EUI_CHARTS_THEME_LIGHT, baseTheme: LIGHT_THEME }, - [isDarkTheme] - ); + const baseTheme = useMemo(() => (isDarkTheme ? DARK_THEME : LIGHT_THEME), [isDarkTheme]); const { isADEnabled, isDFAEnabled, isNLPEnabled } = useEnabledFeatures(); @@ -173,7 +166,7 @@ export const JobMemoryTreeMap: FC = ({ node, type, height }) => { {data.length ? ( - + id="memoryUsageTreeMap" data={data} diff --git a/x-pack/plugins/ml/public/application/memory_usage/nodes_overview/memory_preview_chart.tsx b/x-pack/plugins/ml/public/application/memory_usage/nodes_overview/memory_preview_chart.tsx index dc75907e771b2..269dd2e3fb400 100644 --- a/x-pack/plugins/ml/public/application/memory_usage/nodes_overview/memory_preview_chart.tsx +++ b/x-pack/plugins/ml/public/application/memory_usage/nodes_overview/memory_preview_chart.tsx @@ -18,12 +18,14 @@ import { LineAnnotation, AnnotationDomainType, Tooltip, + LEGACY_LIGHT_THEME, } from '@elastic/charts'; import { EuiIcon } from '@elastic/eui'; import { FIELD_FORMAT_IDS } from '@kbn/field-formats-plugin/common'; import { NodeDeploymentStatsResponse } from '../../../../common/types/trained_models'; import { useFieldFormatter } from '../../contexts/kibana/use_field_formatter'; import { getMemoryItemColor } from '../memory_item_colors'; +import { useMlKibana } from '../../contexts/kibana'; interface MemoryPreviewChartProps { memoryOverview: NodeDeploymentStatsResponse['memory_overview']; @@ -31,6 +33,9 @@ interface MemoryPreviewChartProps { export const MemoryPreviewChart: FC = ({ memoryOverview }) => { const bytesFormatter = useFieldFormatter(FIELD_FORMAT_IDS.BYTES); + const { + services: { charts: chartsService }, + } = useMlKibana(); const groups = useMemo( () => ({ @@ -117,7 +122,8 @@ export const MemoryPreviewChart: FC = ({ memoryOverview } /> diff --git a/x-pack/plugins/ml/public/application/model_management/add_model_flyout.tsx b/x-pack/plugins/ml/public/application/model_management/add_model_flyout.tsx index 6c69446b8725e..cf4efb4846fc0 100644 --- a/x-pack/plugins/ml/public/application/model_management/add_model_flyout.tsx +++ b/x-pack/plugins/ml/public/application/model_management/add_model_flyout.tsx @@ -226,18 +226,6 @@ const ClickToDownloadTabContent: FC = ({ /> - - - - -
@@ -286,25 +274,45 @@ const ClickToDownloadTabContent: FC = ({ {model.model_id}
- {model.recommended ? ( - - - } - > - - - - - - ) : null} + + + {model.recommended ? ( + + + } + > + + + + + + ) : null} + {model.licenseUrl && model.softwareLicense ? ( + + + {model.softwareLicense === 'MIT' ? ( + + ) : null} + + + ) : null} + + } name={model.model_id} diff --git a/x-pack/plugins/ml/public/application/model_management/create_pipeline_for_model/create_pipeline_for_model_flyout.tsx b/x-pack/plugins/ml/public/application/model_management/create_pipeline_for_model/create_pipeline_for_model_flyout.tsx new file mode 100644 index 0000000000000..953aa23e7f811 --- /dev/null +++ b/x-pack/plugins/ml/public/application/model_management/create_pipeline_for_model/create_pipeline_for_model_flyout.tsx @@ -0,0 +1,193 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC, useMemo, useState } from 'react'; + +import { + EuiFlyout, + EuiFlyoutBody, + EuiFlyoutHeader, + EuiFlyoutFooter, + EuiSpacer, + EuiTitle, +} from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; +import { extractErrorProperties } from '@kbn/ml-error-utils'; +import type { SupportedPytorchTasksType } from '@kbn/ml-trained-models-utils'; + +import { ModelItem } from '../models_list'; +import type { AddInferencePipelineSteps } from '../../components/ml_inference/types'; +import { ADD_INFERENCE_PIPELINE_STEPS } from '../../components/ml_inference/constants'; +import { AddInferencePipelineFooter } from '../../components/shared'; +import { AddInferencePipelineHorizontalSteps } from '../../components/shared'; +import { getInitialState } from './state'; +import { PipelineDetails } from './pipeline_details'; +import { TestTrainedModel } from './test_trained_model'; +import { OnFailureConfiguration } from '../../components/shared'; +import { ReviewAndCreatePipeline } from '../../components/shared'; +import { useMlApiContext } from '../../contexts/kibana'; +import { getPipelineConfig } from './get_pipeline_config'; +import { validateInferencePipelineConfigurationStep } from '../../components/ml_inference/validation'; +import { type InferecePipelineCreationState } from './state'; +import { useFetchPipelines } from '../../components/ml_inference/hooks/use_fetch_pipelines'; +import { useTestTrainedModelsContext } from '../test_models/test_trained_models_context'; + +export interface CreatePipelineForModelFlyoutProps { + onClose: (refreshList?: boolean) => void; + model: ModelItem; +} + +export const CreatePipelineForModelFlyout: FC = ({ + onClose, + model, +}) => { + const { + currentContext: { pipelineConfig }, + } = useTestTrainedModelsContext(); + + const initialState = useMemo( + () => getInitialState(model, pipelineConfig), + // eslint-disable-next-line react-hooks/exhaustive-deps + [model.model_id, pipelineConfig] + ); + const [formState, setFormState] = useState(initialState); + const [step, setStep] = useState(ADD_INFERENCE_PIPELINE_STEPS.DETAILS); + const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); + const taskType = useMemo( + () => Object.keys(model.inference_config ?? {})[0], + // eslint-disable-next-line react-hooks/exhaustive-deps + [model.model_id] + ) as SupportedPytorchTasksType; + + const { + trainedModels: { createInferencePipeline }, + } = useMlApiContext(); + + const createPipeline = async () => { + setFormState({ ...formState, creatingPipeline: true }); + try { + const config = getPipelineConfig(formState); + await createInferencePipeline(formState.pipelineName, config); + setFormState({ + ...formState, + pipelineCreated: true, + creatingPipeline: false, + pipelineError: undefined, + }); + } catch (e) { + // eslint-disable-next-line no-console + console.error(e); + const errorProperties = extractErrorProperties(e); + setFormState({ + ...formState, + creatingPipeline: false, + pipelineError: errorProperties.message ?? e.message, + }); + } + }; + + const pipelineNames = useFetchPipelines(); + + const handleConfigUpdate = (configUpdate: Partial) => { + const updatedState = { ...formState, ...configUpdate }; + setFormState(updatedState); + }; + + const handleSetStep = (currentStep: AddInferencePipelineSteps) => { + setStep(currentStep); + }; + + const { pipelineName: pipelineNameError } = useMemo(() => { + const errors = validateInferencePipelineConfigurationStep( + formState.pipelineName, + pipelineNames + ); + return errors; + }, [pipelineNames, formState.pipelineName]); + + return ( + + + +

+ {i18n.translate( + 'xpack.ml.trainedModels.content.indices.pipelines.createInferencePipeline.title', + { + defaultMessage: 'Create inference pipeline', + } + )} +

+
+
+ + + + {step === ADD_INFERENCE_PIPELINE_STEPS.DETAILS && ( + + )} + {step === ADD_INFERENCE_PIPELINE_STEPS.ON_FAILURE && ( + + )} + {step === ADD_INFERENCE_PIPELINE_STEPS.TEST && ( + + )} + {step === ADD_INFERENCE_PIPELINE_STEPS.CREATE && ( + + )} + + + + +
+ ); +}; diff --git a/x-pack/plugins/ml/public/application/model_management/create_pipeline_for_model/get_inference_properties_from_pipeline_config.ts b/x-pack/plugins/ml/public/application/model_management/create_pipeline_for_model/get_inference_properties_from_pipeline_config.ts new file mode 100644 index 0000000000000..68b7dcda61a73 --- /dev/null +++ b/x-pack/plugins/ml/public/application/model_management/create_pipeline_for_model/get_inference_properties_from_pipeline_config.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 * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { + IngestInferenceProcessor, + IngestInferenceConfig, +} from '@elastic/elasticsearch/lib/api/types'; +import { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import { SUPPORTED_PYTORCH_TASKS } from '@kbn/ml-trained-models-utils'; +import { DEFAULT_INPUT_FIELD } from '../test_models/models/inference_base'; + +const INPUT_FIELD = 'inputField'; +const ZERO_SHOT_CLASSIFICATION_PROPERTIES = ['labels', 'multi_label'] as const; +const QUESTION_ANSWERING_PROPERTIES = ['question'] as const; + +const MODEL_INFERENCE_CONFIG_PROPERTIES = { + [SUPPORTED_PYTORCH_TASKS.QUESTION_ANSWERING]: QUESTION_ANSWERING_PROPERTIES, + [SUPPORTED_PYTORCH_TASKS.ZERO_SHOT_CLASSIFICATION]: ZERO_SHOT_CLASSIFICATION_PROPERTIES, +} as const; + +type SupportedModelInferenceConfigPropertiesType = keyof typeof MODEL_INFERENCE_CONFIG_PROPERTIES; + +interface MLIngestInferenceProcessor extends IngestInferenceProcessor { + inference_config: MLInferencePipelineInferenceConfig; +} + +// Currently, estypes doesn't include pipeline processor types with the trained model processors +type MLInferencePipelineInferenceConfig = IngestInferenceConfig & { + zero_shot_classification?: estypes.MlZeroShotClassificationInferenceOptions; + question_answering?: estypes.MlQuestionAnsweringInferenceUpdateOptions; +}; + +interface GetInferencePropertiesFromPipelineConfigReturnType { + inputField: string; + inferenceConfig?: MLInferencePipelineInferenceConfig; + inferenceObj?: IngestInferenceProcessor | MLIngestInferenceProcessor; + fieldMap?: IngestInferenceProcessor['field_map']; + labels?: string[]; + multi_label?: boolean; + question?: string; +} + +function isSupportedInferenceConfigPropertyType( + arg: unknown +): arg is SupportedModelInferenceConfigPropertiesType { + return typeof arg === 'string' && Object.keys(MODEL_INFERENCE_CONFIG_PROPERTIES).includes(arg); +} + +export function isMlInferencePipelineInferenceConfig( + arg: unknown +): arg is MLInferencePipelineInferenceConfig { + return ( + isPopulatedObject(arg, [SUPPORTED_PYTORCH_TASKS.QUESTION_ANSWERING]) || + isPopulatedObject(arg, [SUPPORTED_PYTORCH_TASKS.ZERO_SHOT_CLASSIFICATION]) + ); +} + +export function isMlIngestInferenceProcessor(arg: unknown): arg is MLIngestInferenceProcessor { + return ( + isPopulatedObject(arg) && + arg.hasOwnProperty('inference_config') && + (isPopulatedObject(arg.inference_config, [SUPPORTED_PYTORCH_TASKS.QUESTION_ANSWERING]) || + isPopulatedObject(arg.inference_config, [SUPPORTED_PYTORCH_TASKS.ZERO_SHOT_CLASSIFICATION])) + ); +} + +export function getInferencePropertiesFromPipelineConfig( + type: string, + pipelineConfig: estypes.IngestPipeline +): GetInferencePropertiesFromPipelineConfigReturnType { + const propertiesToReturn: GetInferencePropertiesFromPipelineConfigReturnType = { + [INPUT_FIELD]: '', + }; + + pipelineConfig.processors?.forEach((processor) => { + const { inference } = processor; + if (inference) { + propertiesToReturn.inferenceObj = inference; + // Get the input field + if (inference.field_map) { + propertiesToReturn.fieldMap = inference.field_map; + + for (const [key, value] of Object.entries(inference.field_map)) { + if (value === DEFAULT_INPUT_FIELD) { + propertiesToReturn[INPUT_FIELD] = key; + } + } + if (propertiesToReturn[INPUT_FIELD] === '') { + // If not found, set to the first field in the field map + propertiesToReturn[INPUT_FIELD] = Object.keys(inference.field_map)[0]; + } + } + propertiesToReturn.inferenceConfig = inference.inference_config; + // Get the properties associated with the type of model/task + if ( + isMlInferencePipelineInferenceConfig(propertiesToReturn.inferenceConfig) && + isSupportedInferenceConfigPropertyType(type) + ) { + MODEL_INFERENCE_CONFIG_PROPERTIES[type]?.forEach((property) => { + const configSettings = + propertiesToReturn.inferenceConfig && propertiesToReturn.inferenceConfig[type]; + propertiesToReturn[property] = + configSettings && configSettings.hasOwnProperty(property) + ? // @ts-ignore + configSettings[property] + : undefined; + }); + } + } + }); + + return propertiesToReturn; +} diff --git a/x-pack/plugins/ml/public/application/model_management/create_pipeline_for_model/get_pipeline_config.ts b/x-pack/plugins/ml/public/application/model_management/create_pipeline_for_model/get_pipeline_config.ts new file mode 100644 index 0000000000000..10c405090f057 --- /dev/null +++ b/x-pack/plugins/ml/public/application/model_management/create_pipeline_for_model/get_pipeline_config.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import type { InferecePipelineCreationState } from './state'; + +export function getPipelineConfig(state: InferecePipelineCreationState): estypes.IngestPipeline { + const { ignoreFailure, modelId, onFailure, pipelineDescription, initialPipelineConfig } = state; + const processor = + initialPipelineConfig?.processors && initialPipelineConfig.processors?.length + ? initialPipelineConfig?.processors[0] + : {}; + + return { + description: pipelineDescription, + processors: [ + { + inference: { + ...(processor?.inference + ? { + ...processor.inference, + ignore_failure: ignoreFailure, + ...(onFailure && Object.keys(onFailure).length > 0 + ? { on_failure: onFailure } + : { on_failure: undefined }), + } + : {}), + model_id: modelId, + }, + }, + ], + }; +} diff --git a/x-pack/plugins/ml/public/application/model_management/create_pipeline_for_model/pipeline_details.tsx b/x-pack/plugins/ml/public/application/model_management/create_pipeline_for_model/pipeline_details.tsx new file mode 100644 index 0000000000000..ef474ce3b4345 --- /dev/null +++ b/x-pack/plugins/ml/public/application/model_management/create_pipeline_for_model/pipeline_details.tsx @@ -0,0 +1,183 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC, memo, useState } from 'react'; + +import { + EuiButtonEmpty, + EuiCodeBlock, + EuiFlexGroup, + EuiFlexItem, + EuiForm, + EuiFormRow, + EuiPanel, +} from '@elastic/eui'; + +import { i18n } from '@kbn/i18n'; +import { CodeEditor } from '@kbn/kibana-react-plugin/public'; +import type { SupportedPytorchTasksType } from '@kbn/ml-trained-models-utils'; +import { type InferecePipelineCreationState } from './state'; +import { EDIT_MESSAGE, CANCEL_EDIT_MESSAGE } from '../../components/ml_inference/constants'; +import { isValidJson } from '../../../../common/util/validation_utils'; +import { useTestTrainedModelsContext } from '../test_models/test_trained_models_context'; +import { SaveChangesButton } from '../../components/ml_inference/components/save_changes_button'; +import { validatePipelineProcessors } from '../../components/ml_inference/validation'; +import { PipelineDetailsTitle, PipelineNameAndDescription } from '../../components/shared'; + +interface Props { + handlePipelineConfigUpdate: (configUpdate: Partial) => void; + modelId: string; + pipelineNameError: string | undefined; + pipelineName: string; + pipelineDescription: string; + initialPipelineConfig?: InferecePipelineCreationState['initialPipelineConfig']; + setHasUnsavedChanges: React.Dispatch>; + taskType?: SupportedPytorchTasksType; +} + +export const PipelineDetails: FC = memo( + ({ + handlePipelineConfigUpdate, + modelId, + pipelineName, + pipelineNameError, + pipelineDescription, + initialPipelineConfig, + setHasUnsavedChanges, + taskType, + }) => { + const [isProcessorConfigValid, setIsProcessorConfigValid] = useState(true); + const [processorConfigError, setProcessorConfigError] = useState(); + + const { + currentContext: { pipelineConfig }, + } = useTestTrainedModelsContext(); + const [processorConfigString, setProcessorConfigString] = useState( + JSON.stringify(initialPipelineConfig ?? {}, null, 2) + ); + const [editProcessorConfig, setEditProcessorConfig] = useState(false); + + const updateProcessorConfig = () => { + const invalidProcessorConfigMessage = validatePipelineProcessors( + JSON.parse(processorConfigString), + taskType + ); + if (invalidProcessorConfigMessage === undefined) { + handlePipelineConfigUpdate({ initialPipelineConfig: JSON.parse(processorConfigString) }); + setHasUnsavedChanges(false); + setEditProcessorConfig(false); + setProcessorConfigError(undefined); + } else { + setHasUnsavedChanges(true); + setIsProcessorConfigValid(false); + setProcessorConfigError(invalidProcessorConfigMessage); + } + }; + + const handleProcessorConfigChange = (json: string) => { + setProcessorConfigString(json); + const valid = isValidJson(json); + setIsProcessorConfigValid(valid); + }; + + const resetProcessorConfig = () => { + setProcessorConfigString(JSON.stringify(pipelineConfig, null, 2)); + setIsProcessorConfigValid(true); + setProcessorConfigError(undefined); + }; + + return ( + + + + + + + {/* NAME */} + + {/* NAME and DESCRIPTION */} + + {/* PROCESSOR CONFIGURATION */} + + + { + const editingState = !editProcessorConfig; + if (editingState === false) { + setProcessorConfigError(undefined); + setIsProcessorConfigValid(true); + setHasUnsavedChanges(false); + } + setEditProcessorConfig(editingState); + }} + > + {editProcessorConfig ? CANCEL_EDIT_MESSAGE : EDIT_MESSAGE} + + + + {editProcessorConfig ? ( + + ) : null} + + + {editProcessorConfig ? ( + + {i18n.translate( + 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.steps.advanced.resetInferenceConfigButton', + { defaultMessage: 'Reset' } + )} + + ) : null} + + + } + error={processorConfigError} + isInvalid={processorConfigError !== undefined} + data-test-subj="mlTrainedModelsInferencePipelineInferenceConfigEditor" + > + {editProcessorConfig ? ( + + ) : ( + + {processorConfigString} + + )} + + + + + + ); + } +); diff --git a/x-pack/plugins/ml/public/application/model_management/create_pipeline_for_model/state.ts b/x-pack/plugins/ml/public/application/model_management/create_pipeline_for_model/state.ts new file mode 100644 index 0000000000000..9edcb19e61e38 --- /dev/null +++ b/x-pack/plugins/ml/public/application/model_management/create_pipeline_for_model/state.ts @@ -0,0 +1,42 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { IngestInferenceProcessor } from '@elastic/elasticsearch/lib/api/types'; +import { getDefaultOnFailureConfiguration } from '../../components/ml_inference/state'; +import type { ModelItem } from '../models_list'; + +export interface InferecePipelineCreationState { + creatingPipeline: boolean; + error: boolean; + ignoreFailure: boolean; + modelId: string; + onFailure?: IngestInferenceProcessor['on_failure']; + pipelineName: string; + pipelineNameError?: string; + pipelineDescription: string; + pipelineCreated: boolean; + pipelineError?: string; + initialPipelineConfig?: estypes.IngestPipeline; + takeActionOnFailure: boolean; +} + +export const getInitialState = ( + model: ModelItem, + initialPipelineConfig: estypes.IngestPipeline | undefined +): InferecePipelineCreationState => ({ + creatingPipeline: false, + error: false, + ignoreFailure: false, + modelId: model.model_id, + onFailure: getDefaultOnFailureConfiguration(), + pipelineDescription: `Uses the pre-trained model ${model.model_id} to infer against the data that is being ingested in the pipeline`, + pipelineName: `ml-inference-${model.model_id}`, + pipelineCreated: false, + initialPipelineConfig, + takeActionOnFailure: true, +}); diff --git a/x-pack/plugins/ml/public/application/model_management/create_pipeline_for_model/test_trained_model.tsx b/x-pack/plugins/ml/public/application/model_management/create_pipeline_for_model/test_trained_model.tsx new file mode 100644 index 0000000000000..5f793fa84801e --- /dev/null +++ b/x-pack/plugins/ml/public/application/model_management/create_pipeline_for_model/test_trained_model.tsx @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC } from 'react'; +import { EuiFlexGroup, EuiFlexItem, EuiLink, EuiSpacer, EuiText, EuiTitle } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; + +import { ModelItem } from '../models_list'; +import { TestTrainedModelContent } from '../test_models/test_trained_model_content'; +import { useMlKibana } from '../../contexts/kibana'; +import { type InferecePipelineCreationState } from './state'; + +interface ContentProps { + model: ModelItem; + handlePipelineConfigUpdate: (configUpdate: Partial) => void; + externalPipelineConfig?: estypes.IngestPipeline; +} + +export const TestTrainedModel: FC = ({ + model, + handlePipelineConfigUpdate, + externalPipelineConfig, +}) => { + const { + services: { + docLinks: { links }, + }, + } = useMlKibana(); + + return ( + + + +

+ {i18n.translate( + 'xpack.ml.trainedModels.content.indices.pipelines.addInferencePipelineModal.steps.advanced.testTrainedModelTitle', + { defaultMessage: 'Try it out' } + )} +

+
+ + +

+ +

+

+ + Learn more. + + ), + }} + /> +

+
+
+ + + +
+ ); +}; diff --git a/x-pack/plugins/ml/public/application/model_management/models_list.tsx b/x-pack/plugins/ml/public/application/model_management/models_list.tsx index 0fc27fcb33fd4..dd766d10c36d1 100644 --- a/x-pack/plugins/ml/public/application/model_management/models_list.tsx +++ b/x-pack/plugins/ml/public/application/model_management/models_list.tsx @@ -69,7 +69,7 @@ import { useToastNotificationService } from '../services/toast_notification_serv import { useFieldFormatter } from '../contexts/kibana/use_field_formatter'; import { useRefresh } from '../routing/use_refresh'; import { SavedObjectsWarning } from '../components/saved_objects_warning'; -import { TestTrainedModelFlyout } from './test_models'; +import { TestModelAndPipelineCreationFlyout } from './test_models'; import { TestDfaModelsFlyout } from './test_dfa_models_flyout'; import { AddInferencePipelineFlyout } from '../components/ml_inference'; import { useEnabledFeatures } from '../contexts/ml'; @@ -91,6 +91,8 @@ export type ModelItem = TrainedModelConfigResponse & { modelName?: string; os?: string; arch?: string; + softwareLicense?: string; + licenseUrl?: string; }; export type ModelItemFull = Required; @@ -280,6 +282,8 @@ export const ModelsList: FC = ({ modelName: modelDefinition.modelName, os: modelDefinition.os, arch: modelDefinition.arch, + softwareLicense: modelDefinition.license, + licenseUrl: modelDefinition.licenseUrl, } as ModelItem; }); resultItems = [...resultItems, ...notDownloaded]; @@ -534,7 +538,7 @@ export const ModelsList: FC = ({ content={ } > @@ -815,7 +819,15 @@ export const ModelsList: FC = ({ /> )} {modelToTest === null ? null : ( - + { + setModelToTest(null); + if (refreshList) { + fetchModelsData(); + } + }} + /> )} {dfaModelToTest === null ? null : ( diff --git a/x-pack/plugins/ml/public/application/model_management/test_models/index.ts b/x-pack/plugins/ml/public/application/model_management/test_models/index.ts index 25078a40d4206..4b238f477092e 100644 --- a/x-pack/plugins/ml/public/application/model_management/test_models/index.ts +++ b/x-pack/plugins/ml/public/application/model_management/test_models/index.ts @@ -5,5 +5,5 @@ * 2.0. */ -export { TestTrainedModelFlyout } from './test_flyout'; +export { TestModelAndPipelineCreationFlyout } from './test_model_and_pipeline_creation_flyout'; export { isTestable, isDfaTrainedModel } from './utils'; diff --git a/x-pack/plugins/ml/public/application/model_management/test_models/models/index_input.tsx b/x-pack/plugins/ml/public/application/model_management/test_models/models/index_input.tsx index 03466cd87a16b..3ae11845646ad 100644 --- a/x-pack/plugins/ml/public/application/model_management/test_models/models/index_input.tsx +++ b/x-pack/plugins/ml/public/application/model_management/test_models/models/index_input.tsx @@ -10,7 +10,15 @@ import React, { FC, useState, useMemo, useEffect, useCallback } from 'react'; import useObservable from 'react-use/lib/useObservable'; import { firstValueFrom } from 'rxjs'; import { DataView } from '@kbn/data-views-plugin/common'; -import { EuiSpacer, EuiSelect, EuiFormRow, EuiAccordion, EuiCodeBlock } from '@elastic/eui'; +import { + EuiAccordion, + EuiCode, + EuiCodeBlock, + EuiFormRow, + EuiSpacer, + EuiSelect, + EuiText, +} from '@elastic/eui'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; import { i18n } from '@kbn/i18n'; @@ -21,9 +29,14 @@ import type { InferrerType } from '.'; interface Props { inferrer: InferrerType; data: ReturnType; + disableIndexSelection: boolean; } -export const InferenceInputFormIndexControls: FC = ({ inferrer, data }) => { +export const InferenceInputFormIndexControls: FC = ({ + inferrer, + data, + disableIndexSelection, +}) => { const { dataViewListItems, fieldNames, @@ -40,14 +53,25 @@ export const InferenceInputFormIndexControls: FC = ({ inferrer, data }) = return ( <> - setSelectedDataViewId(e.target.value)} - hasNoInitialSelection={true} - disabled={runningState === RUNNING_STATE.RUNNING} - fullWidth - /> + {disableIndexSelection ? ( + + + {dataViewListItems.find((item) => item.value === selectedDataViewId)?.text} + + + ) : ( + { + inferrer.setSelectedDataViewId(e.target.value); + setSelectedDataViewId(e.target.value); + }} + hasNoInitialSelection={true} + disabled={runningState === RUNNING_STATE.RUNNING} + fullWidth + /> + )} = ({ inferrer, data }) = setSelectedField(e.target.value)} + onChange={(e) => { + setSelectedField(e.target.value); + }} hasNoInitialSelection={true} disabled={runningState === RUNNING_STATE.RUNNING} fullWidth @@ -79,7 +105,14 @@ export const InferenceInputFormIndexControls: FC = ({ inferrer, data }) = } )} > - + {JSON.stringify(pipeline, null, 2)} @@ -87,7 +120,13 @@ export const InferenceInputFormIndexControls: FC = ({ inferrer, data }) = ); }; -export function useIndexInput({ inferrer }: { inferrer: InferrerType }) { +export function useIndexInput({ + inferrer, + defaultSelectedDataViewId, +}: { + inferrer: InferrerType; + defaultSelectedDataViewId?: string; +}) { const { services: { data: { @@ -100,7 +139,9 @@ export function useIndexInput({ inferrer }: { inferrer: InferrerType }) { const [dataViewListItems, setDataViewListItems] = useState< Array<{ value: string; text: string }> >([]); - const [selectedDataViewId, setSelectedDataViewId] = useState(undefined); + const [selectedDataViewId, setSelectedDataViewId] = useState( + defaultSelectedDataViewId + ); const [selectedDataView, setSelectedDataView] = useState(null); const [fieldNames, setFieldNames] = useState>([]); const selectedField = useObservable(inferrer.getInputField$(), inferrer.getInputField()); @@ -197,11 +238,20 @@ export function useIndexInput({ inferrer }: { inferrer: InferrerType }) { })); setFieldNames(tempFieldNames); - const fieldName = tempFieldNames.length === 1 ? tempFieldNames[0].value : undefined; + const defaultSelectedField = inferrer.getInputField(); + + const fieldName = + defaultSelectedField && + tempFieldNames.find((field) => field.value === defaultSelectedField) + ? defaultSelectedField + : tempFieldNames[0].value; + // Only set a field if it's the default field + // if (inferrer.getInputField() === DEFAULT_INPUT_FIELD) { inferrer.setInputField(fieldName); + // } } }, - [selectedDataView, inferrer] + [selectedDataView, inferrer] // defaultSelectedField ); useEffect( diff --git a/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_base.ts b/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_base.ts index 8dc0bf6b88815..bdd082cf8ca04 100644 --- a/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_base.ts +++ b/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_base.ts @@ -31,7 +31,7 @@ export type InferenceOptions = | estypes.MlTextEmbeddingInferenceOptions | estypes.MlQuestionAnsweringInferenceUpdateOptions; -const DEFAULT_INPUT_FIELD = 'text_field'; +export const DEFAULT_INPUT_FIELD = 'text_field'; export const DEFAULT_INFERENCE_TIME_OUT = '30s'; export type FormattedNerResponse = Array<{ @@ -72,8 +72,10 @@ export abstract class InferenceBase { private isValid$ = new BehaviorSubject(false); private pipeline$ = new BehaviorSubject({}); private supportedFieldTypes: ES_FIELD_TYPES[] = [ES_FIELD_TYPES.TEXT]; + private selectedDataViewId: string | undefined; protected readonly info: string[] = []; + public switchToCreationMode?: () => void; private subscriptions$: Subscription = new Subscription(); @@ -87,8 +89,13 @@ export abstract class InferenceBase { this.inputField$.next(this.modelInputField); } + public setSwitchtoCreationMode(callback: () => void) { + this.switchToCreationMode = callback; + } + public destroy() { this.subscriptions$.unsubscribe(); + this.pipeline$.unsubscribe(); } protected initialize( @@ -162,6 +169,15 @@ export abstract class InferenceBase { this.runningState$.next(RUNNING_STATE.STOPPED); } + public setSelectedDataViewId(dataViewId: string) { + // Data view selected for testing + this.selectedDataViewId = dataViewId; + } + + public getSelectedDataViewId() { + return this.selectedDataViewId; + } + public setInputField(field: string | undefined) { // if the field is not set, change to be the same as the model input field this.inputField$.next(field === undefined ? this.modelInputField : field); diff --git a/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_input_form/index_input.tsx b/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_input_form/index_input.tsx index 4dbe900283657..4b87ddccfb4b2 100644 --- a/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_input_form/index_input.tsx +++ b/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_input_form/index_input.tsx @@ -11,7 +11,6 @@ import useObservable from 'react-use/lib/useObservable'; import { EuiSpacer, - EuiButton, EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, @@ -28,13 +27,22 @@ import { ErrorMessage } from '../../inference_error'; import type { InferrerType } from '..'; import { useIndexInput, InferenceInputFormIndexControls } from '../index_input'; import { RUNNING_STATE } from '../inference_base'; +import { InputFormControls } from './input_form_controls'; +import { useTestTrainedModelsContext } from '../../test_trained_models_context'; interface Props { inferrer: InferrerType; } export const IndexInputForm: FC = ({ inferrer }) => { - const data = useIndexInput({ inferrer }); + const { + currentContext: { defaultSelectedDataViewId, createPipelineFlyoutOpen }, + } = useTestTrainedModelsContext(); + + const data = useIndexInput({ + inferrer, + defaultSelectedDataViewId, + }); const { reloadExamples, selectedField } = data; const [errorText, setErrorText] = useState(null); @@ -60,23 +68,26 @@ export const IndexInputForm: FC = ({ inferrer }) => { return ( <>{infoComponent} - + - - + - + {runningState === RUNNING_STATE.RUNNING ? : null} diff --git a/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_input_form/input_form_controls.tsx b/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_input_form/input_form_controls.tsx new file mode 100644 index 0000000000000..18cacbb745504 --- /dev/null +++ b/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_input_form/input_form_controls.tsx @@ -0,0 +1,63 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC } from 'react'; + +import { EuiButton, EuiButtonEmpty, EuiFlexItem } from '@elastic/eui'; + +import { FormattedMessage } from '@kbn/i18n-react'; +import type { InferrerType } from '..'; + +interface Props { + testButtonDisabled: boolean; + createPipelineButtonDisabled: boolean; + inferrer: InferrerType; + showCreatePipelineButton?: boolean; +} + +export const InputFormControls: FC = ({ + testButtonDisabled, + createPipelineButtonDisabled, + inferrer, + showCreatePipelineButton, +}) => { + return ( + <> + + + + + + {showCreatePipelineButton ? ( + + { + if (inferrer.switchToCreationMode) { + inferrer.switchToCreationMode(); + } + }} + > + + + + ) : null} + + ); +}; diff --git a/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_input_form/text_input.tsx b/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_input_form/text_input.tsx index fc162a305c32b..ae5a4c72cac60 100644 --- a/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_input_form/text_input.tsx +++ b/x-pack/plugins/ml/public/application/model_management/test_models/models/inference_input_form/text_input.tsx @@ -8,7 +8,7 @@ import React, { FC, useState, useMemo, useCallback, FormEventHandler } from 'react'; import useObservable from 'react-use/lib/useObservable'; -import { EuiSpacer, EuiButton, EuiTabs, EuiTab, EuiForm } from '@elastic/eui'; +import { EuiFlexGroup, EuiSpacer, EuiTabs, EuiTab, EuiForm } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; import { extractErrorMessage } from '@kbn/ml-error-utils'; @@ -18,6 +18,7 @@ import type { InferrerType } from '..'; import { OutputLoadingContent } from '../../output_loading'; import { RUNNING_STATE } from '../inference_base'; import { RawOutput } from '../raw_output'; +import { InputFormControls } from './input_form_controls'; interface Props { inferrer: InferrerType; @@ -57,17 +58,15 @@ export const TextInputForm: FC = ({ inferrer }) => { <>{inputComponent}
- - + - +
{runningState !== RUNNING_STATE.STOPPED ? ( <> diff --git a/x-pack/plugins/ml/public/application/model_management/test_models/selected_model.tsx b/x-pack/plugins/ml/public/application/model_management/test_models/selected_model.tsx index 4747d9c149186..80ee311d533a8 100644 --- a/x-pack/plugins/ml/public/application/model_management/test_models/selected_model.tsx +++ b/x-pack/plugins/ml/public/application/model_management/test_models/selected_model.tsx @@ -7,6 +7,7 @@ import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import React, { FC, useMemo, useEffect } from 'react'; +import { cloneDeep } from 'lodash'; import { TRAINED_MODEL_TYPE, SUPPORTED_PYTORCH_TASKS } from '@kbn/ml-trained-models-utils'; import { NerInference } from './models/ner'; @@ -22,52 +23,182 @@ import { import { TextEmbeddingInference } from './models/text_embedding'; import { useMlApiContext } from '../../contexts/kibana'; +import { type TestTrainedModelsContextType } from './test_trained_models_context'; import { InferenceInputForm } from './models/inference_input_form'; import { InferrerType } from './models'; import { INPUT_TYPE } from './models/inference_base'; import { TextExpansionInference } from './models/text_expansion'; +import { type InferecePipelineCreationState } from '../create_pipeline_for_model/state'; +import { + getInferencePropertiesFromPipelineConfig, + isMlIngestInferenceProcessor, + isMlInferencePipelineInferenceConfig, +} from '../create_pipeline_for_model/get_inference_properties_from_pipeline_config'; interface Props { model: estypes.MlTrainedModelConfig; inputType: INPUT_TYPE; deploymentId: string; + handlePipelineConfigUpdate?: (configUpdate: Partial) => void; + externalPipelineConfig?: estypes.IngestPipeline; + setCurrentContext?: React.Dispatch; } -export const SelectedModel: FC = ({ model, inputType, deploymentId }) => { +export const SelectedModel: FC = ({ + model, + inputType, + deploymentId, + handlePipelineConfigUpdate, + externalPipelineConfig, + setCurrentContext, +}) => { const { trainedModels } = useMlApiContext(); const inferrer = useMemo(() => { - if (model.model_type === TRAINED_MODEL_TYPE.PYTORCH) { - const taskType = Object.keys(model.inference_config ?? {})[0]; + const taskType = Object.keys(model.inference_config ?? {})[0]; + let tempInferrer: InferrerType | undefined; + const pipelineConfigValues = externalPipelineConfig + ? getInferencePropertiesFromPipelineConfig(taskType, externalPipelineConfig) + : null; + if (model.model_type === TRAINED_MODEL_TYPE.PYTORCH) { switch (taskType) { case SUPPORTED_PYTORCH_TASKS.NER: - return new NerInference(trainedModels, model, inputType, deploymentId); + tempInferrer = new NerInference(trainedModels, model, inputType, deploymentId); + break; case SUPPORTED_PYTORCH_TASKS.TEXT_CLASSIFICATION: - return new TextClassificationInference(trainedModels, model, inputType, deploymentId); + tempInferrer = new TextClassificationInference( + trainedModels, + model, + inputType, + deploymentId + ); + break; case SUPPORTED_PYTORCH_TASKS.ZERO_SHOT_CLASSIFICATION: - return new ZeroShotClassificationInference(trainedModels, model, inputType, deploymentId); + tempInferrer = new ZeroShotClassificationInference( + trainedModels, + model, + inputType, + deploymentId + ); + if (pipelineConfigValues) { + const { labels, multi_label: multiLabel } = pipelineConfigValues; + if (labels && multiLabel !== undefined) { + tempInferrer.setLabelsText(Array.isArray(labels) ? labels.join(',') : labels); + tempInferrer.setMultiLabel(Boolean(multiLabel)); + } + } + break; case SUPPORTED_PYTORCH_TASKS.TEXT_EMBEDDING: - return new TextEmbeddingInference(trainedModels, model, inputType, deploymentId); + tempInferrer = new TextEmbeddingInference(trainedModels, model, inputType, deploymentId); + break; case SUPPORTED_PYTORCH_TASKS.FILL_MASK: - return new FillMaskInference(trainedModels, model, inputType, deploymentId); + tempInferrer = new FillMaskInference(trainedModels, model, inputType, deploymentId); + break; case SUPPORTED_PYTORCH_TASKS.QUESTION_ANSWERING: - return new QuestionAnsweringInference(trainedModels, model, inputType, deploymentId); + tempInferrer = new QuestionAnsweringInference( + trainedModels, + model, + inputType, + deploymentId + ); + if (pipelineConfigValues?.question) { + tempInferrer.setQuestionText(pipelineConfigValues.question); + } + break; case SUPPORTED_PYTORCH_TASKS.TEXT_EXPANSION: - return new TextExpansionInference(trainedModels, model, inputType, deploymentId); + tempInferrer = new TextExpansionInference(trainedModels, model, inputType, deploymentId); + break; default: break; } } else if (model.model_type === TRAINED_MODEL_TYPE.LANG_IDENT) { - return new LangIdentInference(trainedModels, model, inputType, deploymentId); + tempInferrer = new LangIdentInference(trainedModels, model, inputType, deploymentId); + } + if (tempInferrer) { + if (pipelineConfigValues) { + tempInferrer.setInputField(pipelineConfigValues.inputField); + } + if (externalPipelineConfig === undefined) { + tempInferrer.setSwitchtoCreationMode(() => { + if (tempInferrer && setCurrentContext) { + setCurrentContext({ + pipelineConfig: tempInferrer.getPipeline(), + defaultSelectedDataViewId: tempInferrer.getSelectedDataViewId(), + createPipelineFlyoutOpen: true, + }); + } + }); + } else { + tempInferrer?.getPipeline$().subscribe((testPipeline) => { + if (handlePipelineConfigUpdate && testPipeline && externalPipelineConfig) { + const { + fieldMap: testFieldMap, + inferenceConfig: testInferenceConfig, + labels, + multi_label: multiLabel, + question, + } = getInferencePropertiesFromPipelineConfig(taskType, testPipeline); + + const updatedPipeline = cloneDeep(externalPipelineConfig); + const { inferenceObj: externalInference, inferenceConfig: externalInferenceConfig } = + getInferencePropertiesFromPipelineConfig(taskType, updatedPipeline); + + if (externalInference) { + // Always update target field change + externalInference.field_map = testFieldMap; + + if (externalInferenceConfig === undefined) { + externalInference.inference_config = testInferenceConfig; + } else if ( + isMlIngestInferenceProcessor(externalInference) && + isMlInferencePipelineInferenceConfig(externalInference.inference_config) + ) { + // Only update the properties that change in the test step to avoid overwriting user edits + if ( + taskType === SUPPORTED_PYTORCH_TASKS.ZERO_SHOT_CLASSIFICATION && + labels && + multiLabel !== undefined + ) { + const external = + externalInference.inference_config[ + SUPPORTED_PYTORCH_TASKS.ZERO_SHOT_CLASSIFICATION + ]; + + if (external) { + external.multi_label = multiLabel; + external.labels = labels; + } + } else if ( + taskType === SUPPORTED_PYTORCH_TASKS.QUESTION_ANSWERING && + question !== undefined + ) { + const external = + externalInference.inference_config[SUPPORTED_PYTORCH_TASKS.QUESTION_ANSWERING]; + + if (external) { + external.question = question; + } + } + } + } + + handlePipelineConfigUpdate({ + initialPipelineConfig: updatedPipeline, + }); + } + }); + } } - }, [inputType, model, trainedModels, deploymentId]); + return tempInferrer; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [inputType, model, trainedModels, deploymentId, setCurrentContext]); useEffect(() => { return () => { inferrer?.destroy(); }; - }, [inferrer]); + }, [inferrer, model.model_id]); if (inferrer !== undefined) { return ; diff --git a/x-pack/plugins/ml/public/application/model_management/test_models/test_flyout.tsx b/x-pack/plugins/ml/public/application/model_management/test_models/test_flyout.tsx index 434621d11773b..f595fd1de35d7 100644 --- a/x-pack/plugins/ml/public/application/model_management/test_models/test_flyout.tsx +++ b/x-pack/plugins/ml/public/application/model_management/test_models/test_flyout.tsx @@ -5,126 +5,36 @@ * 2.0. */ -import React, { FC, useState, useMemo } from 'react'; +import React, { FC } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; -import { - EuiFlyout, - EuiFlyoutBody, - EuiFlyoutHeader, - EuiFormRow, - EuiSelect, - EuiSpacer, - EuiTab, - EuiTabs, - EuiTitle, - useEuiPaddingSize, -} from '@elastic/eui'; +import { EuiFlyout, EuiFlyoutBody, EuiFlyoutHeader, EuiSpacer, EuiTitle } from '@elastic/eui'; -import { SUPPORTED_PYTORCH_TASKS } from '@kbn/ml-trained-models-utils'; -import { SelectedModel } from './selected_model'; -import { INPUT_TYPE } from './models/inference_base'; import { type ModelItem } from '../models_list'; +import { TestTrainedModelContent } from './test_trained_model_content'; interface Props { model: ModelItem; onClose: () => void; } -export const TestTrainedModelFlyout: FC = ({ model, onClose }) => { - const [deploymentId, setDeploymentId] = useState(model.deployment_ids[0]); - const mediumPadding = useEuiPaddingSize('m'); - - const [inputType, setInputType] = useState(INPUT_TYPE.TEXT); - - const onlyShowTab: INPUT_TYPE | undefined = useMemo(() => { - return (model.type ?? []).includes(SUPPORTED_PYTORCH_TASKS.TEXT_EXPANSION) - ? INPUT_TYPE.INDEX - : undefined; - }, [model]); - - return ( - <> - - - -

- -

-
- - -

{model.model_id}

-
-
- - {model.deployment_ids.length > 1 ? ( - <> - - } - > - { - return { text: v, value: v }; - })} - value={deploymentId} - onChange={(e) => { - setDeploymentId(e.target.value); - }} - /> - - - - ) : null} - - {onlyShowTab === undefined ? ( - <> - - setInputType(INPUT_TYPE.TEXT)} - > - - - setInputType(INPUT_TYPE.INDEX)} - > - - - - - - - ) : null} - - = ({ model, onClose }) => ( + + + +

+ - - - - ); -}; +

+
+ + +

{model.model_id}

+
+
+ + + +
+); diff --git a/x-pack/plugins/ml/public/application/model_management/test_models/test_model_and_pipeline_creation_flyout.tsx b/x-pack/plugins/ml/public/application/model_management/test_models/test_model_and_pipeline_creation_flyout.tsx new file mode 100644 index 0000000000000..33c4e69df59b6 --- /dev/null +++ b/x-pack/plugins/ml/public/application/model_management/test_models/test_model_and_pipeline_creation_flyout.tsx @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC, useState } from 'react'; + +import { + type TestTrainedModelsContextType, + TestTrainedModelsContext, +} from './test_trained_models_context'; +import type { ModelItem } from '../models_list'; +import { TestTrainedModelFlyout } from './test_flyout'; +import { CreatePipelineForModelFlyout } from '../create_pipeline_for_model/create_pipeline_for_model_flyout'; + +interface Props { + model: ModelItem; + onClose: (refreshList?: boolean) => void; +} +export const TestModelAndPipelineCreationFlyout: FC = ({ model, onClose }) => { + const [currentContext, setCurrentContext] = useState({ + pipelineConfig: undefined, + createPipelineFlyoutOpen: false, + }); + + return ( + + {currentContext.createPipelineFlyoutOpen === false ? ( + + ) : ( + + )} + + ); +}; diff --git a/x-pack/plugins/ml/public/application/model_management/test_models/test_trained_model_content.tsx b/x-pack/plugins/ml/public/application/model_management/test_models/test_trained_model_content.tsx new file mode 100644 index 0000000000000..52665343cb351 --- /dev/null +++ b/x-pack/plugins/ml/public/application/model_management/test_models/test_trained_model_content.tsx @@ -0,0 +1,115 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { FC, useState, useMemo } from 'react'; + +import { SUPPORTED_PYTORCH_TASKS } from '@kbn/ml-trained-models-utils'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { EuiFormRow, EuiSelect, EuiSpacer, EuiTab, EuiTabs, useEuiPaddingSize } from '@elastic/eui'; +import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { SelectedModel } from './selected_model'; +import { type ModelItem } from '../models_list'; +import { INPUT_TYPE } from './models/inference_base'; +import { useTestTrainedModelsContext } from './test_trained_models_context'; +import { type InferecePipelineCreationState } from '../create_pipeline_for_model/state'; + +interface ContentProps { + model: ModelItem; + handlePipelineConfigUpdate?: (configUpdate: Partial) => void; + externalPipelineConfig?: estypes.IngestPipeline; +} + +export const TestTrainedModelContent: FC = ({ + model, + handlePipelineConfigUpdate, + externalPipelineConfig, +}) => { + const [deploymentId, setDeploymentId] = useState(model.deployment_ids[0]); + const mediumPadding = useEuiPaddingSize('m'); + + const [inputType, setInputType] = useState(INPUT_TYPE.TEXT); + const { + currentContext: { createPipelineFlyoutOpen }, + setCurrentContext, + } = useTestTrainedModelsContext(); + + const onlyShowTab: INPUT_TYPE | undefined = useMemo(() => { + return (model.type ?? []).includes(SUPPORTED_PYTORCH_TASKS.TEXT_EXPANSION) || + createPipelineFlyoutOpen + ? INPUT_TYPE.INDEX + : undefined; + }, [model, createPipelineFlyoutOpen]); + return ( + <> + {' '} + {model.deployment_ids.length > 1 ? ( + <> + + } + > + { + return { text: v, value: v }; + })} + value={deploymentId} + onChange={(e) => { + setDeploymentId(e.target.value); + }} + /> + + + + ) : null} + {onlyShowTab === undefined ? ( + <> + + setInputType(INPUT_TYPE.TEXT)} + > + + + setInputType(INPUT_TYPE.INDEX)} + > + + + + + + + ) : null} + + + ); +}; diff --git a/x-pack/plugins/ml/public/application/model_management/test_models/test_trained_models_context.tsx b/x-pack/plugins/ml/public/application/model_management/test_models/test_trained_models_context.tsx new file mode 100644 index 0000000000000..5db45a334b16a --- /dev/null +++ b/x-pack/plugins/ml/public/application/model_management/test_models/test_trained_models_context.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 { createContext, Dispatch, useContext } from 'react'; +import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; + +export interface TestTrainedModelsContextType { + pipelineConfig?: estypes.IngestPipeline; + createPipelineFlyoutOpen: boolean; + defaultSelectedDataViewId?: string; +} +export const TestTrainedModelsContext = createContext< + | { + currentContext: TestTrainedModelsContextType; + setCurrentContext: Dispatch; + } + | undefined +>(undefined); + +export function useTestTrainedModelsContext() { + const testTrainedModelsContext = useContext(TestTrainedModelsContext); + + if (testTrainedModelsContext === undefined) { + throw new Error('TestTrainedModelsContext has not been initialized.'); + } + + return testTrainedModelsContext; +} diff --git a/x-pack/plugins/ml/server/lib/alerts/register_anomaly_detection_alert_type.ts b/x-pack/plugins/ml/server/lib/alerts/register_anomaly_detection_alert_type.ts index ad536027a6619..2dadf5e510268 100644 --- a/x-pack/plugins/ml/server/lib/alerts/register_anomaly_detection_alert_type.ts +++ b/x-pack/plugins/ml/server/lib/alerts/register_anomaly_detection_alert_type.ts @@ -166,7 +166,7 @@ export function registerAnomalyDetectionAlertType({ >({ id: ML_ALERT_TYPES.ANOMALY_DETECTION, name: i18n.translate('xpack.ml.anomalyDetectionAlert.name', { - defaultMessage: 'Anomaly detection alert', + defaultMessage: 'Anomaly detection', }), actionGroups: [THRESHOLD_MET_GROUP], defaultActionGroupId: ANOMALY_SCORE_MATCH_GROUP_ID, diff --git a/x-pack/plugins/ml/server/models/model_management/model_provider.test.ts b/x-pack/plugins/ml/server/models/model_management/model_provider.test.ts index ff18327fdea5e..48c80ceca1a95 100644 --- a/x-pack/plugins/ml/server/models/model_management/model_provider.test.ts +++ b/x-pack/plugins/ml/server/models/model_management/model_provider.test.ts @@ -87,6 +87,7 @@ describe('modelsProvider', () => { version: 1, modelName: 'e5', license: 'MIT', + licenseUrl: 'https://huggingface.co/elastic/multilingual-e5-small', type: ['pytorch', 'text_embedding'], }, { @@ -100,6 +101,7 @@ describe('modelsProvider', () => { version: 1, modelName: 'e5', license: 'MIT', + licenseUrl: 'https://huggingface.co/elastic/multilingual-e5-small_linux-x86_64', type: ['pytorch', 'text_embedding'], }, ]); @@ -167,6 +169,7 @@ describe('modelsProvider', () => { modelName: 'e5', type: ['pytorch', 'text_embedding'], license: 'MIT', + licenseUrl: 'https://huggingface.co/elastic/multilingual-e5-small', }, { arch: 'amd64', @@ -179,6 +182,7 @@ describe('modelsProvider', () => { modelName: 'e5', type: ['pytorch', 'text_embedding'], license: 'MIT', + licenseUrl: 'https://huggingface.co/elastic/multilingual-e5-small_linux-x86_64', }, ]); }); diff --git a/x-pack/plugins/monitoring/common/types/alerts.ts b/x-pack/plugins/monitoring/common/types/alerts.ts index adf00789d4056..d00cc90c5516b 100644 --- a/x-pack/plugins/monitoring/common/types/alerts.ts +++ b/x-pack/plugins/monitoring/common/types/alerts.ts @@ -169,9 +169,10 @@ export interface AlertNodeStats { } export interface AlertCpuUsageNodeStats extends AlertNodeStats { - cpuUsage?: number; - limitsChanged?: boolean; - unexpectedLimits?: boolean; + cpuUsage: number; + containerUsage: number; + containerPeriods: number; + containerQuota: number; } export interface AlertThreadPoolRejectionsStats { diff --git a/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/__snapshots__/cells.test.js.snap b/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/__snapshots__/cells.test.js.snap index b5cc6f0e7ecc0..aadc12d246169 100644 --- a/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/__snapshots__/cells.test.js.snap +++ b/x-pack/plugins/monitoring/public/components/elasticsearch/nodes/__snapshots__/cells.test.js.snap @@ -26,26 +26,22 @@ exports[`Node Listing Metric Cell should format a non-percentage metric 1`] = ` class="euiFlexItem emotion-euiFlexItem-growZero" >
-
- -
+

-
- -
+
{ expect(rule.ruleOptions.throttle).toBe('1d'); expect(rule.ruleOptions.defaultParams).toStrictEqual({ threshold: 85, duration: '5m' }); expect(rule.ruleOptions.actionVariables).toStrictEqual([ - { name: 'node', description: 'The node reporting high CPU usage.' }, + { name: 'node', description: 'The node reporting high cpu usage.' }, { name: 'internalShortMessage', description: 'The short internal message generated by Elastic.', @@ -114,7 +114,7 @@ describe('CpuUsageRule', () => { getState.mockReset(); }); - it('should fire actions when threshold is exceeded', async () => { + it('should fire actions', async () => { const rule = new CpuUsageRule(); const type = rule.getRuleType(); await type.executor({ @@ -122,7 +122,6 @@ describe('CpuUsageRule', () => { params: rule.ruleOptions.defaultParams, } as any); const count = 1; - const threshold = rule.ruleOptions.defaultParams?.threshold; expect(replaceState).toHaveBeenCalledWith({ alertStates: [ { @@ -135,14 +134,13 @@ describe('CpuUsageRule', () => { cpuUsage, nodeId, nodeName, - threshold, }, nodeId, nodeName, ui: { isFiring: true, message: { - text: `Node #start_link${nodeName}#end_link is reporting CPU usage of ${cpuUsage}% which is above the configured threshold of ${threshold}%. Last checked at #absolute`, + text: `Node #start_link${nodeName}#end_link is reporting cpu usage of ${cpuUsage}% at #absolute`, nextSteps: [ { text: '#start_linkCheck hot threads#end_link', @@ -170,12 +168,6 @@ describe('CpuUsageRule', () => { }, ], tokens: [ - { - startToken: '#start_link', - endToken: '#end_link', - type: 'link', - url: 'elasticsearch/nodes/myNodeId', - }, { startToken: '#absolute', type: 'time', @@ -183,6 +175,12 @@ describe('CpuUsageRule', () => { isRelative: false, timestamp: 1, }, + { + startToken: '#start_link', + endToken: '#end_link', + type: 'link', + url: 'elasticsearch/nodes/myNodeId', + }, ], }, severity: 'danger', @@ -193,10 +191,10 @@ describe('CpuUsageRule', () => { ], }); expect(scheduleActions).toHaveBeenCalledWith('default', { - internalFullMessage: `CPU usage alert is firing for node ${nodeName} in cluster ${clusterName}. [View node](http://localhost:5601/app/monitoring#/elasticsearch/nodes/${nodeId}?_g=(cluster_uuid:${clusterUuid}))`, - internalShortMessage: `CPU usage alert is firing for node ${nodeName} in cluster ${clusterName}. Verify CPU usage of node.`, + internalFullMessage: `CPU usage alert is firing for node ${nodeName} in cluster: ${clusterName}. [View node](http://localhost:5601/app/monitoring#/elasticsearch/nodes/${nodeId}?_g=(cluster_uuid:${clusterUuid}))`, + internalShortMessage: `CPU usage alert is firing for node ${nodeName} in cluster: ${clusterName}. Verify CPU level of node.`, action: `[View node](http://localhost:5601/app/monitoring#/elasticsearch/nodes/${nodeId}?_g=(cluster_uuid:${clusterUuid}))`, - actionPlain: 'Verify CPU usage of node.', + actionPlain: 'Verify CPU level of node.', clusterName, count, nodes: `${nodeName}:${cpuUsage}`, @@ -244,10 +242,10 @@ describe('CpuUsageRule', () => { } as any); const count = 1; expect(scheduleActions).toHaveBeenCalledWith('default', { - internalFullMessage: `CPU usage alert is firing for node ${nodeName} in cluster ${clusterName}. [View node](http://localhost:5601/app/monitoring#/elasticsearch/nodes/${nodeId}?_g=(cluster_uuid:${clusterUuid},ccs:${ccs}))`, - internalShortMessage: `CPU usage alert is firing for node ${nodeName} in cluster ${clusterName}. Verify CPU usage of node.`, + internalFullMessage: `CPU usage alert is firing for node ${nodeName} in cluster: ${clusterName}. [View node](http://localhost:5601/app/monitoring#/elasticsearch/nodes/${nodeId}?_g=(cluster_uuid:${clusterUuid},ccs:${ccs}))`, + internalShortMessage: `CPU usage alert is firing for node ${nodeName} in cluster: ${clusterName}. Verify CPU level of node.`, action: `[View node](http://localhost:5601/app/monitoring#/elasticsearch/nodes/${nodeId}?_g=(cluster_uuid:${clusterUuid},ccs:testCluster))`, - actionPlain: 'Verify CPU usage of node.', + actionPlain: 'Verify CPU level of node.', clusterName, count, nodes: `${nodeName}:${cpuUsage}`, @@ -255,324 +253,5 @@ describe('CpuUsageRule', () => { state: 'firing', }); }); - - it('should fire actions when resource limits are missing', async () => { - (fetchCpuUsageNodeStats as jest.Mock).mockImplementation(() => { - return [stat]; - }); - - const rule = new CpuUsageRule(); - const type = rule.getRuleType(); - await type.executor({ - ...executorOptions, - params: rule.ruleOptions.defaultParams, - } as any); - const count = 1; - const threshold = rule.ruleOptions.defaultParams?.threshold; - expect(replaceState).toHaveBeenCalledWith({ - alertStates: [ - { - ccs: undefined, - cluster: { clusterUuid, clusterName }, - cpuUsage, - itemLabel: undefined, - meta: { - clusterUuid, - cpuUsage, - nodeId, - nodeName, - threshold, - }, - nodeId, - nodeName, - ui: { - isFiring: true, - message: { - text: `Node #start_link${nodeName}#end_link is reporting CPU usage of ${cpuUsage}% which is above the configured threshold of ${threshold}%. Last checked at #absolute`, - nextSteps: [ - { - text: '#start_linkCheck hot threads#end_link', - tokens: [ - { - startToken: '#start_link', - endToken: '#end_link', - type: 'docLink', - partialUrl: - '{elasticWebsiteUrl}guide/en/elasticsearch/reference/{docLinkVersion}/cluster-nodes-hot-threads.html', - }, - ], - }, - { - text: '#start_linkCheck long running tasks#end_link', - tokens: [ - { - startToken: '#start_link', - endToken: '#end_link', - type: 'docLink', - partialUrl: - '{elasticWebsiteUrl}guide/en/elasticsearch/reference/{docLinkVersion}/tasks.html', - }, - ], - }, - ], - tokens: [ - { - startToken: '#start_link', - endToken: '#end_link', - type: 'link', - url: 'elasticsearch/nodes/myNodeId', - }, - { - startToken: '#absolute', - type: 'time', - isAbsolute: true, - isRelative: false, - timestamp: 1, - }, - ], - }, - severity: 'danger', - triggeredMS: 1, - lastCheckedMS: 0, - }, - }, - ], - }); - expect(scheduleActions).toHaveBeenCalledWith('default', { - internalFullMessage: `CPU usage alert is firing for node ${nodeName} in cluster ${clusterName}. [View node](http://localhost:5601/app/monitoring#/elasticsearch/nodes/${nodeId}?_g=(cluster_uuid:${clusterUuid}))`, - internalShortMessage: `CPU usage alert is firing for node ${nodeName} in cluster ${clusterName}. Verify CPU usage of node.`, - action: `[View node](http://localhost:5601/app/monitoring#/elasticsearch/nodes/${nodeId}?_g=(cluster_uuid:${clusterUuid}))`, - actionPlain: 'Verify CPU usage of node.', - clusterName, - count, - nodes: `${nodeName}:${cpuUsage}`, - node: `${nodeName}:${cpuUsage}`, - state: 'firing', - }); - }); - - it('should fire actions when resource limits have changed', async () => { - (fetchCpuUsageNodeStats as jest.Mock).mockImplementation(() => { - return [ - { - ...stat, - limitsChanged: true, - }, - ]; - }); - - const rule = new CpuUsageRule(); - const type = rule.getRuleType(); - await type.executor({ - ...executorOptions, - params: rule.ruleOptions.defaultParams, - } as any); - const count = 1; - const threshold = rule.ruleOptions.defaultParams?.threshold; - expect(replaceState).toHaveBeenCalledWith({ - alertStates: [ - { - ccs: undefined, - cluster: { clusterUuid, clusterName }, - cpuUsage, - itemLabel: undefined, - meta: { - clusterUuid, - cpuUsage, - nodeId, - nodeName, - threshold, - limitsChanged: true, - }, - nodeId, - nodeName, - ui: { - isFiring: true, - message: { - text: 'Resource limits for node #start_linkmyNodeName#end_link has changed within the look back window, unable to confidently calculate CPU usage for alerting. Please monitor the usage until the window has moved. Last checked at #absolute', - tokens: [ - { - startToken: '#start_link', - endToken: '#end_link', - type: 'link', - url: 'elasticsearch/nodes/myNodeId', - }, - { - startToken: '#absolute', - type: 'time', - isAbsolute: true, - isRelative: false, - timestamp: 1, - }, - ], - }, - severity: 'danger', - triggeredMS: 1, - lastCheckedMS: 0, - }, - }, - ], - }); - expect(scheduleActions).toHaveBeenCalledWith('default', { - internalFullMessage: `CPU usage alert for node ${nodeName} in cluster ${clusterName} faced issues while evaluating the usage. [View node](http://localhost:5601/app/monitoring#/elasticsearch/nodes/${nodeId}?_g=(cluster_uuid:${clusterUuid}))`, - internalShortMessage: `CPU usage alert for node ${nodeName} in cluster ${clusterName} faced issues while evaluating the usage. Verify CPU usage of node.`, - action: `[View node](http://localhost:5601/app/monitoring#/elasticsearch/nodes/${nodeId}?_g=(cluster_uuid:${clusterUuid}))`, - actionPlain: 'Verify CPU usage of node.', - clusterName, - count, - nodes: `${nodeName}:${cpuUsage}`, - node: `${nodeName}:${cpuUsage}`, - state: 'firing', - }); - }); - - it('should fire actions when resource limits are set but not expected', async () => { - (fetchCpuUsageNodeStats as jest.Mock).mockImplementation(() => { - return [ - { - ...stat, - unexpectedLimits: true, - }, - ]; - }); - - const rule = new CpuUsageRule(); - const type = rule.getRuleType(); - await type.executor({ - ...executorOptions, - params: rule.ruleOptions.defaultParams, - } as any); - const count = 1; - const threshold = rule.ruleOptions.defaultParams?.threshold; - expect(replaceState).toHaveBeenCalledWith({ - alertStates: [ - { - ccs: undefined, - cluster: { clusterUuid, clusterName }, - cpuUsage, - itemLabel: undefined, - meta: { - clusterUuid, - cpuUsage, - nodeId, - nodeName, - threshold, - unexpectedLimits: true, - }, - nodeId, - nodeName, - ui: { - isFiring: true, - message: { - text: `Kibana is configured for non-containerized workloads but node #start_linkmyNodeName#end_link has resource limits configured. Node reports usage of ${cpuUsage}%. Last checked at #absolute`, - tokens: [ - { - startToken: '#start_link', - endToken: '#end_link', - type: 'link', - url: 'elasticsearch/nodes/myNodeId', - }, - { - startToken: '#absolute', - type: 'time', - isAbsolute: true, - isRelative: false, - timestamp: 1, - }, - ], - }, - severity: 'danger', - triggeredMS: 1, - lastCheckedMS: 0, - }, - }, - ], - }); - expect(scheduleActions).toHaveBeenCalledWith('default', { - internalFullMessage: `CPU usage alert for node ${nodeName} in cluster ${clusterName} faced issues while evaluating the usage. [View node](http://localhost:5601/app/monitoring#/elasticsearch/nodes/${nodeId}?_g=(cluster_uuid:${clusterUuid}))`, - internalShortMessage: `CPU usage alert for node ${nodeName} in cluster ${clusterName} faced issues while evaluating the usage. Verify CPU usage of node.`, - action: `[View node](http://localhost:5601/app/monitoring#/elasticsearch/nodes/${nodeId}?_g=(cluster_uuid:${clusterUuid}))`, - actionPlain: 'Verify CPU usage of node.', - clusterName, - count, - nodes: `${nodeName}:${cpuUsage}`, - node: `${nodeName}:${cpuUsage}`, - state: 'firing', - }); - }); - - it('should fire actions when it fails to calculate CPU usage', async () => { - (fetchCpuUsageNodeStats as jest.Mock).mockImplementation(() => { - return [ - { - ...stat, - cpuUsage: undefined, - }, - ]; - }); - - const rule = new CpuUsageRule(); - const type = rule.getRuleType(); - await type.executor({ - ...executorOptions, - params: rule.ruleOptions.defaultParams, - } as any); - const count = 1; - const threshold = rule.ruleOptions.defaultParams?.threshold; - expect(replaceState).toHaveBeenCalledWith({ - alertStates: [ - { - ccs: undefined, - cluster: { clusterUuid, clusterName }, - cpuUsage: undefined, - itemLabel: undefined, - meta: { - clusterUuid, - cpuUsage: undefined, - nodeId, - nodeName, - threshold, - }, - nodeId, - nodeName, - ui: { - isFiring: true, - message: { - text: 'Failed to compute CPU usage for node #start_linkmyNodeName#end_link. Please check the Kibana logs for more details. Last checked at #absolute', - tokens: [ - { - startToken: '#start_link', - endToken: '#end_link', - type: 'link', - url: 'elasticsearch/nodes/myNodeId', - }, - { - startToken: '#absolute', - type: 'time', - isAbsolute: true, - isRelative: false, - timestamp: 1, - }, - ], - }, - severity: 'warning', - triggeredMS: 1, - lastCheckedMS: 0, - }, - }, - ], - }); - expect(scheduleActions).toHaveBeenCalledWith('default', { - internalFullMessage: `CPU usage alert for node ${nodeName} in cluster ${clusterName} faced issues while evaluating the usage. [View node](http://localhost:5601/app/monitoring#/elasticsearch/nodes/${nodeId}?_g=(cluster_uuid:${clusterUuid}))`, - internalShortMessage: `CPU usage alert for node ${nodeName} in cluster ${clusterName} faced issues while evaluating the usage. Verify CPU usage of node.`, - action: `[View node](http://localhost:5601/app/monitoring#/elasticsearch/nodes/${nodeId}?_g=(cluster_uuid:${clusterUuid}))`, - actionPlain: 'Verify CPU usage of node.', - clusterName, - count, - nodes: `${nodeName}:undefined`, - node: `${nodeName}:undefined`, - state: 'firing', - }); - }); }); }); diff --git a/x-pack/plugins/monitoring/server/alerts/cpu_usage_rule.ts b/x-pack/plugins/monitoring/server/alerts/cpu_usage_rule.ts index 49ab66f2ce10d..92c45c9e61ae2 100644 --- a/x-pack/plugins/monitoring/server/alerts/cpu_usage_rule.ts +++ b/x-pack/plugins/monitoring/server/alerts/cpu_usage_rule.ts @@ -11,7 +11,6 @@ import { ElasticsearchClient } from '@kbn/core/server'; import { Alert } from '@kbn/alerting-plugin/server'; import { RawAlertInstance, SanitizedRule } from '@kbn/alerting-plugin/common'; import { parseDuration } from '@kbn/alerting-plugin/common/parse_duration'; -import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; import { BaseRule } from './base_rule'; import { AlertData, @@ -47,7 +46,7 @@ export class CpuUsageRule extends BaseRule { { name: 'node', description: i18n.translate('xpack.monitoring.alerts.cpuUsage.actionVariables.node', { - defaultMessage: 'The node reporting high CPU usage.', + defaultMessage: 'The node reporting high cpu usage.', }), }, ...Object.values(AlertingDefaults.ALERT_TYPE.context), @@ -63,52 +62,28 @@ export class CpuUsageRule extends BaseRule { const duration = parseDuration(params.duration); const endMs = +new Date(); const startMs = endMs - duration; - - let filterQuery; - if (params.filterQuery) { - try { - filterQuery = JSON.parse(params.filterQuery) as QueryDslQueryContainer; - } catch (error) { - throw new Error(`Failed to parse filter query in CPU usage rule ${error}`); - } - } - const stats = await fetchCpuUsageNodeStats( - { - esClient, - clusterUuids: clusters.map((cluster) => cluster.clusterUuid), - startMs, - endMs, - filterQuery, - logger: this.scopedLogger, - }, - Globals.app.config + esClient, + clusters, + startMs, + endMs, + Globals.app.config.ui.max_bucket_size, + params.filterQuery ); - - return stats.map((stat) => ({ - clusterUuid: stat.clusterUuid, - ...this.outcomeAndSeverity(stat, params.threshold!), - meta: { - ...stat, - threshold: params.threshold!, - }, - ccs: stat.ccs, - })); - } - - private outcomeAndSeverity( - stat: AlertCpuUsageNodeStats, - threshold: number - ): { shouldFire: boolean; severity: AlertSeverity } { - if (stat.limitsChanged || stat.unexpectedLimits || stat.cpuUsage === undefined) { - let severity = AlertSeverity.Warning; - if (stat.cpuUsage && stat.cpuUsage > threshold) { - severity = AlertSeverity.Danger; + return stats.map((stat) => { + if (Globals.app.config.ui.container.elasticsearch.enabled) { + stat.cpuUsage = + (stat.containerUsage / (stat.containerPeriods * stat.containerQuota * 1000)) * 100; } - return { shouldFire: true, severity }; - } - return { shouldFire: stat.cpuUsage > threshold, severity: AlertSeverity.Danger }; + return { + clusterUuid: stat.clusterUuid, + shouldFire: stat.cpuUsage > params.threshold!, + severity: AlertSeverity.Danger, + meta: stat, + ccs: stat.ccs, + }; + }); } protected filterAlertInstance(alertInstance: RawAlertInstance, filters: CommonAlertFilter[]) { @@ -127,67 +102,13 @@ export class CpuUsageRule extends BaseRule { } protected getUiMessage(alertState: AlertState, item: AlertData): AlertMessage { - const stat = item.meta as AlertCpuUsageNodeStats & Pick; - const tokens = [ - { - startToken: '#start_link', - endToken: '#end_link', - type: AlertMessageTokenType.Link, - url: `elasticsearch/nodes/${stat.nodeId}`, - } as AlertMessageLinkToken, - { - startToken: '#absolute', - type: AlertMessageTokenType.Time, - isAbsolute: true, - isRelative: false, - timestamp: alertState.ui.triggeredMS, - } as AlertMessageTimeToken, - ]; - - if (stat.unexpectedLimits) { - return { - text: i18n.translate('xpack.monitoring.alerts.cpuUsage.ui.unexpectedLimits', { - defaultMessage: `Kibana is configured for non-containerized workloads but node #start_link{nodeName}#end_link has resource limits configured. Node reports usage of {cpuUsage}%. Last checked at #absolute`, - values: { - nodeName: stat.nodeName, - cpuUsage: numeral(stat.cpuUsage).format(ROUNDED_FLOAT), - }, - }), - tokens, - }; - } - - if (stat.limitsChanged) { - return { - text: i18n.translate('xpack.monitoring.alerts.cpuUsage.ui.limitsChanged', { - defaultMessage: `Resource limits for node #start_link{nodeName}#end_link has changed within the look back window, unable to confidently calculate CPU usage for alerting. Please monitor the usage until the window has moved. Last checked at #absolute`, - values: { - nodeName: stat.nodeName, - }, - }), - tokens, - }; - } - - if (stat.cpuUsage === undefined) { - return { - text: i18n.translate('xpack.monitoring.alerts.cpuUsage.ui.failedToComputeUsage', { - defaultMessage: `Failed to compute CPU usage for node #start_link{nodeName}#end_link. Please check the Kibana logs for more details. Last checked at #absolute`, - values: { - nodeName: stat.nodeName, - }, - }), - tokens, - }; - } - + const stat = item.meta as AlertCpuUsageNodeStats; return { text: i18n.translate('xpack.monitoring.alerts.cpuUsage.ui.firingMessage', { - defaultMessage: `Node #start_link{nodeName}#end_link is reporting CPU usage of {cpuUsage}% which is above the configured threshold of {threshold}%. Last checked at #absolute`, + defaultMessage: `Node #start_link{nodeName}#end_link is reporting cpu usage of {cpuUsage}% at #absolute`, values: { nodeName: stat.nodeName, cpuUsage: numeral(stat.cpuUsage).format(ROUNDED_FLOAT), - threshold: stat.threshold, }, }), nextSteps: [ @@ -204,7 +125,21 @@ export class CpuUsageRule extends BaseRule { `{elasticWebsiteUrl}guide/en/elasticsearch/reference/{docLinkVersion}/tasks.html` ), ], - tokens, + tokens: [ + { + startToken: '#absolute', + type: AlertMessageTokenType.Time, + isAbsolute: true, + isRelative: false, + timestamp: alertState.ui.triggeredMS, + } as AlertMessageTimeToken, + { + startToken: '#start_link', + endToken: '#end_link', + type: AlertMessageTokenType.Link, + url: `elasticsearch/nodes/${stat.nodeId}`, + } as AlertMessageLinkToken, + ], }; } @@ -222,7 +157,7 @@ export class CpuUsageRule extends BaseRule { return; } const shortActionText = i18n.translate('xpack.monitoring.alerts.cpuUsage.shortAction', { - defaultMessage: 'Verify CPU usage of node.', + defaultMessage: 'Verify CPU level of node.', }); const fullActionText = i18n.translate('xpack.monitoring.alerts.cpuUsage.fullAction', { defaultMessage: 'View node', @@ -234,8 +169,28 @@ export class CpuUsageRule extends BaseRule { ccs ); const action = `[${fullActionText}](${globalStateLink})`; - const internalShortMessage = this.getMessage(firingNode, cluster.clusterName, shortActionText); - const internalFullMessage = this.getMessage(firingNode, cluster.clusterName, action); + const internalShortMessage = i18n.translate( + 'xpack.monitoring.alerts.cpuUsage.firing.internalShortMessage', + { + defaultMessage: `CPU usage alert is firing for node {nodeName} in cluster: {clusterName}. {shortActionText}`, + values: { + clusterName: cluster.clusterName, + nodeName: firingNode.nodeName, + shortActionText, + }, + } + ); + const internalFullMessage = i18n.translate( + 'xpack.monitoring.alerts.cpuUsage.firing.internalFullMessage', + { + defaultMessage: `CPU usage alert is firing for node {nodeName} in cluster: {clusterName}. {action}`, + values: { + clusterName: cluster.clusterName, + nodeName: firingNode.nodeName, + action, + }, + } + ); instance.scheduleActions('default', { internalShortMessage, internalFullMessage: Globals.app.isCloud ? internalShortMessage : internalFullMessage, @@ -251,28 +206,4 @@ export class CpuUsageRule extends BaseRule { actionPlain: shortActionText, }); } - - private getMessage(state: AlertCpuUsageState, clusterName: string, action: string) { - const stat = state.meta as AlertCpuUsageNodeStats; - - if (stat.limitsChanged || stat.unexpectedLimits || stat.cpuUsage === undefined) { - return i18n.translate('xpack.monitoring.alerts.cpuUsage.firing.internalMessageForFailure', { - defaultMessage: `CPU usage alert for node {nodeName} in cluster {clusterName} faced issues while evaluating the usage. {action}`, - values: { - clusterName, - nodeName: state.nodeName, - action, - }, - }); - } - - return i18n.translate('xpack.monitoring.alerts.cpuUsage.firing.internalMessage', { - defaultMessage: `CPU usage alert is firing for node {nodeName} in cluster {clusterName}. {action}`, - values: { - clusterName, - nodeName: state.nodeName, - action, - }, - }); - } } diff --git a/x-pack/plugins/monitoring/server/lib/alerts/__snapshots__/fetch_cpu_usage_node_stats.test.ts.snap b/x-pack/plugins/monitoring/server/lib/alerts/__snapshots__/fetch_cpu_usage_node_stats.test.ts.snap deleted file mode 100644 index 9a06dcd7263d2..0000000000000 --- a/x-pack/plugins/monitoring/server/lib/alerts/__snapshots__/fetch_cpu_usage_node_stats.test.ts.snap +++ /dev/null @@ -1,247 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`fetchCpuUsageNodeStats when running in a container calculates the containerized CPU usage 1`] = ` -Object { - "aggs": Object { - "clusters": Object { - "aggs": Object { - "nodes": Object { - "aggs": Object { - "average_cpu_usage_percent": Object { - "avg": Object { - "field": "node_stats.process.cpu.percent", - }, - }, - "index": Object { - "terms": Object { - "field": "_index", - "size": 1, - }, - }, - "max_periods": Object { - "max": Object { - "field": "node_stats.os.cgroup.cpu.stat.number_of_elapsed_periods", - }, - }, - "max_usage_nanos": Object { - "max": Object { - "field": "node_stats.os.cgroup.cpuacct.usage_nanos", - }, - }, - "min_periods": Object { - "min": Object { - "field": "node_stats.os.cgroup.cpu.stat.number_of_elapsed_periods", - }, - }, - "min_usage_nanos": Object { - "min": Object { - "field": "node_stats.os.cgroup.cpuacct.usage_nanos", - }, - }, - "name": Object { - "terms": Object { - "field": "source_node.name", - "size": 1, - }, - }, - "quota_micros_max": Object { - "max": Object { - "field": "node_stats.os.cgroup.cpu.cfs_quota_micros", - }, - }, - "quota_micros_min": Object { - "min": Object { - "field": "node_stats.os.cgroup.cpu.cfs_quota_micros", - }, - }, - }, - "terms": Object { - "field": "node_stats.node_id", - "size": 10, - }, - }, - }, - "terms": Object { - "field": "cluster_uuid", - "size": 10, - }, - }, - }, - "filter_path": Array [ - "aggregations", - ], - "index": ".monitoring-es-*,metrics-elasticsearch.stack_monitoring.node_stats-*", - "query": Object { - "bool": Object { - "filter": Array [ - Object { - "bool": Object { - "minimum_should_match": 1, - "should": Array [ - Object { - "term": Object { - "type": "node_stats", - }, - }, - Object { - "term": Object { - "metricset.name": "node_stats", - }, - }, - Object { - "term": Object { - "data_stream.dataset": "elasticsearch.stack_monitoring.node_stats", - }, - }, - ], - }, - }, - Object { - "terms": Object { - "cluster_uuid": Array [ - "my-test-cluster", - ], - }, - }, - Object { - "range": Object { - "timestamp": Object { - "format": "epoch_millis", - "gte": 0, - "lte": 10, - }, - }, - }, - Object { - "bool": Object { - "minimum_should_match": 1, - "should": Array [ - Object { - "term": Object { - "cluster_uuid": Object { - "value": "my-test-cluster", - }, - }, - }, - ], - }, - }, - ], - }, - }, - "size": 0, -} -`; - -exports[`fetchCpuUsageNodeStats when running outside a container calculates the CPU usage 1`] = ` -Object { - "aggs": Object { - "clusters": Object { - "aggs": Object { - "nodes": Object { - "aggs": Object { - "average_cpu": Object { - "avg": Object { - "field": "node_stats.process.cpu.percent", - }, - }, - "index": Object { - "terms": Object { - "field": "_index", - "size": 1, - }, - }, - "name": Object { - "terms": Object { - "field": "source_node.name", - "size": 1, - }, - }, - "quota_micros_max": Object { - "max": Object { - "field": "node_stats.os.cgroup.cpu.cfs_quota_micros", - }, - }, - "quota_micros_min": Object { - "min": Object { - "field": "node_stats.os.cgroup.cpu.cfs_quota_micros", - }, - }, - }, - "terms": Object { - "field": "node_stats.node_id", - "size": 10, - }, - }, - }, - "terms": Object { - "field": "cluster_uuid", - "size": 10, - }, - }, - }, - "filter_path": Array [ - "aggregations", - ], - "index": ".monitoring-es-*,metrics-elasticsearch.stack_monitoring.node_stats-*", - "query": Object { - "bool": Object { - "filter": Array [ - Object { - "bool": Object { - "minimum_should_match": 1, - "should": Array [ - Object { - "term": Object { - "type": "node_stats", - }, - }, - Object { - "term": Object { - "metricset.name": "node_stats", - }, - }, - Object { - "term": Object { - "data_stream.dataset": "elasticsearch.stack_monitoring.node_stats", - }, - }, - ], - }, - }, - Object { - "terms": Object { - "cluster_uuid": Array [ - "my-test-cluster", - ], - }, - }, - Object { - "range": Object { - "timestamp": Object { - "format": "epoch_millis", - "gte": 0, - "lte": 10, - }, - }, - }, - Object { - "bool": Object { - "minimum_should_match": 1, - "should": Array [ - Object { - "term": Object { - "cluster_uuid": Object { - "value": "my-test-cluster", - }, - }, - }, - ], - }, - }, - ], - }, - }, - "size": 0, -} -`; diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_cpu_usage_node_stats.test.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_cpu_usage_node_stats.test.ts index 214a7c04005f5..77c96e8b6138a 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/fetch_cpu_usage_node_stats.test.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_cpu_usage_node_stats.test.ts @@ -5,75 +5,64 @@ * 2.0. */ +import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; -import { loggerMock } from '@kbn/logging-mocks'; import { fetchCpuUsageNodeStats } from './fetch_cpu_usage_node_stats'; -describe('fetchCpuUsageNodeStats', () => { - describe('when running outside a container', () => { - const esClient = elasticsearchClientMock.createScopedClusterClient().asCurrentUser; - - const configSlice: any = { - ui: { - ccs: { enabled: false }, - container: { - elasticsearch: { - enabled: false, - }, +jest.mock('../../static_globals', () => ({ + Globals: { + app: { + config: { + ui: { + ccs: { enabled: true }, }, - max_bucket_size: 10, }, - }; + }, + }, +})); - const filterQuery = { - bool: { - should: [ - { - term: { - cluster_uuid: { - value: 'my-test-cluster', - }, - }, - }, - ], - minimum_should_match: 1, - }, - }; +describe('fetchCpuUsageNodeStats', () => { + const esClient = elasticsearchClientMock.createScopedClusterClient().asCurrentUser; + const clusters = [ + { + clusterUuid: 'abc123', + clusterName: 'test', + }, + ]; + const startMs = 0; + const endMs = 0; + const size = 10; - it('calculates the CPU usage', async () => { - esClient.search.mockResponse({ + it('fetch normal stats', async () => { + esClient.search.mockResponse( + // @ts-expect-error not full response interface + { aggregations: { clusters: { buckets: [ { - key: 'my-test-cluster', + key: clusters[0].clusterUuid, nodes: { buckets: [ { - key: 'my-test-node', - average_cpu: { - value: 45, - }, - quota_micros_max: { - value: null, - }, - quota_micros_min: { - value: null, - }, - name: { + key: 'theNodeId', + index: { buckets: [ { - key: 'test-node', + key: '.monitoring-es-TODAY', }, ], }, - index: { + name: { buckets: [ { - key: 'a-local-index', + key: 'theNodeName', }, ], }, + average_cpu: { + value: 10, + }, }, ], }, @@ -81,70 +70,66 @@ describe('fetchCpuUsageNodeStats', () => { ], }, }, - } as any); - - const stats = await fetchCpuUsageNodeStats( - { - esClient, - clusterUuids: ['my-test-cluster'], - startMs: 0, - endMs: 10, - filterQuery, - logger: loggerMock.create(), - }, - configSlice - ); - - expect(stats).toEqual([ - { - clusterUuid: 'my-test-cluster', - nodeId: 'my-test-node', - nodeName: 'test-node', - ccs: undefined, - cpuUsage: 45, - unexpectedLimits: false, - }, - ]); - - // If this check fails, it means the query has changed which `might` mean the response shape has changed and - // the test data needs to be updated to reflect the new format. - expect(esClient.search.mock.calls[0][0]).toMatchSnapshot(); - }); + } + ); + const result = await fetchCpuUsageNodeStats(esClient, clusters, startMs, endMs, size); + expect(result).toEqual([ + { + clusterUuid: clusters[0].clusterUuid, + nodeName: 'theNodeName', + nodeId: 'theNodeId', + cpuUsage: 10, + containerUsage: undefined, + containerPeriods: undefined, + containerQuota: undefined, + ccs: null, + }, + ]); + }); - it('warns about container metrics being present', async () => { - esClient.search.mockResponse({ + it('fetch container stats', async () => { + esClient.search.mockResponse( + // @ts-expect-error not full response interface + { aggregations: { clusters: { buckets: [ { - key: 'my-test-cluster', + key: clusters[0].clusterUuid, nodes: { buckets: [ { - key: 'my-test-node', - average_cpu: { - value: 45, - }, - quota_micros_max: { - value: 2000, - }, - quota_micros_min: { - value: 2000, + key: 'theNodeId', + index: { + buckets: [ + { + key: '.monitoring-es-TODAY', + }, + ], }, name: { buckets: [ { - key: 'test-node', + key: 'theNodeName', }, ], }, - index: { + histo: { buckets: [ + null, { - key: 'a-local-index', + usage_deriv: { + normalized_value: 10, + }, + periods_deriv: { + normalized_value: 5, + }, }, ], }, + average_quota: { + value: 50, + }, }, ], }, @@ -152,115 +137,59 @@ describe('fetchCpuUsageNodeStats', () => { ], }, }, - } as any); - - const stats = await fetchCpuUsageNodeStats( - { - esClient, - clusterUuids: ['my-test-cluster'], - startMs: 0, - endMs: 10, - filterQuery, - logger: loggerMock.create(), - }, - configSlice - ); - - expect(stats).toEqual([ - { - unexpectedLimits: true, - clusterUuid: 'my-test-cluster', - nodeId: 'my-test-node', - nodeName: 'test-node', - ccs: undefined, - cpuUsage: 45, - }, - ]); - }); - }); - - describe('when running in a container', () => { - const esClient = elasticsearchClientMock.createScopedClusterClient().asCurrentUser; - - const configSlice: any = { - ui: { - ccs: { enabled: false }, - container: { - elasticsearch: { - enabled: true, - }, - }, - max_bucket_size: 10, - }, - }; - - const filterQuery = { - bool: { - should: [ - { - term: { - cluster_uuid: { - value: 'my-test-cluster', - }, - }, - }, - ], - minimum_should_match: 1, + } + ); + const result = await fetchCpuUsageNodeStats(esClient, clusters, startMs, endMs, size); + expect(result).toEqual([ + { + clusterUuid: clusters[0].clusterUuid, + nodeName: 'theNodeName', + nodeId: 'theNodeId', + cpuUsage: undefined, + containerUsage: 10, + containerPeriods: 5, + containerQuota: 50, + ccs: null, }, - }; - - it('calculates the containerized CPU usage', async () => { - // 45% CPU usage - const maxPeriods = 1000; - const quotaMicros = 100000; - const usageLimitNanos = maxPeriods * quotaMicros * 1000; - const maxUsageNanos = 0.45 * usageLimitNanos; + ]); + }); - esClient.search.mockResponse({ + it('fetch properly return ccs', async () => { + esClient.search.mockResponse( + // @ts-expect-error not full response interface + { aggregations: { clusters: { buckets: [ { - key: 'my-test-cluster', + key: clusters[0].clusterUuid, nodes: { buckets: [ { - key: 'my-test-node', - min_usage_nanos: { - value: 0, - }, - max_usage_nanos: { - value: maxUsageNanos, - }, - min_periods: { - value: 0, - }, - max_periods: { - value: maxPeriods, - }, - quota_micros_min: { - value: quotaMicros, - }, - quota_micros_max: { - value: quotaMicros, - }, - average_cpu_usage_percent: { - value: 45, - }, - name: { + key: 'theNodeId', + index: { buckets: [ { - key: 'test-node', + key: 'foo:.monitoring-es-TODAY', }, ], }, - index: { + name: { buckets: [ { - key: 'a-local-index', + key: 'theNodeName', }, ], }, + average_usage: { + value: 10, + }, + average_periods: { + value: 5, + }, + average_quota: { + value: 50, + }, }, ], }, @@ -268,190 +197,90 @@ describe('fetchCpuUsageNodeStats', () => { ], }, }, - } as any); - - const stats = await fetchCpuUsageNodeStats( - { - esClient, - clusterUuids: ['my-test-cluster'], - startMs: 0, - endMs: 10, - filterQuery, - logger: loggerMock.create(), - }, - configSlice - ); - - expect(stats).toEqual([ - { - clusterUuid: 'my-test-cluster', - nodeId: 'my-test-node', - nodeName: 'test-node', - ccs: undefined, - cpuUsage: 45, - }, - ]); + } + ); + const result = await fetchCpuUsageNodeStats(esClient, clusters, startMs, endMs, size); + expect(result[0].ccs).toBe('foo'); + }); - // If this check fails, it means the query has changed which `might` mean the response shape has changed and - // the test data needs to be updated to reflect the new format. - expect(esClient.search.mock.calls[0][0]).toMatchSnapshot(); + it('should use consistent params', async () => { + let params = null; + esClient.search.mockImplementation((...args) => { + params = args[0]; + return Promise.resolve({} as estypes.SearchResponse); }); - - it('warns about resource usage limits not being set', async () => { - esClient.search.mockResponse({ - aggregations: { - clusters: { - buckets: [ + const filterQuery = + '{"bool":{"should":[{"exists":{"field":"cluster_uuid"}}],"minimum_should_match":1}}'; + await fetchCpuUsageNodeStats(esClient, clusters, startMs, endMs, size, filterQuery); + expect(params).toStrictEqual({ + index: + '*:.monitoring-es-*,.monitoring-es-*,*:metrics-elasticsearch.stack_monitoring.node_stats-*,metrics-elasticsearch.stack_monitoring.node_stats-*', + filter_path: ['aggregations'], + body: { + size: 0, + query: { + bool: { + filter: [ + { terms: { cluster_uuid: ['abc123'] } }, { - key: 'my-test-cluster', - nodes: { - buckets: [ + bool: { + should: [ + { term: { type: 'node_stats' } }, + { term: { 'metricset.name': 'node_stats' } }, { - key: 'my-test-node', - min_usage_nanos: { - value: 0, - }, - max_usage_nanos: { - value: 1000, - }, - min_periods: { - value: 0, - }, - max_periods: { - value: 100, - }, - quota_micros_min: { - value: -1, - }, - quota_micros_max: { - value: -1, - }, - average_cpu_usage_percent: { - value: 45, - }, - name: { - buckets: [ - { - key: 'test-node', - }, - ], - }, - index: { - buckets: [ - { - key: 'a-local-index', - }, - ], - }, + term: { 'data_stream.dataset': 'elasticsearch.stack_monitoring.node_stats' }, }, ], + minimum_should_match: 1, }, }, + { range: { timestamp: { format: 'epoch_millis', gte: 0, lte: 0 } } }, + { + bool: { should: [{ exists: { field: 'cluster_uuid' } }], minimum_should_match: 1 }, + }, ], }, }, - } as any); - - const stats = await fetchCpuUsageNodeStats( - { - esClient, - clusterUuids: ['my-test-cluster'], - startMs: 0, - endMs: 10, - filterQuery, - logger: loggerMock.create(), - }, - configSlice - ); - - expect(stats).toEqual([ - { - clusterUuid: 'my-test-cluster', - nodeId: 'my-test-node', - nodeName: 'test-node', - ccs: undefined, - cpuUsage: 45, - }, - ]); - }); - - it('warns about resource usage limits being changed', async () => { - esClient.search.mockResponse({ - aggregations: { + aggs: { clusters: { - buckets: [ - { - key: 'my-test-cluster', - nodes: { - buckets: [ - { - key: 'my-test-node', - min_usage_nanos: { - value: 0, - }, - max_usage_nanos: { - value: 1000, - }, - min_periods: { - value: 0, - }, - max_periods: { - value: 100, - }, - quota_micros_min: { - value: -1, - }, - quota_micros_max: { - value: 10000, - }, - average_cpu_usage_percent: { - value: 45, - }, - name: { - buckets: [ - { - key: 'test-node', - }, - ], - }, - index: { - buckets: [ - { - key: 'a-local-index', - }, - ], + terms: { field: 'cluster_uuid', size: 10, include: ['abc123'] }, + aggs: { + nodes: { + terms: { field: 'node_stats.node_id', size: 10 }, + aggs: { + index: { terms: { field: '_index', size: 1 } }, + average_cpu: { avg: { field: 'node_stats.process.cpu.percent' } }, + average_quota: { avg: { field: 'node_stats.os.cgroup.cpu.cfs_quota_micros' } }, + name: { terms: { field: 'source_node.name', size: 1 } }, + histo: { + date_histogram: { field: 'timestamp', fixed_interval: '0m' }, + aggs: { + average_periods: { + max: { field: 'node_stats.os.cgroup.cpu.stat.number_of_elapsed_periods' }, + }, + average_usage: { max: { field: 'node_stats.os.cgroup.cpuacct.usage_nanos' } }, + usage_deriv: { + derivative: { + buckets_path: 'average_usage', + gap_policy: 'skip', + unit: '1s', + }, + }, + periods_deriv: { + derivative: { + buckets_path: 'average_periods', + gap_policy: 'skip', + unit: '1s', + }, }, }, - ], + }, }, }, - ], + }, }, }, - } as any); - - const stats = await fetchCpuUsageNodeStats( - { - esClient, - clusterUuids: ['my-test-cluster'], - startMs: 0, - endMs: 10, - filterQuery, - logger: loggerMock.create(), - }, - configSlice - ); - - expect(stats).toEqual([ - { - limitsChanged: true, - clusterUuid: 'my-test-cluster', - nodeId: 'my-test-node', - nodeName: 'test-node', - ccs: undefined, - cpuUsage: undefined, - }, - ]); + }, }); }); }); diff --git a/x-pack/plugins/monitoring/server/lib/alerts/fetch_cpu_usage_node_stats.ts b/x-pack/plugins/monitoring/server/lib/alerts/fetch_cpu_usage_node_stats.ts index 5ccaa522c7368..8037ad94e6764 100644 --- a/x-pack/plugins/monitoring/server/lib/alerts/fetch_cpu_usage_node_stats.ts +++ b/x-pack/plugins/monitoring/server/lib/alerts/fetch_cpu_usage_node_stats.ts @@ -5,303 +5,139 @@ * 2.0. */ -import { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/types'; -import { ElasticsearchClient, Logger } from '@kbn/core/server'; -import { InferSearchResponseOf } from '@kbn/es-types'; -import { CCS_REMOTE_PATTERN } from '../../../common/constants'; -import { AlertCpuUsageNodeStats } from '../../../common/types/alerts'; -import { MonitoringConfig } from '../../config'; -import { getElasticsearchDataset, getIndexPatterns } from '../cluster/get_index_patterns'; +import { ElasticsearchClient } from '@kbn/core/server'; +import { get } from 'lodash'; +import moment from 'moment'; +import { NORMALIZED_DERIVATIVE_UNIT } from '../../../common/constants'; +import { AlertCluster, AlertCpuUsageNodeStats } from '../../../common/types/alerts'; import { createDatasetFilter } from './create_dataset_query_filter'; +import { getIndexPatterns, getElasticsearchDataset } from '../cluster/get_index_patterns'; +import { Globals } from '../../static_globals'; +import { CCS_REMOTE_PATTERN } from '../../../common/constants'; + +interface NodeBucketESResponse { + key: string; + average_cpu: { value: number }; +} -interface Options { - esClient: ElasticsearchClient; - clusterUuids: string[]; - startMs: number; - endMs: number; - filterQuery?: QueryDslQueryContainer; - logger: Logger; +interface ClusterBucketESResponse { + key: string; + nodes: { + buckets: NodeBucketESResponse[]; + }; } export async function fetchCpuUsageNodeStats( - options: Options, - config: MonitoringConfig + esClient: ElasticsearchClient, + clusters: AlertCluster[], + startMs: number, + endMs: number, + size: number, + filterQuery?: string ): Promise { - if (config.ui.container.elasticsearch.enabled) { - options.logger.debug('CPU usage rule: Computing usage for containerized clusters'); - return fetchContainerStats(options, config); - } + // Using pure MS didn't seem to work well with the date_histogram interval + // but minutes does + const intervalInMinutes = moment.duration(endMs - startMs).asMinutes(); - options.logger.debug('CPU usage rule: Computing usage for non-containerized clusters'); - return fetchNonContainerStats(options, config); -} - -async function fetchContainerStats( - { esClient, startMs, endMs, clusterUuids, filterQuery }: Options, - config: MonitoringConfig -) { const indexPatterns = getIndexPatterns({ - config, + config: Globals.app.config, moduleType: 'elasticsearch', dataset: 'node_stats', ccs: CCS_REMOTE_PATTERN, }); - const params = { index: indexPatterns, filter_path: ['aggregations'], - size: 0, - query: { - bool: { - filter: [ - createDatasetFilter('node_stats', 'node_stats', getElasticsearchDataset('node_stats')), - { - terms: { - cluster_uuid: clusterUuids, - }, - }, - { - range: { - timestamp: { - format: 'epoch_millis', - gte: startMs, - lte: endMs, + body: { + size: 0, + query: { + bool: { + filter: [ + { + terms: { + cluster_uuid: clusters.map((cluster) => cluster.clusterUuid), }, }, - }, - ], - }, - }, - aggs: { - clusters: { - terms: { - field: 'cluster_uuid', - size: config.ui.max_bucket_size, - }, - aggs: { - nodes: { - terms: { - field: 'node_stats.node_id', - size: config.ui.max_bucket_size, - }, - aggs: { - name: { - terms: { - field: 'source_node.name', - size: 1, - }, - }, - // Used to check for CCS and get the remote cluster name - index: { - terms: { - field: '_index', - size: 1, - }, - }, - // Fallback value in case container limits are not specified - average_cpu_usage_percent: { - avg: { - field: 'node_stats.process.cpu.percent', - }, - }, - // Container limit min and max, to calculate usage and detect config changes - quota_micros_max: { - max: { - field: 'node_stats.os.cgroup.cpu.cfs_quota_micros', - }, - }, - quota_micros_min: { - min: { - field: 'node_stats.os.cgroup.cpu.cfs_quota_micros', - }, - }, - // Usage to calculate delta - max_usage_nanos: { - max: { - field: 'node_stats.os.cgroup.cpuacct.usage_nanos', - }, - }, - min_usage_nanos: { - min: { - field: 'node_stats.os.cgroup.cpuacct.usage_nanos', - }, - }, - // Periods to calculate delta - max_periods: { - max: { - field: 'node_stats.os.cgroup.cpu.stat.number_of_elapsed_periods', - }, - }, - min_periods: { - min: { - field: 'node_stats.os.cgroup.cpu.stat.number_of_elapsed_periods', + createDatasetFilter('node_stats', 'node_stats', getElasticsearchDataset('node_stats')), + { + range: { + timestamp: { + format: 'epoch_millis', + gte: startMs, + lte: endMs, }, }, }, - }, + ], }, }, - }, - }; - - if (filterQuery) { - (params.query!.bool!.filter! as QueryDslQueryContainer[]).push(filterQuery); - } - - const response = (await esClient.search(params)) as unknown as InferSearchResponseOf< - unknown, - typeof params - >; - - if (!response.aggregations) { - throw new Error('Failed to resolve needed aggregations for CPU Usage Rule'); - } - - return response.aggregations.clusters.buckets.flatMap((cluster) => { - return cluster.nodes.buckets.map((node): AlertCpuUsageNodeStats => { - let nodeName; - if (node.name.buckets.length) { - nodeName = node.name.buckets[0].key as string; - } - - let ccs; - if (node.index.buckets.length) { - const index = node.index.buckets[0].key as string; - ccs = index.includes(':') ? index.split(':')[0] : undefined; - } - - const nodeStats = { - clusterUuid: cluster.key as string, - nodeId: node.key as string, - nodeName, - ccs, - }; - - const limitsNotSet = node.quota_micros_max.value === -1 && node.quota_micros_min.value === -1; - - if ( - limitsNotSet || - node.max_usage_nanos.value === null || - node.min_usage_nanos.value === null || - node.max_periods.value === null || - node.min_periods.value === null || - node.quota_micros_max.value === null - ) { - return { - ...nodeStats, - cpuUsage: node.average_cpu_usage_percent.value ?? undefined, - }; - } - - if (node.quota_micros_min.value !== node.quota_micros_max.value) { - return { - ...nodeStats, - limitsChanged: true, - cpuUsage: undefined, - }; - } - - const usageDeltaNanos = node.max_usage_nanos.value - node.min_usage_nanos.value; - const periodsDelta = node.max_periods.value - node.min_periods.value; - - const cpuUsage = computeCfsPercentCpuUsage( - usageDeltaNanos, - node.quota_micros_max.value, - periodsDelta - ); - - return { - ...nodeStats, - cpuUsage: Math.round(cpuUsage * 100) / 100, - }; - }); - }); -} - -function computeCfsPercentCpuUsage(usageNanos: number, quotaMicros: number, periods: number) { - // See https://github.com/elastic/kibana/pull/159351 for an explanation of this formula - const quotaNanos = quotaMicros * 1000; - const limitNanos = quotaNanos * periods; - const usageAsFactor = usageNanos / limitNanos; - return usageAsFactor * 100; -} - -async function fetchNonContainerStats( - { esClient, startMs, endMs, clusterUuids, filterQuery }: Options, - config: MonitoringConfig -) { - const indexPatterns = getIndexPatterns({ - config, - moduleType: 'elasticsearch', - dataset: 'node_stats', - ccs: CCS_REMOTE_PATTERN, - }); - - const params = { - index: indexPatterns, - filter_path: ['aggregations'], - size: 0, - query: { - bool: { - filter: [ - createDatasetFilter('node_stats', 'node_stats', getElasticsearchDataset('node_stats')), - { - terms: { - cluster_uuid: clusterUuids, - }, - }, - { - range: { - timestamp: { - format: 'epoch_millis', - gte: startMs, - lte: endMs, - }, - }, + aggs: { + clusters: { + terms: { + field: 'cluster_uuid', + size, + include: clusters.map((cluster) => cluster.clusterUuid), }, - ], - }, - }, - aggs: { - clusters: { - terms: { - field: 'cluster_uuid', - size: config.ui.max_bucket_size, - }, - aggs: { - nodes: { - terms: { - field: 'node_stats.node_id', - size: config.ui.max_bucket_size, - }, - aggs: { - name: { - terms: { - field: 'source_node.name', - size: 1, + aggs: { + nodes: { + terms: { + field: 'node_stats.node_id', + size, + }, + aggs: { + index: { + terms: { + field: '_index', + size: 1, + }, }, - }, - // Used to check for CCS and get the remote cluster name - index: { - terms: { - field: '_index', - size: 1, + average_cpu: { + avg: { + field: 'node_stats.process.cpu.percent', + }, }, - }, - average_cpu: { - avg: { - field: 'node_stats.process.cpu.percent', + average_quota: { + avg: { + field: 'node_stats.os.cgroup.cpu.cfs_quota_micros', + }, }, - }, - // Container limit min and max, to detect possible config errors - quota_micros_max: { - max: { - field: 'node_stats.os.cgroup.cpu.cfs_quota_micros', + name: { + terms: { + field: 'source_node.name', + size: 1, + }, }, - }, - quota_micros_min: { - min: { - field: 'node_stats.os.cgroup.cpu.cfs_quota_micros', + histo: { + date_histogram: { + field: 'timestamp', + fixed_interval: `${intervalInMinutes}m`, + }, + aggs: { + average_periods: { + max: { + field: 'node_stats.os.cgroup.cpu.stat.number_of_elapsed_periods', + }, + }, + average_usage: { + max: { + field: 'node_stats.os.cgroup.cpuacct.usage_nanos', + }, + }, + usage_deriv: { + derivative: { + buckets_path: 'average_usage', + gap_policy: 'skip' as const, + unit: NORMALIZED_DERIVATIVE_UNIT, + }, + }, + periods_deriv: { + derivative: { + buckets_path: 'average_periods', + gap_policy: 'skip' as const, + unit: NORMALIZED_DERIVATIVE_UNIT, + }, + }, + }, }, }, }, @@ -311,44 +147,38 @@ async function fetchNonContainerStats( }, }; - if (filterQuery) { - (params.query!.bool!.filter! as QueryDslQueryContainer[]).push(filterQuery); - } - - const response = (await esClient.search(params)) as unknown as InferSearchResponseOf< - unknown, - typeof params - >; - - if (!response.aggregations) { - throw new Error('Failed to resolve needed aggregations for CPU Usage Rule'); + try { + if (filterQuery) { + const filterQueryObject = JSON.parse(filterQuery); + params.body.query.bool.filter.push(filterQueryObject); + } + } catch (e) { + // meh } - return response.aggregations.clusters.buckets.flatMap((cluster) => { - return cluster.nodes.buckets.map((node): AlertCpuUsageNodeStats => { - let nodeName; - if (node.name.buckets.length) { - nodeName = node.name.buckets[0].key as string; - } - - let ccs; - if (node.index.buckets.length) { - const index = node.index.buckets[0].key as string; - ccs = index.includes(':') ? index.split(':')[0] : undefined; - } - - const runningInAContainerWithLimits = - (node.quota_micros_min.value !== null && node.quota_micros_min.value !== -1) || - (node.quota_micros_max.value !== null && node.quota_micros_max.value !== -1); - - return { - clusterUuid: cluster.key as string, - nodeId: node.key as string, - cpuUsage: node.average_cpu.value ?? undefined, - nodeName, - ccs, - unexpectedLimits: runningInAContainerWithLimits, + const response = await esClient.search(params); + const stats: AlertCpuUsageNodeStats[] = []; + const clusterBuckets = get( + response, + 'aggregations.clusters.buckets', + [] + ) as ClusterBucketESResponse[]; + for (const clusterBucket of clusterBuckets) { + for (const node of clusterBucket.nodes.buckets) { + const lastBucket = get(node, 'histo.buckets[1]', {}); + const indexName = get(node, 'index.buckets[0].key', ''); + const stat = { + clusterUuid: clusterBucket.key, + nodeId: node.key, + nodeName: get(node, 'name.buckets[0].key'), + cpuUsage: get(node, 'average_cpu.value'), + containerUsage: get(lastBucket, 'usage_deriv.normalized_value'), + containerPeriods: get(lastBucket, 'periods_deriv.normalized_value'), + containerQuota: get(node, 'average_quota.value'), + ccs: indexName.includes(':') ? indexName.split(':')[0] : null, }; - }); - }); + stats.push(stat); + } + } + return stats; } diff --git a/x-pack/plugins/monitoring/tsconfig.json b/x-pack/plugins/monitoring/tsconfig.json index d70d8b51fcd08..00ca962568141 100644 --- a/x-pack/plugins/monitoring/tsconfig.json +++ b/x-pack/plugins/monitoring/tsconfig.json @@ -41,7 +41,6 @@ "@kbn/shared-ux-router", "@kbn/observability-shared-plugin", "@kbn/shared-ux-link-redirect-app", - "@kbn/es-types", "@kbn/logs-shared-plugin", ], "exclude": [ diff --git a/x-pack/plugins/notifications/kibana.jsonc b/x-pack/plugins/notifications/kibana.jsonc index b6c7c6a3e334f..e223a12dbc793 100644 --- a/x-pack/plugins/notifications/kibana.jsonc +++ b/x-pack/plugins/notifications/kibana.jsonc @@ -6,7 +6,7 @@ "id": "notifications", "server": true, "browser": false, - "requiredPlugins": [ + "optionalPlugins": [ "actions", "licensing" ] diff --git a/x-pack/plugins/notifications/server/index.ts b/x-pack/plugins/notifications/server/index.ts index 4c3afb14f7a57..76df75c9afd25 100755 --- a/x-pack/plugins/notifications/server/index.ts +++ b/x-pack/plugins/notifications/server/index.ts @@ -10,7 +10,7 @@ export { config } from './config'; // This exports static code and TypeScript types, // as well as, Kibana Platform `plugin()` initializer. -export type { NotificationsPluginStart } from './types'; +export type { NotificationsServerStart as NotificationsPluginStart } from './types'; export async function plugin(initializerContext: PluginInitializerContext) { const { NotificationsPlugin } = await import('./plugin'); diff --git a/x-pack/plugins/notifications/server/mocks.ts b/x-pack/plugins/notifications/server/mocks.ts index 65d600fda6e96..aa6f5c227d445 100644 --- a/x-pack/plugins/notifications/server/mocks.ts +++ b/x-pack/plugins/notifications/server/mocks.ts @@ -7,7 +7,7 @@ import type { PublicMethodsOf } from '@kbn/utility-types'; import type { EmailService } from './services'; -import type { NotificationsPluginStart } from './types'; +import type { NotificationsServerStart } from './types'; import type { NotificationsPlugin } from './plugin'; const emailServiceMock: jest.Mocked = { @@ -19,7 +19,7 @@ const createEmailServiceMock = () => { return emailServiceMock; }; -const startMock: jest.Mocked = { +const startMock: jest.Mocked = { isEmailServiceAvailable: jest.fn(), getEmailService: jest.fn(createEmailServiceMock), }; @@ -30,7 +30,7 @@ const createStartMock = () => { const notificationsPluginMock: jest.Mocked> = { setup: jest.fn(), - start: jest.fn(createStartMock) as jest.Mock, + start: jest.fn(createStartMock) as jest.Mock, stop: jest.fn(), }; diff --git a/x-pack/plugins/notifications/server/plugin.ts b/x-pack/plugins/notifications/server/plugin.ts index 562db1977a73c..542eaeb0ba23b 100755 --- a/x-pack/plugins/notifications/server/plugin.ts +++ b/x-pack/plugins/notifications/server/plugin.ts @@ -7,14 +7,23 @@ import type { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '@kbn/core/server'; import type { - NotificationsPluginSetupDeps, - NotificationsPluginStartDeps, - NotificationsPluginStart, + NotificationsServerSetupDependencies, + NotificationsServerStartDependencies, + NotificationsServerStart, + NotificationsServerSetup, } from './types'; import type { NotificationsConfigType } from './config'; import { EmailServiceProvider } from './services/connectors_email_service_provider'; -export class NotificationsPlugin implements Plugin { +export class NotificationsPlugin + implements + Plugin< + NotificationsServerSetup, + NotificationsServerStart, + NotificationsServerSetupDependencies, + NotificationsServerStartDependencies + > +{ private emailServiceProvider: EmailServiceProvider; constructor(initializerContext: PluginInitializerContext) { @@ -24,11 +33,11 @@ export class NotificationsPlugin implements Plugin `${OBSERVABILITY_BASE_PATH}${RULES_PATH}/${encodeURI(ruleId)}`, slos: `${OBSERVABILITY_BASE_PATH}${SLOS_PATH}`, slosWelcome: `${OBSERVABILITY_BASE_PATH}${SLOS_WELCOME_PATH}`, + slosOutdatedDefinitions: `${OBSERVABILITY_BASE_PATH}${SLOS_OUTDATED_DEFINITIONS_PATH}`, sloCreate: `${OBSERVABILITY_BASE_PATH}${SLO_CREATE_PATH}`, sloCreateWithEncodedForm: (encodedParams: string) => `${OBSERVABILITY_BASE_PATH}${SLO_CREATE_PATH}?_a=${encodedParams}`, diff --git a/x-pack/plugins/observability/common/slo/constants.ts b/x-pack/plugins/observability/common/slo/constants.ts index 0dd9df915eee4..d49b1a805e192 100644 --- a/x-pack/plugins/observability/common/slo/constants.ts +++ b/x-pack/plugins/observability/common/slo/constants.ts @@ -5,8 +5,8 @@ * 2.0. */ -export const SLO_RESOURCES_VERSION = 2; -export const SLO_SUMMARY_TRANSFORMS_VERSION = 3; +export const SLO_MODEL_VERSION = 2; +export const SLO_RESOURCES_VERSION = 3; export const SLO_COMPONENT_TEMPLATE_MAPPINGS_NAME = '.slo-observability.sli-mappings'; export const SLO_COMPONENT_TEMPLATE_SETTINGS_NAME = '.slo-observability.sli-settings'; @@ -17,8 +17,7 @@ export const SLO_INDEX_TEMPLATE_PATTERN = `.slo-observability.sli-*`; export const SLO_DESTINATION_INDEX_NAME = `.slo-observability.sli-v${SLO_RESOURCES_VERSION}`; export const SLO_DESTINATION_INDEX_PATTERN = `.slo-observability.sli-v${SLO_RESOURCES_VERSION}*`; -export const SLO_INGEST_PIPELINE_NAME = `.slo-observability.sli.pipeline`; -// slo-observability.sli-v.(YYYY-MM-DD) +export const SLO_INGEST_PIPELINE_NAME = `.slo-observability.sli.pipeline-v${SLO_RESOURCES_VERSION}`; export const SLO_INGEST_PIPELINE_INDEX_NAME_PREFIX = `.slo-observability.sli-v${SLO_RESOURCES_VERSION}.`; export const SLO_SUMMARY_COMPONENT_TEMPLATE_MAPPINGS_NAME = '.slo-observability.summary-mappings'; @@ -26,12 +25,16 @@ export const SLO_SUMMARY_COMPONENT_TEMPLATE_SETTINGS_NAME = '.slo-observability. export const SLO_SUMMARY_INDEX_TEMPLATE_NAME = '.slo-observability.summary'; export const SLO_SUMMARY_INDEX_TEMPLATE_PATTERN = `.slo-observability.summary-*`; -export const SLO_SUMMARY_TRANSFORM_NAME_PREFIX = 'slo-summary-'; export const SLO_SUMMARY_DESTINATION_INDEX_NAME = `.slo-observability.summary-v${SLO_RESOURCES_VERSION}`; // store the temporary summary document generated by transform export const SLO_SUMMARY_TEMP_INDEX_NAME = `.slo-observability.summary-v${SLO_RESOURCES_VERSION}.temp`; // store the temporary summary document export const SLO_SUMMARY_DESTINATION_INDEX_PATTERN = `.slo-observability.summary-v${SLO_RESOURCES_VERSION}*`; // include temp and non-temp summary indices -export const SLO_SUMMARY_INGEST_PIPELINE_NAME = `.slo-observability.summary.pipeline`; - export const getSLOTransformId = (sloId: string, sloRevision: number) => `slo-${sloId}-${sloRevision}`; + +export const DEFAULT_SLO_PAGE_SIZE = 25; +export const getSLOSummaryTransformId = (sloId: string, sloRevision: number) => + `slo-summary-${sloId}-${sloRevision}`; + +export const getSLOSummaryPipelineId = (sloId: string, sloRevision: number) => + `.slo-observability.summary.pipeline-${sloId}-${sloRevision}`; diff --git a/x-pack/plugins/observability/common/ui_settings_keys.ts b/x-pack/plugins/observability/common/ui_settings_keys.ts index 5745882055cab..bcc2f0451caac 100644 --- a/x-pack/plugins/observability/common/ui_settings_keys.ts +++ b/x-pack/plugins/observability/common/ui_settings_keys.ts @@ -17,6 +17,8 @@ export const apmServiceGroupMaxNumberOfServices = export const apmTraceExplorerTab = 'observability:apmTraceExplorerTab'; export const apmLabsButton = 'observability:apmLabsButton'; export const enableInfrastructureHostsView = 'observability:enableInfrastructureHostsView'; +export const enableInfrastructureProfilingIntegration = + 'observability:enableInfrastructureProfilingIntegration'; export const enableAwsLambdaMetrics = 'observability:enableAwsLambdaMetrics'; export const enableAgentExplorerView = 'observability:apmAgentExplorerView'; export const apmAWSLambdaPriceFactor = 'observability:apmAWSLambdaPriceFactor'; diff --git a/x-pack/plugins/observability/docs/openapi/slo/bundled.json b/x-pack/plugins/observability/docs/openapi/slo/bundled.json index ff366afc2ff1f..1ec1b5a629f02 100644 --- a/x-pack/plugins/observability/docs/openapi/slo/bundled.json +++ b/x-pack/plugins/observability/docs/openapi/slo/bundled.json @@ -143,7 +143,7 @@ { "name": "page", "in": "query", - "description": "The page number to return", + "description": "The page to use for pagination, must be greater or equal than 1", "schema": { "type": "integer", "default": 1 @@ -153,7 +153,7 @@ { "name": "perPage", "in": "query", - "description": "The number of SLOs to return per page", + "description": "Number of SLOs returned by page", "schema": { "type": "integer", "default": 25, @@ -280,7 +280,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/slo_response" + "$ref": "#/components/schemas/slo_with_summary_response" } } } @@ -361,7 +361,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/slo_response" + "$ref": "#/components/schemas/slo_definition_response" } } } @@ -605,6 +605,79 @@ } } }, + "/s/{spaceId}/api/observability/slos/{sloId}/_reset": { + "post": { + "summary": "Resets an SLO.", + "operationId": "resetSloOp", + "description": "You must have the `write` privileges for the **SLOs** feature in the **Observability** section of the Kibana feature privileges.\n", + "tags": [ + "slo" + ], + "parameters": [ + { + "$ref": "#/components/parameters/kbn_xsrf" + }, + { + "$ref": "#/components/parameters/space_id" + }, + { + "$ref": "#/components/parameters/slo_id" + } + ], + "responses": { + "204": { + "description": "Successful request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/slo_definition_response" + } + } + } + }, + "400": { + "description": "Bad request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/400_response" + } + } + } + }, + "401": { + "description": "Unauthorized response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/401_response" + } + } + } + }, + "403": { + "description": "Unauthorized response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/403_response" + } + } + } + }, + "404": { + "description": "Not found response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/404_response" + } + } + } + } + } + } + }, "/s/{spaceId}/internal/observability/slos/_historical_summary": { "post": { "summary": "Retrieves the historical summary for a list of SLOs", @@ -675,6 +748,104 @@ } } }, + "/s/{spaceId}/internal/observability/slos/_definitions": { + "get": { + "summary": "Get the SLO definitions", + "operationId": "getDefinitionsOp", + "description": "You must have the `read` privileges for the **SLOs** feature in the **Observability** section of the Kibana feature privileges.\n", + "tags": [ + "slo" + ], + "parameters": [ + { + "$ref": "#/components/parameters/kbn_xsrf" + }, + { + "$ref": "#/components/parameters/space_id" + }, + { + "name": "includeOutdatedOnly", + "in": "query", + "description": "Indicates if the API returns only outdated SLO or all SLO definitions", + "schema": { + "type": "boolean" + }, + "example": true + }, + { + "name": "search", + "in": "query", + "description": "Filters the SLOs by name", + "schema": { + "type": "string" + }, + "example": "my service availability" + }, + { + "name": "page", + "in": "query", + "description": "The page to use for pagination, must be greater or equal than 1", + "schema": { + "type": "number" + }, + "example": 1 + }, + { + "name": "perPage", + "in": "query", + "description": "Number of SLOs returned by page", + "schema": { + "type": "integer", + "default": 100, + "maximum": 1000 + }, + "example": 100 + } + ], + "responses": { + "200": { + "description": "Successful request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/find_slo_definitions_response" + } + } + } + }, + "400": { + "description": "Bad request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/400_response" + } + } + } + }, + "401": { + "description": "Unauthorized response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/401_response" + } + } + } + }, + "403": { + "description": "Unauthorized response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/403_response" + } + } + } + } + } + } + }, "/s/{spaceId}/api/observability/slos/_delete_instances": { "post": { "summary": "Batch delete rollup and summary data for the matching list of sloId and instanceId", @@ -1587,7 +1758,7 @@ } } }, - "slo_response": { + "slo_with_summary_response": { "title": "SLO response", "type": "object", "required": [ @@ -1606,7 +1777,8 @@ "instanceId", "tags", "createdAt", - "updatedAt" + "updatedAt", + "version" ], "properties": { "id": { @@ -1708,6 +1880,11 @@ "description": "The last update date", "type": "string", "example": "2023-01-12T10:03:19.000Z" + }, + "version": { + "description": "The internal SLO version", + "type": "number", + "example": 2 } } }, @@ -1731,7 +1908,7 @@ "results": { "type": "array", "items": { - "$ref": "#/components/schemas/slo_response" + "$ref": "#/components/schemas/slo_with_summary_response" } } } @@ -1994,6 +2171,126 @@ } } }, + "slo_definition_response": { + "title": "SLO definition response", + "type": "object", + "required": [ + "id", + "name", + "description", + "indicator", + "timeWindow", + "budgetingMethod", + "objective", + "settings", + "revision", + "enabled", + "groupBy", + "tags", + "createdAt", + "updatedAt", + "version" + ], + "properties": { + "id": { + "description": "The identifier of the SLO.", + "type": "string", + "example": "8853df00-ae2e-11ed-90af-09bb6422b258" + }, + "name": { + "description": "The name of the SLO.", + "type": "string", + "example": "My Service SLO" + }, + "description": { + "description": "The description of the SLO.", + "type": "string", + "example": "My SLO description" + }, + "indicator": { + "discriminator": { + "propertyName": "type", + "mapping": { + "sli.apm.transactionErrorRate": "#/components/schemas/indicator_properties_apm_availability", + "sli.kql.custom": "#/components/schemas/indicator_properties_custom_kql", + "sli.apm.transactionDuration": "#/components/schemas/indicator_properties_apm_latency", + "sli.metric.custom": "#/components/schemas/indicator_properties_custom_metric", + "sli.histogram.custom": "#/components/schemas/indicator_properties_histogram", + "sli.metric.timeslice": "#/components/schemas/indicator_properties_timeslice_metric" + } + }, + "oneOf": [ + { + "$ref": "#/components/schemas/indicator_properties_custom_kql" + }, + { + "$ref": "#/components/schemas/indicator_properties_apm_availability" + }, + { + "$ref": "#/components/schemas/indicator_properties_apm_latency" + }, + { + "$ref": "#/components/schemas/indicator_properties_custom_metric" + }, + { + "$ref": "#/components/schemas/indicator_properties_histogram" + }, + { + "$ref": "#/components/schemas/indicator_properties_timeslice_metric" + } + ] + }, + "timeWindow": { + "$ref": "#/components/schemas/time_window" + }, + "budgetingMethod": { + "$ref": "#/components/schemas/budgeting_method" + }, + "objective": { + "$ref": "#/components/schemas/objective" + }, + "settings": { + "$ref": "#/components/schemas/settings" + }, + "revision": { + "description": "The SLO revision", + "type": "number", + "example": 2 + }, + "enabled": { + "description": "Indicate if the SLO is enabled", + "type": "boolean", + "example": true + }, + "groupBy": { + "description": "optional group by field to use to generate an SLO per distinct value", + "type": "string", + "example": "some.field" + }, + "tags": { + "description": "List of tags", + "type": "array", + "items": { + "type": "string" + } + }, + "createdAt": { + "description": "The creation date", + "type": "string", + "example": "2023-01-12T10:03:19.000Z" + }, + "updatedAt": { + "description": "The last update date", + "type": "string", + "example": "2023-01-12T10:03:19.000Z" + }, + "version": { + "description": "The internal SLO version", + "type": "number", + "example": 2 + } + } + }, "historical_summary_request": { "title": "Historical summary request", "type": "object", @@ -2037,6 +2334,31 @@ } } }, + "find_slo_definitions_response": { + "title": "Find SLO definitions response", + "description": "A paginated response of SLO definitions matching the query.\n", + "type": "object", + "properties": { + "page": { + "type": "number", + "example": 2 + }, + "perPage": { + "type": "number", + "example": 100 + }, + "total": { + "type": "number", + "example": 123 + }, + "results": { + "type": "array", + "items": { + "$ref": "#/components/schemas/slo_definition_response" + } + } + } + }, "delete_slo_instances_request": { "title": "Delete SLO instances request", "description": "The delete SLO instances request takes a list of SLO id and instance id, then delete the rollup and summary data. This API can be used to remove the staled data of an instance SLO that no longer get updated.\n", diff --git a/x-pack/plugins/observability/docs/openapi/slo/bundled.yaml b/x-pack/plugins/observability/docs/openapi/slo/bundled.yaml index 5aa20726b6a07..643b0d29fea66 100644 --- a/x-pack/plugins/observability/docs/openapi/slo/bundled.yaml +++ b/x-pack/plugins/observability/docs/openapi/slo/bundled.yaml @@ -86,14 +86,14 @@ paths: example: 'slo.name:latency* and slo.tags : "prod"' - name: page in: query - description: The page number to return + description: The page to use for pagination, must be greater or equal than 1 schema: type: integer default: 1 example: 1 - name: perPage in: query - description: The number of SLOs to return per page + description: Number of SLOs returned by page schema: type: integer default: 25 @@ -176,7 +176,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/slo_response' + $ref: '#/components/schemas/slo_with_summary_response' '400': description: Bad request content: @@ -224,7 +224,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/slo_response' + $ref: '#/components/schemas/slo_definition_response' '400': description: Bad request content: @@ -365,6 +365,49 @@ paths: application/json: schema: $ref: '#/components/schemas/404_response' + /s/{spaceId}/api/observability/slos/{sloId}/_reset: + post: + summary: Resets an SLO. + operationId: resetSloOp + description: | + You must have the `write` privileges for the **SLOs** feature in the **Observability** section of the Kibana feature privileges. + tags: + - slo + parameters: + - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/components/parameters/space_id' + - $ref: '#/components/parameters/slo_id' + responses: + '204': + description: Successful request + content: + application/json: + schema: + $ref: '#/components/schemas/slo_definition_response' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/400_response' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '#/components/schemas/401_response' + '403': + description: Unauthorized response + content: + application/json: + schema: + $ref: '#/components/schemas/403_response' + '404': + description: Not found response + content: + application/json: + schema: + $ref: '#/components/schemas/404_response' /s/{spaceId}/internal/observability/slos/_historical_summary: post: summary: Retrieves the historical summary for a list of SLOs @@ -407,6 +450,68 @@ paths: application/json: schema: $ref: '#/components/schemas/403_response' + /s/{spaceId}/internal/observability/slos/_definitions: + get: + summary: Get the SLO definitions + operationId: getDefinitionsOp + description: | + You must have the `read` privileges for the **SLOs** feature in the **Observability** section of the Kibana feature privileges. + tags: + - slo + parameters: + - $ref: '#/components/parameters/kbn_xsrf' + - $ref: '#/components/parameters/space_id' + - name: includeOutdatedOnly + in: query + description: Indicates if the API returns only outdated SLO or all SLO definitions + schema: + type: boolean + example: true + - name: search + in: query + description: Filters the SLOs by name + schema: + type: string + example: my service availability + - name: page + in: query + description: The page to use for pagination, must be greater or equal than 1 + schema: + type: number + example: 1 + - name: perPage + in: query + description: Number of SLOs returned by page + schema: + type: integer + default: 100 + maximum: 1000 + example: 100 + responses: + '200': + description: Successful request + content: + application/json: + schema: + $ref: '#/components/schemas/find_slo_definitions_response' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '#/components/schemas/400_response' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '#/components/schemas/401_response' + '403': + description: Unauthorized response + content: + application/json: + schema: + $ref: '#/components/schemas/403_response' /s/{spaceId}/api/observability/slos/_delete_instances: post: summary: Batch delete rollup and summary data for the matching list of sloId and instanceId @@ -1103,7 +1208,7 @@ components: example: 0.9836 errorBudget: $ref: '#/components/schemas/error_budget' - slo_response: + slo_with_summary_response: title: SLO response type: object required: @@ -1123,6 +1228,7 @@ components: - tags - createdAt - updatedAt + - version properties: id: description: The identifier of the SLO. @@ -1192,6 +1298,10 @@ components: description: The last update date type: string example: '2023-01-12T10:03:19.000Z' + version: + description: The internal SLO version + type: number + example: 2 find_slo_response: title: Find SLO response description: | @@ -1210,7 +1320,7 @@ components: results: type: array items: - $ref: '#/components/schemas/slo_response' + $ref: '#/components/schemas/slo_with_summary_response' 400_response: title: Bad request type: object @@ -1386,6 +1496,92 @@ components: type: array items: type: string + slo_definition_response: + title: SLO definition response + type: object + required: + - id + - name + - description + - indicator + - timeWindow + - budgetingMethod + - objective + - settings + - revision + - enabled + - groupBy + - tags + - createdAt + - updatedAt + - version + properties: + id: + description: The identifier of the SLO. + type: string + example: 8853df00-ae2e-11ed-90af-09bb6422b258 + name: + description: The name of the SLO. + type: string + example: My Service SLO + description: + description: The description of the SLO. + type: string + example: My SLO description + indicator: + discriminator: + propertyName: type + mapping: + sli.apm.transactionErrorRate: '#/components/schemas/indicator_properties_apm_availability' + sli.kql.custom: '#/components/schemas/indicator_properties_custom_kql' + sli.apm.transactionDuration: '#/components/schemas/indicator_properties_apm_latency' + sli.metric.custom: '#/components/schemas/indicator_properties_custom_metric' + sli.histogram.custom: '#/components/schemas/indicator_properties_histogram' + sli.metric.timeslice: '#/components/schemas/indicator_properties_timeslice_metric' + oneOf: + - $ref: '#/components/schemas/indicator_properties_custom_kql' + - $ref: '#/components/schemas/indicator_properties_apm_availability' + - $ref: '#/components/schemas/indicator_properties_apm_latency' + - $ref: '#/components/schemas/indicator_properties_custom_metric' + - $ref: '#/components/schemas/indicator_properties_histogram' + - $ref: '#/components/schemas/indicator_properties_timeslice_metric' + timeWindow: + $ref: '#/components/schemas/time_window' + budgetingMethod: + $ref: '#/components/schemas/budgeting_method' + objective: + $ref: '#/components/schemas/objective' + settings: + $ref: '#/components/schemas/settings' + revision: + description: The SLO revision + type: number + example: 2 + enabled: + description: Indicate if the SLO is enabled + type: boolean + example: true + groupBy: + description: optional group by field to use to generate an SLO per distinct value + type: string + example: some.field + tags: + description: List of tags + type: array + items: + type: string + createdAt: + description: The creation date + type: string + example: '2023-01-12T10:03:19.000Z' + updatedAt: + description: The last update date + type: string + example: '2023-01-12T10:03:19.000Z' + version: + description: The internal SLO version + type: number + example: 2 historical_summary_request: title: Historical summary request type: object @@ -1416,6 +1612,25 @@ components: example: 0.9836 errorBudget: $ref: '#/components/schemas/error_budget' + find_slo_definitions_response: + title: Find SLO definitions response + description: | + A paginated response of SLO definitions matching the query. + type: object + properties: + page: + type: number + example: 2 + perPage: + type: number + example: 100 + total: + type: number + example: 123 + results: + type: array + items: + $ref: '#/components/schemas/slo_definition_response' delete_slo_instances_request: title: Delete SLO instances request description: | diff --git a/x-pack/plugins/observability/docs/openapi/slo/components/schemas/find_slo_definitions_response.yaml b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/find_slo_definitions_response.yaml new file mode 100644 index 0000000000000..274bdc7016a04 --- /dev/null +++ b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/find_slo_definitions_response.yaml @@ -0,0 +1,18 @@ +title: Find SLO definitions response +description: > + A paginated response of SLO definitions matching the query. +type: object +properties: + page: + type: number + example: 2 + perPage: + type: number + example: 100 + total: + type: number + example: 123 + results: + type: array + items: + $ref: 'slo_definition_response.yaml' \ No newline at end of file diff --git a/x-pack/plugins/observability/docs/openapi/slo/components/schemas/find_slo_response.yaml b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/find_slo_response.yaml index 36a701efa34f4..b94aa6e6dc1c5 100644 --- a/x-pack/plugins/observability/docs/openapi/slo/components/schemas/find_slo_response.yaml +++ b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/find_slo_response.yaml @@ -15,4 +15,4 @@ properties: results: type: array items: - $ref: 'slo_response.yaml' \ No newline at end of file + $ref: 'slo_with_summary_response.yaml' \ No newline at end of file diff --git a/x-pack/plugins/observability/docs/openapi/slo/components/schemas/slo_definition_response.yaml b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/slo_definition_response.yaml new file mode 100644 index 0000000000000..0b4ffa774d10f --- /dev/null +++ b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/slo_definition_response.yaml @@ -0,0 +1,85 @@ +title: SLO definition response +type: object +required: + - id + - name + - description + - indicator + - timeWindow + - budgetingMethod + - objective + - settings + - revision + - enabled + - groupBy + - tags + - createdAt + - updatedAt + - version +properties: + id: + description: The identifier of the SLO. + type: string + example: 8853df00-ae2e-11ed-90af-09bb6422b258 + name: + description: The name of the SLO. + type: string + example: My Service SLO + description: + description: The description of the SLO. + type: string + example: My SLO description + indicator: + discriminator: + propertyName: type + mapping: + sli.apm.transactionErrorRate: './indicator_properties_apm_availability.yaml' + sli.kql.custom: './indicator_properties_custom_kql.yaml' + sli.apm.transactionDuration: './indicator_properties_apm_latency.yaml' + sli.metric.custom: './indicator_properties_custom_metric.yaml' + sli.histogram.custom: './indicator_properties_histogram.yaml' + sli.metric.timeslice: './indicator_properties_timeslice_metric.yaml' + oneOf: + - $ref: "indicator_properties_custom_kql.yaml" + - $ref: "indicator_properties_apm_availability.yaml" + - $ref: "indicator_properties_apm_latency.yaml" + - $ref: "indicator_properties_custom_metric.yaml" + - $ref: "indicator_properties_histogram.yaml" + - $ref: "indicator_properties_timeslice_metric.yaml" + timeWindow: + $ref: "time_window.yaml" + budgetingMethod: + $ref: "budgeting_method.yaml" + objective: + $ref: "objective.yaml" + settings: + $ref: "settings.yaml" + revision: + description: The SLO revision + type: number + example: 2 + enabled: + description: Indicate if the SLO is enabled + type: boolean + example: true + groupBy: + description: optional group by field to use to generate an SLO per distinct value + type: string + example: "some.field" + tags: + description: List of tags + type: array + items: + type: string + createdAt: + description: The creation date + type: string + example: "2023-01-12T10:03:19.000Z" + updatedAt: + description: The last update date + type: string + example: "2023-01-12T10:03:19.000Z" + version: + description: The internal SLO version + type: number + example: 2 \ No newline at end of file diff --git a/x-pack/plugins/observability/docs/openapi/slo/components/schemas/slo_response.yaml b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/slo_with_summary_response.yaml similarity index 96% rename from x-pack/plugins/observability/docs/openapi/slo/components/schemas/slo_response.yaml rename to x-pack/plugins/observability/docs/openapi/slo/components/schemas/slo_with_summary_response.yaml index bd58e88c7b641..df8e35996feb3 100644 --- a/x-pack/plugins/observability/docs/openapi/slo/components/schemas/slo_response.yaml +++ b/x-pack/plugins/observability/docs/openapi/slo/components/schemas/slo_with_summary_response.yaml @@ -17,6 +17,7 @@ required: - tags - createdAt - updatedAt + - version properties: id: description: The identifier of the SLO. @@ -86,3 +87,7 @@ properties: description: The last update date type: string example: "2023-01-12T10:03:19.000Z" + version: + description: The internal SLO version + type: number + example: 2 \ No newline at end of file diff --git a/x-pack/plugins/observability/docs/openapi/slo/entrypoint.yaml b/x-pack/plugins/observability/docs/openapi/slo/entrypoint.yaml index 910f795aa40a7..10ed40a98d479 100644 --- a/x-pack/plugins/observability/docs/openapi/slo/entrypoint.yaml +++ b/x-pack/plugins/observability/docs/openapi/slo/entrypoint.yaml @@ -20,11 +20,15 @@ paths: "/s/{spaceId}/api/observability/slos/{sloId}": $ref: "paths/s@{spaceid}@api@slos@{sloid}.yaml" "/s/{spaceId}/api/observability/slos/{sloId}/enable": - $ref: "paths/s@{spaceid}@api@slos@{sloid}@{enable}.yaml" + $ref: "paths/s@{spaceid}@api@slos@{sloid}@enable.yaml" "/s/{spaceId}/api/observability/slos/{sloId}/disable": - $ref: "paths/s@{spaceid}@api@slos@{sloid}@{disable}.yaml" + $ref: "paths/s@{spaceid}@api@slos@{sloid}@disable.yaml" + "/s/{spaceId}/api/observability/slos/{sloId}/_reset": + $ref: "paths/s@{spaceid}@api@slos@{sloid}@_reset.yaml" "/s/{spaceId}/internal/observability/slos/_historical_summary": $ref: "paths/s@{spaceid}@api@slos@_historical_summary.yaml" + "/s/{spaceId}/internal/observability/slos/_definitions": + $ref: "paths/s@{spaceid}@api@slos@_definitions.yaml" "/s/{spaceId}/api/observability/slos/_delete_instances": $ref: "paths/s@{spaceid}@api@slos@_delete_instances.yaml" components: diff --git a/x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos.yaml b/x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos.yaml index b606a0aac05fb..782e8fb477f94 100644 --- a/x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos.yaml +++ b/x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos.yaml @@ -68,14 +68,14 @@ get: example: 'slo.name:latency* and slo.tags : "prod"' - name: page in: query - description: The page number to return + description: The page to use for pagination, must be greater or equal than 1 schema: type: integer default: 1 example: 1 - name: perPage in: query - description: The number of SLOs to return per page + description: Number of SLOs returned by page schema: type: integer default: 25 diff --git a/x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos@_definitions.yaml b/x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos@_definitions.yaml new file mode 100644 index 0000000000000..508c3cc86f8fe --- /dev/null +++ b/x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos@_definitions.yaml @@ -0,0 +1,62 @@ +get: + summary: Get the SLO definitions + operationId: getDefinitionsOp + description: > + You must have the `read` privileges for the **SLOs** feature in the + **Observability** section of the Kibana feature privileges. + tags: + - slo + parameters: + - $ref: ../components/headers/kbn_xsrf.yaml + - $ref: ../components/parameters/space_id.yaml + - name: includeOutdatedOnly + in: query + description: Indicates if the API returns only outdated SLO or all SLO definitions + schema: + type: boolean + example: true + - name: search + in: query + description: Filters the SLOs by name + schema: + type: string + example: 'my service availability' + - name: page + in: query + description: The page to use for pagination, must be greater or equal than 1 + schema: + type: number + example: 1 + - name: perPage + in: query + description: Number of SLOs returned by page + schema: + type: integer + default: 100 + maximum: 1000 + example: 100 + responses: + '200': + description: Successful request + content: + application/json: + schema: + $ref: '../components/schemas/find_slo_definitions_response.yaml' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '../components/schemas/400_response.yaml' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '../components/schemas/401_response.yaml' + '403': + description: Unauthorized response + content: + application/json: + schema: + $ref: '../components/schemas/403_response.yaml' diff --git a/x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos@{sloid}.yaml b/x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos@{sloid}.yaml index a7740b7517464..76d8f0eb640da 100644 --- a/x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos@{sloid}.yaml +++ b/x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos@{sloid}.yaml @@ -22,7 +22,7 @@ get: content: application/json: schema: - $ref: '../components/schemas/slo_response.yaml' + $ref: '../components/schemas/slo_with_summary_response.yaml' '400': description: Bad request content: @@ -72,7 +72,7 @@ put: content: application/json: schema: - $ref: '../components/schemas/slo_response.yaml' + $ref: '../components/schemas/slo_definition_response.yaml' '400': description: Bad request content: diff --git a/x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos@{sloid}@_reset.yaml b/x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos@{sloid}@_reset.yaml new file mode 100644 index 0000000000000..6739d3df78328 --- /dev/null +++ b/x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos@{sloid}@_reset.yaml @@ -0,0 +1,43 @@ +post: + summary: Resets an SLO. + operationId: resetSloOp + description: > + You must have the `write` privileges for the **SLOs** feature in the + **Observability** section of the Kibana feature privileges. + tags: + - slo + parameters: + - $ref: ../components/headers/kbn_xsrf.yaml + - $ref: ../components/parameters/space_id.yaml + - $ref: ../components/parameters/slo_id.yaml + responses: + '204': + description: Successful request + content: + application/json: + schema: + $ref: '../components/schemas/slo_definition_response.yaml' + '400': + description: Bad request + content: + application/json: + schema: + $ref: '../components/schemas/400_response.yaml' + '401': + description: Unauthorized response + content: + application/json: + schema: + $ref: '../components/schemas/401_response.yaml' + '403': + description: Unauthorized response + content: + application/json: + schema: + $ref: '../components/schemas/403_response.yaml' + '404': + description: Not found response + content: + application/json: + schema: + $ref: '../components/schemas/404_response.yaml' diff --git a/x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos@{sloid}@{disable}.yaml b/x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos@{sloid}@disable.yaml similarity index 100% rename from x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos@{sloid}@{disable}.yaml rename to x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos@{sloid}@disable.yaml diff --git a/x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos@{sloid}@{enable}.yaml b/x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos@{sloid}@enable.yaml similarity index 100% rename from x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos@{sloid}@{enable}.yaml rename to x-pack/plugins/observability/docs/openapi/slo/paths/s@{spaceid}@api@slos@{sloid}@enable.yaml diff --git a/x-pack/plugins/observability/kibana.jsonc b/x-pack/plugins/observability/kibana.jsonc index 75c4e164c298a..d5633ad9f36fe 100644 --- a/x-pack/plugins/observability/kibana.jsonc +++ b/x-pack/plugins/observability/kibana.jsonc @@ -21,6 +21,7 @@ "dataViewEditor", "embeddable", "uiActions", + "presentationUtil", "exploratoryView", "features", "files", @@ -29,6 +30,7 @@ "observabilityShared", "observabilityAIAssistant", "ruleRegistry", + "taskManager", "triggersActionsUi", "security", "share", @@ -54,7 +56,8 @@ "kibanaUtils", "unifiedSearch", "stackAlerts", - "spaces" + "spaces", + "embeddable" ], "extraPublicDirs": [ "common" diff --git a/x-pack/plugins/observability/public/application/application.test.tsx b/x-pack/plugins/observability/public/application/application.test.tsx index eb7da89d3441d..5b01eb93b788b 100644 --- a/x-pack/plugins/observability/public/application/application.test.tsx +++ b/x-pack/plugins/observability/public/application/application.test.tsx @@ -15,7 +15,7 @@ import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; import { ConfigSchema, ObservabilityPublicPluginsStart } from '../plugin'; import { createObservabilityRuleTypeRegistryMock } from '../rules/observability_rule_type_registry_mock'; import { renderApp } from '.'; -import { mockObservabilityAIAssistantService } from '@kbn/observability-ai-assistant-plugin/public'; +import { mockService } from '@kbn/observability-ai-assistant-plugin/public/mock'; describe('renderApp', () => { const originalConsole = global.console; @@ -51,7 +51,7 @@ describe('renderApp', () => { }, }, usageCollection: { reportUiCounter: noop }, - observabilityAIAssistant: { service: mockObservabilityAIAssistantService }, + observabilityAIAssistant: { service: mockService }, } as unknown as ObservabilityPublicPluginsStart; const core = { @@ -77,7 +77,6 @@ describe('renderApp', () => { const config = { unsafe: { alertDetails: { - logs: { enabled: false }, metrics: { enabled: false }, uptime: { enabled: false }, }, diff --git a/x-pack/plugins/observability/public/application/index.tsx b/x-pack/plugins/observability/public/application/index.tsx index 5eb3fa65849cd..7b01bc1f8eeb1 100644 --- a/x-pack/plugins/observability/public/application/index.tsx +++ b/x-pack/plugins/observability/public/application/index.tsx @@ -19,7 +19,6 @@ import { KibanaThemeProvider } from '@kbn/react-kibana-context-theme'; import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app'; import { Storage } from '@kbn/kibana-utils-plugin/public'; import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; -import { ObservabilityAIAssistantProvider } from '@kbn/observability-ai-assistant-plugin/public'; import { PluginContext } from '../context/plugin_context/plugin_context'; import { ConfigSchema, ObservabilityPublicPluginsStart } from '../plugin'; import { routes } from '../routes/routes'; @@ -85,23 +84,24 @@ export const renderApp = ({ const ApplicationUsageTrackingProvider = usageCollection?.components.ApplicationUsageTrackingProvider ?? React.Fragment; const CloudProvider = plugins.cloud?.CloudContextProvider ?? React.Fragment; + const PresentationContextProvider = plugins.presentationUtil?.ContextProvider ?? React.Fragment; ReactDOM.render( - - - - - - + + + + + + - - - - - - , + + + + + + , element ); return () => { diff --git a/x-pack/plugins/observability/public/components/alert_status_indicator.tsx b/x-pack/plugins/observability/public/components/alert_status_indicator.tsx index 8090e26d4596f..9dcdbe660e6fe 100644 --- a/x-pack/plugins/observability/public/components/alert_status_indicator.tsx +++ b/x-pack/plugins/observability/public/components/alert_status_indicator.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; import { EuiHealth, EuiText } from '@elastic/eui'; import { ALERT_STATUS_ACTIVE, AlertStatus } from '@kbn/rule-data-utils'; -import { LIGHT_THEME } from '@elastic/charts'; +import { LEGACY_LIGHT_THEME } from '@elastic/charts'; interface AlertStatusIndicatorProps { alertStatus: AlertStatus; @@ -19,7 +19,7 @@ interface AlertStatusIndicatorProps { export function AlertStatusIndicator({ alertStatus, textSize = 'xs' }: AlertStatusIndicatorProps) { if (alertStatus === ALERT_STATUS_ACTIVE) { return ( - + {i18n.translate('xpack.observability.alertsTGrid.statusActiveDescription', { defaultMessage: 'Active', })} @@ -28,7 +28,7 @@ export function AlertStatusIndicator({ alertStatus, textSize = 'xs' }: AlertStat } return ( - + {i18n.translate('xpack.observability.alertsTGrid.statusRecoveredDescription', { defaultMessage: 'Recovered', diff --git a/x-pack/plugins/observability/public/components/burn_rate_rule_editor/budget_consumed.tsx b/x-pack/plugins/observability/public/components/burn_rate_rule_editor/budget_consumed.tsx index 85806a8b8d4f5..005cefec1e30f 100644 --- a/x-pack/plugins/observability/public/components/burn_rate_rule_editor/budget_consumed.tsx +++ b/x-pack/plugins/observability/public/components/burn_rate_rule_editor/budget_consumed.tsx @@ -8,7 +8,6 @@ import { EuiFieldNumber, EuiFormRow, EuiIconTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { ChangeEvent, useState } from 'react'; -import numeral from '@elastic/numeral'; interface Props { initialBurnRate?: number; @@ -28,6 +27,7 @@ export function BudgetConsumed({ const [budgetConsumed, setBudgetConsumed] = useState( ((initialBurnRate * longLookbackWindowInHours) / sloTimeWindowInHours) * 100 ); + const [formattedValue, setFormattedValue] = useState(budgetConsumed.toFixed(2)); const hasError = errors !== undefined && errors.length > 0; const onBudgetConsumedChanged = (event: ChangeEvent) => { @@ -60,8 +60,15 @@ export function BudgetConsumed({ step={0.01} min={0.01} max={100} - value={numeral(budgetConsumed).format('0[.0]')} - onChange={(event) => onBudgetConsumedChanged(event)} + value={formattedValue} + onChange={(event) => { + onBudgetConsumedChanged(event); + setFormattedValue(event.target.value); + }} + onBlur={(event) => { + const value = event.target.value; + setFormattedValue(Number(value).toFixed(2)); + }} data-test-subj="budgetConsumed" /> diff --git a/x-pack/plugins/observability/public/components/burn_rate_rule_editor/burn_rate.tsx b/x-pack/plugins/observability/public/components/burn_rate_rule_editor/burn_rate.tsx index ac7b92950fc96..1e76ddec456e2 100644 --- a/x-pack/plugins/observability/public/components/burn_rate_rule_editor/burn_rate.tsx +++ b/x-pack/plugins/observability/public/components/burn_rate_rule_editor/burn_rate.tsx @@ -8,7 +8,6 @@ import { EuiFieldNumber, EuiFormRow, EuiIconTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { ChangeEvent, useState } from 'react'; -import numeral from '@elastic/numeral'; interface Props { initialBurnRate?: number; @@ -20,6 +19,7 @@ interface Props { export function BurnRate({ onChange, initialBurnRate = 1, maxBurnRate, errors }: Props) { const [burnRate, setBurnRate] = useState(initialBurnRate); const hasError = errors !== undefined && errors.length > 0; + const [formattedValue, setFormattedValue] = useState(burnRate.toFixed(2)); const onBurnRateChange = (event: ChangeEvent) => { const value = Number(event.target.value); @@ -51,8 +51,15 @@ export function BurnRate({ onChange, initialBurnRate = 1, maxBurnRate, errors }: step={0.01} min={0.01} max={maxBurnRate} - value={numeral(burnRate).format('0[.0]')} - onChange={(event) => onBurnRateChange(event)} + value={formattedValue} + onChange={(event) => { + onBurnRateChange(event); + setFormattedValue(event.target.value); + }} + onBlur={(event) => { + const value = event.target.value; + setFormattedValue(Number(value).toFixed(2)); + }} data-test-subj="burnRate" /> 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 8eb9ca972d28c..44a0a55e4e083 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 @@ -111,7 +111,7 @@ export function BurnRateRuleEditor(props: Props) { size="s" title={i18n.translate('xpack.observability.slo.rules.groupByMessage', { defaultMessage: - 'The SLO you selected has been created with a partition on "{groupByField}". This rule will monitor and generate an alert for every instance found in the partition field.', + 'The SLO you selected has been created with a group-by on "{groupByField}". This rule will monitor and generate an alert for every instance found in the group-by field.', values: { groupByField: selectedSlo.groupBy }, })} /> diff --git a/x-pack/plugins/observability/public/components/burn_rate_rule_editor/slo_selector.tsx b/x-pack/plugins/observability/public/components/burn_rate_rule_editor/slo_selector.tsx index 5ec21da2efc1c..6e35ce265bad5 100644 --- a/x-pack/plugins/observability/public/components/burn_rate_rule_editor/slo_selector.tsx +++ b/x-pack/plugins/observability/public/components/burn_rate_rule_editor/slo_selector.tsx @@ -22,7 +22,7 @@ function SloSelector({ initialSlo, onSelected, errors }: Props) { const [options, setOptions] = useState>>([]); const [selectedOptions, setSelectedOptions] = useState>>(); const [searchValue, setSearchValue] = useState(''); - const { isLoading, data: sloList } = useFetchSloDefinitions({ name: searchValue }); + const { isLoading, data } = useFetchSloDefinitions({ name: searchValue }); const hasError = errors !== undefined && errors.length > 0; useEffect(() => { @@ -30,17 +30,17 @@ function SloSelector({ initialSlo, onSelected, errors }: Props) { }, [initialSlo]); useEffect(() => { - const isLoadedWithData = !isLoading && sloList !== undefined; + const isLoadedWithData = !isLoading && !!data?.results; const opts: Array> = isLoadedWithData - ? sloList.map((slo) => ({ value: slo.id, label: slo.name })) + ? data?.results?.map((slo) => ({ value: slo.id, label: slo.name })) : []; setOptions(opts); - }, [isLoading, sloList]); + }, [isLoading, data]); const onChange = (opts: Array>) => { setSelectedOptions(opts); const selectedSlo = - opts.length === 1 ? sloList?.find((slo) => slo.id === opts[0].value) : undefined; + opts.length === 1 ? data?.results?.find((slo) => slo.id === opts[0].value) : undefined; onSelected(selectedSlo); }; diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section.tsx index c9b17b4f48c92..ce9a651a7a1e3 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_details_app_section.tsx @@ -63,7 +63,6 @@ export default function AlertDetailsAppSection({ const [, setDataViewError] = useState(); const ruleParams = rule.params as RuleTypeParams & AlertParams; const chartProps = { - theme: charts.theme.useChartsTheme(), baseTheme: charts.theme.useChartsBaseTheme(), }; const timeRange = getPaddedAlertTimeRange(alert.fields[ALERT_START]!, alert.fields[ALERT_END]); diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_flyout.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/alert_flyout.tsx deleted file mode 100644 index 1a90ffc7460c5..0000000000000 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/alert_flyout.tsx +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React, { useCallback, useContext, useMemo } from 'react'; - -import { OBSERVABILITY_THRESHOLD_RULE_TYPE_ID } from '@kbn/rule-data-utils'; -import { TriggerActionsContext } from './triggers_actions_context'; -import { observabilityRuleCreationValidConsumers } from '../../../../common/constants'; - -interface Props { - visible?: boolean; - setVisible: React.Dispatch>; -} - -export function AlertFlyout(props: Props) { - const { visible, setVisible } = props; - const { triggersActionsUI } = useContext(TriggerActionsContext); - const onCloseFlyout = useCallback(() => setVisible(false), [setVisible]); - const AddAlertFlyout = useMemo( - () => - triggersActionsUI && - triggersActionsUI.getAddRuleFlyout({ - consumer: 'logs', - onClose: onCloseFlyout, - canChangeTrigger: false, - ruleTypeId: OBSERVABILITY_THRESHOLD_RULE_TYPE_ID, - validConsumers: observabilityRuleCreationValidConsumers, - useRuleProducer: true, - }), - [triggersActionsUI, onCloseFlyout] - ); - - return <>{visible && AddAlertFlyout}; -} - -export function PrefilledThresholdAlertFlyout({ onClose }: { onClose(): void }) { - return ; -} diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/criterion_preview_chart/criterion_preview_chart.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/criterion_preview_chart/criterion_preview_chart.tsx index 5669f7a190f81..806844e2531f6 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/criterion_preview_chart/criterion_preview_chart.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/criterion_preview_chart/criterion_preview_chart.tsx @@ -7,7 +7,7 @@ import React, { useMemo } from 'react'; import { niceTimeFormatter } from '@elastic/charts'; -import { Theme, LIGHT_THEME, DARK_THEME } from '@elastic/charts'; +import { Theme, LEGACY_LIGHT_THEME, LEGACY_DARK_THEME } from '@elastic/charts'; import { i18n } from '@kbn/i18n'; import { EuiLoadingChart, EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -81,9 +81,9 @@ export const getDomain = (series: Series, stacked: boolean = false) => { return { yMin: min || 0, yMax: max || 0, xMin: minTimestamp, xMax: maxTimestamp }; }; -// TODO use the EUI charts theme see src/plugins/charts/public/services/theme/README.md +// TODO connect to charts.theme service see src/plugins/charts/public/services/theme/README.md export const getChartTheme = (isDarkMode: boolean): Theme => { - return isDarkMode ? DARK_THEME : LIGHT_THEME; + return isDarkMode ? LEGACY_DARK_THEME : LEGACY_LIGHT_THEME; }; export const EmptyContainer: React.FC = ({ children }) => ( diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/custom_equation/custom_equation_editor.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/custom_equation/custom_equation_editor.tsx index e15248d72c497..98627c9ca80d5 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/custom_equation/custom_equation_editor.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/custom_equation/custom_equation_editor.tsx @@ -16,7 +16,7 @@ import { EuiExpression, EuiPopover, } from '@elastic/eui'; -import React, { useState, useCallback, useMemo } from 'react'; +import React, { useState, useCallback, useMemo, useEffect } from 'react'; import { range, first, xor, debounce } from 'lodash'; import { IErrorObject } from '@kbn/triggers-actions-ui-plugin/public'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -64,9 +64,14 @@ export function CustomEquationEditor({ expression?.metrics ?? [NEW_METRIC] ); const [customEqPopoverOpen, setCustomEqPopoverOpen] = useState(false); - const [equation, setEquation] = useState(expression?.equation || undefined); + const [equation, setEquation] = useState(expression?.equation); const debouncedOnChange = useMemo(() => debounce(onChange, 500), [onChange]); + useEffect(() => { + setCustomMetrics(expression?.metrics ?? [NEW_METRIC]); + setEquation(expression?.equation); + }, [expression?.metrics, expression?.equation]); + const handleAddNewRow = useCallback(() => { setCustomMetrics((previous) => { const currentVars = previous?.map((m) => m.name) ?? []; diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/custom_threshold.stories.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/custom_threshold.stories.tsx index 9598aabcbf70f..674ce1c579756 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/custom_threshold.stories.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/custom_threshold.stories.tsx @@ -8,7 +8,6 @@ import React from 'react'; import { ComponentMeta } from '@storybook/react'; import { LIGHT_THEME } from '@elastic/charts'; -import { EUI_CHARTS_THEME_LIGHT } from '@elastic/eui/dist/eui_charts_theme'; import { Comparator } from '../../../../common/custom_threshold_rule/types'; import { Props, Threshold as Component } from './custom_threshold'; @@ -31,7 +30,7 @@ export default { } as ComponentMeta; const defaultProps: Props = { - chartProps: { theme: EUI_CHARTS_THEME_LIGHT.theme, baseTheme: LIGHT_THEME }, + chartProps: { baseTheme: LIGHT_THEME }, comparator: Comparator.GT, id: 'componentId', threshold: [90], diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/custom_threshold.test.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/custom_threshold.test.tsx index 7731792f7b322..fbb728b4b81aa 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/custom_threshold.test.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/custom_threshold.test.tsx @@ -6,7 +6,6 @@ */ import { LIGHT_THEME } from '@elastic/charts'; -import { EUI_CHARTS_THEME_LIGHT } from '@elastic/eui/dist/eui_charts_theme'; import { render } from '@testing-library/react'; import { Props, Threshold } from './custom_threshold'; @@ -16,7 +15,7 @@ import { Comparator } from '../../../../common/custom_threshold_rule/types'; describe('Threshold', () => { const renderComponent = (props: Partial = {}) => { const defaultProps: Props = { - chartProps: { theme: EUI_CHARTS_THEME_LIGHT.theme, baseTheme: LIGHT_THEME }, + chartProps: { baseTheme: LIGHT_THEME }, comparator: Comparator.GT, id: 'componentId', threshold: [90], diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/custom_threshold.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/custom_threshold.tsx index 5f28676eb37b6..7e9fbfce9d682 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/custom_threshold.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/custom_threshold.tsx @@ -13,7 +13,7 @@ import { i18n } from '@kbn/i18n'; import { Comparator } from '../../../../common/custom_threshold_rule/types'; export interface ChartProps { - theme: PartialTheme; + theme?: PartialTheme; baseTheme: Theme; } diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/expression_chart.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/expression_chart.tsx index c18b2860df95d..010642acdf551 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/expression_chart.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/components/expression_chart.tsx @@ -15,6 +15,7 @@ import { RectAnnotation, Settings, Tooltip, + LEGACY_LIGHT_THEME, } from '@elastic/charts'; import { EuiText } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -192,6 +193,8 @@ export function ExpressionChart({ tooltip: { visible: true }, }} theme={getChartTheme(isDarkMode)} + // TODO connect to charts.theme service see src/plugins/charts/public/services/theme/README.md + baseTheme={LEGACY_LIGHT_THEME} locale={i18n.getLocale()} /> diff --git a/x-pack/plugins/observability/public/components/custom_threshold/components/metrics_alert_dropdown.tsx b/x-pack/plugins/observability/public/components/custom_threshold/components/metrics_alert_dropdown.tsx deleted file mode 100644 index 91b97161f78da..0000000000000 --- a/x-pack/plugins/observability/public/components/custom_threshold/components/metrics_alert_dropdown.tsx +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; -import React, { useState, useCallback, useMemo } from 'react'; -import { - EuiPopover, - EuiHeaderLink, - EuiContextMenu, - EuiContextMenuPanelDescriptor, -} from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n-react'; -import { useKibana } from '@kbn/kibana-react-plugin/public'; -import { InfraClientStartDeps } from '../types'; -import { PrefilledThresholdAlertFlyout } from './alert_flyout'; - -type VisibleFlyoutType = 'inventory' | 'threshold' | null; - -export function MetricsAlertDropdown() { - const [popoverOpen, setPopoverOpen] = useState(false); - const [visibleFlyoutType, setVisibleFlyoutType] = useState(null); - const uiCapabilities = useKibana().services.application?.capabilities; - const { - services: { observability }, - } = useKibana(); - const canCreateAlerts = useMemo( - () => Boolean(uiCapabilities?.infrastructure?.save), - [uiCapabilities] - ); - - const closeFlyout = useCallback(() => setVisibleFlyoutType(null), [setVisibleFlyoutType]); - - const closePopover = useCallback(() => { - setPopoverOpen(false); - }, [setPopoverOpen]); - - const togglePopover = useCallback(() => { - setPopoverOpen(!popoverOpen); - }, [setPopoverOpen, popoverOpen]); - const infrastructureAlertsPanel = useMemo( - () => ({ - id: 1, - title: i18n.translate( - 'xpack.observability.customThreshold.rule.infrastructureDropdownTitle', - { - defaultMessage: 'Infrastructure rules', - } - ), - items: [ - { - 'data-test-subj': 'inventory-alerts-create-rule', - name: i18n.translate( - 'xpack.observability.customThreshold.rule.createInventoryRuleButton', - { - defaultMessage: 'Create inventory rule', - } - ), - onClick: () => { - closePopover(); - setVisibleFlyoutType('inventory'); - }, - }, - ], - }), - [setVisibleFlyoutType, closePopover] - ); - - const metricsAlertsPanel = useMemo( - () => ({ - id: 2, - title: i18n.translate('xpack.observability.customThreshold.rule.metricsDropdownTitle', { - defaultMessage: 'Metrics rules', - }), - items: [ - { - 'data-test-subj': 'metrics-threshold-alerts-create-rule', - name: i18n.translate( - 'xpack.observability.customThreshold.rule.createThresholdRuleButton', - { - defaultMessage: 'Create threshold rule', - } - ), - onClick: () => { - closePopover(); - setVisibleFlyoutType('threshold'); - }, - }, - ], - }), - [setVisibleFlyoutType, closePopover] - ); - - const manageRulesLinkProps = observability.useRulesLink(); - - const manageAlertsMenuItem = useMemo( - () => ({ - name: i18n.translate('xpack.observability.customThreshold.rule.manageRules', { - defaultMessage: 'Manage rules', - }), - icon: 'tableOfContents', - onClick: manageRulesLinkProps.onClick, - }), - [manageRulesLinkProps] - ); - - const firstPanelMenuItems: EuiContextMenuPanelDescriptor['items'] = useMemo( - () => - canCreateAlerts - ? [ - { - 'data-test-subj': 'inventory-alerts-menu-option', - name: i18n.translate( - 'xpack.observability.customThreshold.rule.infrastructureDropdownMenu', - { - defaultMessage: 'Infrastructure', - } - ), - panel: 1, - }, - { - 'data-test-subj': 'metrics-threshold-alerts-menu-option', - name: i18n.translate('xpack.observability.customThreshold.rule.metricsDropdownMenu', { - defaultMessage: 'Metrics', - }), - panel: 2, - }, - manageAlertsMenuItem, - ] - : [manageAlertsMenuItem], - [canCreateAlerts, manageAlertsMenuItem] - ); - - const panels: EuiContextMenuPanelDescriptor[] = useMemo( - () => - [ - { - id: 0, - title: i18n.translate('xpack.observability.customThreshold.rule.alertDropdownTitle', { - defaultMessage: 'Alerts and rules', - }), - items: firstPanelMenuItems, - }, - ].concat(canCreateAlerts ? [infrastructureAlertsPanel, metricsAlertsPanel] : []), - [infrastructureAlertsPanel, metricsAlertsPanel, firstPanelMenuItems, canCreateAlerts] - ); - return ( - <> - - - - } - isOpen={popoverOpen} - closePopover={closePopover} - > - - - - - ); -} - -interface AlertFlyoutProps { - visibleFlyoutType: VisibleFlyoutType; - onClose(): void; -} - -function AlertFlyout({ visibleFlyoutType, onClose }: AlertFlyoutProps) { - switch (visibleFlyoutType) { - case 'threshold': - return ; - default: - return null; - } -} diff --git a/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.test.tsx b/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.test.tsx index 0f1742469c312..ce1c9527904cc 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.test.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.test.tsx @@ -6,16 +6,16 @@ */ import React from 'react'; -import { act } from 'react-dom/test-utils'; -import { QueryClientProvider } from '@tanstack/react-query'; import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; import { queryClient } from '@kbn/osquery-plugin/public/query_client'; import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers'; - +import { QueryClientProvider } from '@tanstack/react-query'; +import { act } from 'react-dom/test-utils'; import { Aggregators, Comparator } from '../../../common/custom_threshold_rule/types'; import { useKibana } from '../../utils/kibana_react'; import { kibanaStartMock } from '../../utils/kibana_react.mock'; import Expressions from './custom_threshold_rule_expression'; +import { CustomThresholdPrefillOptions } from './types'; jest.mock('../../utils/kibana_react'); jest.mock('./components/preview_chart/preview_chart', () => ({ @@ -38,7 +38,10 @@ describe('Expression', () => { mockKibana(); }); - async function setup() { + async function setup( + currentOptions?: CustomThresholdPrefillOptions, + customRuleParams?: Record + ) { const ruleParams = { criteria: [], groupBy: undefined, @@ -50,6 +53,11 @@ describe('Expression', () => { language: 'kuery', }, }, + ...customRuleParams, + }; + const metadata = { + currentOptions, + adHocDataViewList: [], }; const wrapper = mountWithIntl( @@ -61,9 +69,7 @@ describe('Expression', () => { errors={{}} setRuleParams={(key, value) => Reflect.set(ruleParams, key, value)} setRuleProperty={() => {}} - metadata={{ - adHocDataViewList: [], - }} + metadata={metadata} dataViews={dataViewMock} onChangeMetaData={jest.fn()} /> @@ -81,6 +87,59 @@ describe('Expression', () => { return { wrapper, update, ruleParams }; } + const updateUseKibanaMock = (mockedIndex: any) => { + const mockedDataView = { + getIndexPattern: () => 'mockedIndexPattern', + getName: () => 'mockedName', + ...mockedIndex, + }; + const mockedSearchSource = { + id: 'data_source', + shouldOverwriteDataViewType: false, + requestStartHandlers: [], + inheritOptions: {}, + history: [], + fields: { + index: mockedIndex, + }, + getField: jest.fn(() => mockedDataView), + setField: jest.fn(), + getSerializedFields: jest.fn().mockReturnValue({ index: mockedIndex }), + dependencies: { + aggs: { + types: {}, + }, + }, + }; + const kibanaMock = kibanaStartMock.startContract(); + useKibanaMock.mockReturnValue({ + ...kibanaMock, + services: { + ...kibanaMock.services, + data: { + dataViews: { + create: jest.fn(), + getDefaultDataView: jest.fn(), + }, + query: { + timefilter: { + timefilter: jest.fn(), + }, + queryString: { + getDefaultQuery: jest.fn(), + }, + }, + search: { + searchSource: { + create: jest.fn(() => mockedSearchSource), + createEmpty: jest.fn(() => mockedSearchSource), + }, + }, + }, + }, + }); + }; + it('should use default metrics', async () => { const { ruleParams } = await setup(); expect(ruleParams.criteria).toEqual([ @@ -99,6 +158,41 @@ describe('Expression', () => { ]); }); + it('should prefill the rule using the context metadata', async () => { + const index = 'changedMockedIndex'; + const currentOptions: CustomThresholdPrefillOptions = { + groupBy: ['host.hostname'], + filterQuery: 'foo', + searchConfiguration: { index }, + criteria: [ + { + metrics: [ + { name: 'A', aggType: Aggregators.AVERAGE, field: 'system.load.1' }, + { name: 'B', aggType: Aggregators.CARDINALITY, field: 'system.cpu.user.pct' }, + ], + }, + ], + }; + + const { ruleParams } = await setup(currentOptions, { searchConfiguration: undefined }); + + expect(ruleParams.groupBy).toEqual(['host.hostname']); + expect(ruleParams.searchConfiguration.query.query).toBe('foo'); + expect(ruleParams.searchConfiguration.index).toBe(index); + expect(ruleParams.criteria).toEqual([ + { + metrics: [ + { name: 'A', aggType: Aggregators.AVERAGE, field: 'system.load.1' }, + { name: 'B', aggType: Aggregators.CARDINALITY, field: 'system.cpu.user.pct' }, + ], + comparator: Comparator.GT, + threshold: [100], + timeSize: 1, + timeUnit: 'm', + }, + ]); + }); + it('should show an error message when searchSource throws an error', async () => { const errorMessage = 'Error in searchSource create'; const kibanaMock = kibanaStartMock.startContract(); @@ -140,49 +234,7 @@ describe('Expression', () => { // We should not provide timeFieldName here to show thresholdRuleDataViewErrorNoTimestamp error // timeFieldName: '@timestamp', }; - const mockedDataView = { - getIndexPattern: () => 'mockedIndexPattern', - getName: () => 'mockedName', - ...mockedIndex, - }; - const mockedSearchSource = { - id: 'data_source', - shouldOverwriteDataViewType: false, - requestStartHandlers: [], - inheritOptions: {}, - history: [], - fields: { - index: mockedIndex, - }, - getField: jest.fn(() => mockedDataView), - dependencies: { - aggs: { - types: {}, - }, - }, - }; - const kibanaMock = kibanaStartMock.startContract(); - useKibanaMock.mockReturnValue({ - ...kibanaMock, - services: { - ...kibanaMock.services, - data: { - dataViews: { - create: jest.fn(), - }, - query: { - timefilter: { - timefilter: jest.fn(), - }, - }, - search: { - searchSource: { - create: jest.fn(() => mockedSearchSource), - }, - }, - }, - }, - }); + updateUseKibanaMock(mockedIndex); const { wrapper } = await setup(); expect( wrapper.find(`[data-test-subj="thresholdRuleDataViewErrorNoTimestamp"]`).first().text() @@ -190,4 +242,19 @@ describe('Expression', () => { 'The selected data view does not have a timestamp field, please select another data view.' ); }); + + it('should use output of getSerializedFields() as searchConfiguration', async () => { + const mockedIndex = { + id: 'c34a7c79-a88b-4b4a-ad19-72f6d24104e4', + title: 'metrics-fake_hosts', + fieldFormatMap: {}, + typeMeta: {}, + timeFieldName: '@timestamp', + }; + updateUseKibanaMock(mockedIndex); + const { ruleParams } = await setup(undefined, { searchConfiguration: undefined }); + expect(ruleParams.searchConfiguration).toEqual({ + index: mockedIndex, + }); + }); }); diff --git a/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx b/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx index 539f8fbf4f58d..0df72511ce559 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx +++ b/x-pack/plugins/observability/public/components/custom_threshold/custom_threshold_rule_expression.tsx @@ -96,14 +96,24 @@ export default function Expressions(props: Props) { let initialSearchConfiguration = ruleParams.searchConfiguration; if (!ruleParams.searchConfiguration || !ruleParams.searchConfiguration.index) { - const newSearchSource = data.search.searchSource.createEmpty(); - newSearchSource.setField('query', data.query.queryString.getDefaultQuery()); - const defaultDataView = await data.dataViews.getDefaultDataView(); - if (defaultDataView) { - newSearchSource.setField('index', defaultDataView); - setDataView(defaultDataView); + if (metadata?.currentOptions?.searchConfiguration) { + initialSearchConfiguration = { + ...metadata.currentOptions.searchConfiguration, + query: { + query: ruleParams.searchConfiguration?.query ?? '', + language: 'kuery', + }, + }; + } else { + const newSearchSource = data.search.searchSource.createEmpty(); + newSearchSource.setField('query', data.query.queryString.getDefaultQuery()); + const defaultDataView = await data.dataViews.getDefaultDataView(); + if (defaultDataView) { + newSearchSource.setField('index', defaultDataView); + setDataView(defaultDataView); + } + initialSearchConfiguration = newSearchSource.getSerializedFields(); } - initialSearchConfiguration = newSearchSource.getSerializedFields(); } try { @@ -151,7 +161,15 @@ export default function Expressions(props: Props) { setTimeSize(ruleParams.criteria[0].timeSize); setTimeUnit(ruleParams.criteria[0].timeUnit); } else { - setRuleParams('criteria', [defaultExpression]); + preFillCriteria(); + } + + if (!ruleParams.filterQuery) { + preFillFilterQuery(); + } + + if (!ruleParams.groupBy) { + preFillGroupBy(); } if (typeof ruleParams.alertOnNoData === 'undefined') { @@ -259,6 +277,42 @@ export default function Expressions(props: Props) { [ruleParams.criteria, setRuleParams] ); + const preFillFilterQuery = useCallback(() => { + const md = metadata; + + if (md && md.currentOptions?.filterQuery) { + setRuleParams('searchConfiguration', { + ...ruleParams.searchConfiguration, + query: { + query: md.currentOptions.filterQuery, + language: 'kuery', + }, + }); + } + }, [metadata, setRuleParams, ruleParams.searchConfiguration]); + + const preFillCriteria = useCallback(() => { + const md = metadata; + if (md?.currentOptions?.criteria?.length) { + setRuleParams( + 'criteria', + md.currentOptions.criteria.map((criterion) => ({ + ...defaultExpression, + ...criterion, + })) + ); + } else { + setRuleParams('criteria', [defaultExpression]); + } + }, [metadata, setRuleParams]); + + const preFillGroupBy = useCallback(() => { + const md = metadata; + if (md && md.currentOptions?.groupBy) { + setRuleParams('groupBy', md.currentOptions.groupBy); + } + }, [metadata, setRuleParams]); + const hasGroupBy = useMemo( () => ruleParams.groupBy && ruleParams.groupBy.length > 0, [ruleParams.groupBy] diff --git a/x-pack/plugins/observability/public/components/custom_threshold/hooks/use_metrics_explorer_data.test.tsx b/x-pack/plugins/observability/public/components/custom_threshold/hooks/use_expression_data.test.tsx similarity index 100% rename from x-pack/plugins/observability/public/components/custom_threshold/hooks/use_metrics_explorer_data.test.tsx rename to x-pack/plugins/observability/public/components/custom_threshold/hooks/use_expression_data.test.tsx diff --git a/x-pack/plugins/observability/public/components/custom_threshold/types.ts b/x-pack/plugins/observability/public/components/custom_threshold/types.ts index dc0ad4eb71627..9753cabbc118f 100644 --- a/x-pack/plugins/observability/public/components/custom_threshold/types.ts +++ b/x-pack/plugins/observability/public/components/custom_threshold/types.ts @@ -15,6 +15,7 @@ import { EmbeddableStart } from '@kbn/embeddable-plugin/public'; import { IStorageWrapper } from '@kbn/kibana-utils-plugin/public'; import { LensPublicStart } from '@kbn/lens-plugin/public'; import { ObservabilitySharedPluginStart } from '@kbn/observability-shared-plugin/public'; +import { OsqueryPluginStart } from '@kbn/osquery-plugin/public'; import { SharePluginStart } from '@kbn/share-plugin/public'; import { SpacesPluginStart } from '@kbn/spaces-plugin/public'; import { @@ -28,11 +29,18 @@ import { CustomMetricExpressionParams, BaseMetricExpressionParams, aggType, + ThresholdParams, + MetricExpressionParams, } from '../../../common/custom_threshold_rule/types'; import { ObservabilityPublicStart } from '../../plugin'; +export type CustomThresholdPrefillOptions = Partial< + Omit & { criteria: Array> } +>; + export interface AlertContextMeta { adHocDataViewList: DataView[]; + currentOptions?: CustomThresholdPrefillOptions; } export type MetricExpression = Omit & { @@ -66,7 +74,7 @@ export interface InfraClientStartDeps { lens: LensPublicStart; observability: ObservabilityPublicStart; observabilityShared: ObservabilitySharedPluginStart; - osquery?: unknown; // OsqueryPluginStart; + osquery?: OsqueryPluginStart; share: SharePluginStart; spaces: SpacesPluginStart; storage: IStorageWrapper; diff --git a/x-pack/plugins/observability/public/components/slo/delete_confirmation_modal/slo_delete_confirmation_modal.tsx b/x-pack/plugins/observability/public/components/slo/delete_confirmation_modal/slo_delete_confirmation_modal.tsx index 0822758859d60..aa5cd00972b49 100644 --- a/x-pack/plugins/observability/public/components/slo/delete_confirmation_modal/slo_delete_confirmation_modal.tsx +++ b/x-pack/plugins/observability/public/components/slo/delete_confirmation_modal/slo_delete_confirmation_modal.tsx @@ -7,11 +7,11 @@ import { EuiConfirmModal } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { ALL_VALUE, SLOWithSummaryResponse } from '@kbn/slo-schema'; +import { ALL_VALUE, SLOResponse, SLOWithSummaryResponse } from '@kbn/slo-schema'; import React from 'react'; export interface SloDeleteConfirmationModalProps { - slo: SLOWithSummaryResponse; + slo: SLOWithSummaryResponse | SLOResponse; onCancel: () => void; onConfirm: () => void; } @@ -42,14 +42,11 @@ export function SloDeleteConfirmationModal({ onConfirm={onConfirm} > {groupBy !== ALL_VALUE - ? i18n.translate( - 'xpack.observability.slo.deleteConfirmationModal.partitionByDisclaimerText', - { - defaultMessage: - 'This SLO has been generated with a partition key on "{partitionKey}". Deleting this SLO definition will result in all instances being deleted.', - values: { partitionKey: groupBy }, - } - ) + ? i18n.translate('xpack.observability.slo.deleteConfirmationModal.groupByDisclaimerText', { + defaultMessage: + 'This SLO has been generated with a group key on "{groupKey}". Deleting this SLO definition will result in all instances being deleted.', + values: { groupKey: groupBy }, + }) : i18n.translate('xpack.observability.slo.deleteConfirmationModal.descriptionText', { defaultMessage: "You can't recover this SLO after deleting it.", })} diff --git a/x-pack/plugins/observability/public/components/slo/reset_confirmation_modal/slo_reset_confirmation_modal.tsx b/x-pack/plugins/observability/public/components/slo/reset_confirmation_modal/slo_reset_confirmation_modal.tsx new file mode 100644 index 0000000000000..9fabcb61efc96 --- /dev/null +++ b/x-pack/plugins/observability/public/components/slo/reset_confirmation_modal/slo_reset_confirmation_modal.tsx @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { EuiConfirmModal } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { SLOResponse, SLOWithSummaryResponse } from '@kbn/slo-schema'; +import React from 'react'; + +export interface SloResetConfirmationModalProps { + slo: SLOWithSummaryResponse | SLOResponse; + onCancel: () => void; + onConfirm: () => void; +} + +export function SloResetConfirmationModal({ + slo, + onCancel, + onConfirm, +}: SloResetConfirmationModalProps) { + const { name } = slo; + return ( + + {i18n.translate('xpack.observability.slo.resetConfirmationModal.descriptionText', { + defaultMessage: 'Resetting this SLO will also regenerate the historical data.', + })} + + ); +} diff --git a/x-pack/plugins/observability/public/components/slo/slo_outdated_callout/index.tsx b/x-pack/plugins/observability/public/components/slo/slo_outdated_callout/index.tsx new file mode 100644 index 0000000000000..dae8f76fb85a5 --- /dev/null +++ b/x-pack/plugins/observability/public/components/slo/slo_outdated_callout/index.tsx @@ -0,0 +1,62 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { EuiButton, EuiCallOut } from '@elastic/eui'; +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { useFetchSloDefinitions } from '../../../hooks/slo/use_fetch_slo_definitions'; +import { useKibana } from '../../../utils/kibana_react'; +import { paths } from '../../../../common/locators/paths'; + +export function SloOutdatedCallout() { + const { + application: { navigateToUrl }, + http: { basePath }, + } = useKibana().services; + + const handleClick = () => { + navigateToUrl(basePath.prepend(paths.observability.slosOutdatedDefinitions)); + }; + + const { isLoading, data } = useFetchSloDefinitions({ includeOutdatedOnly: true }); + if (!isLoading && data && data.total > 0) { + return ( + +

+ +

+

+ + + +

+
+ ); + } + return null; +} diff --git a/x-pack/plugins/observability/public/components/slo/slo_status_badge/slo_group_by_badge.tsx b/x-pack/plugins/observability/public/components/slo/slo_status_badge/slo_group_by_badge.tsx index f79b700ed9be5..dd0aa83bc14cb 100644 --- a/x-pack/plugins/observability/public/components/slo/slo_status_badge/slo_group_by_badge.tsx +++ b/x-pack/plugins/observability/public/components/slo/slo_status_badge/slo_group_by_badge.tsx @@ -26,10 +26,10 @@ export function SloGroupByBadge({ slo, color }: Props) { = { enabled: true, createdAt: now, updatedAt: now, + version: 2, }; export const sloList: FindSLOResponse = { diff --git a/x-pack/plugins/observability/public/embeddable/slo/overview/slo_overview.tsx b/x-pack/plugins/observability/public/embeddable/slo/overview/slo_overview.tsx index 0bd1b7048dcc0..cb0a6492ed310 100644 --- a/x-pack/plugins/observability/public/embeddable/slo/overview/slo_overview.tsx +++ b/x-pack/plugins/observability/public/embeddable/slo/overview/slo_overview.tsx @@ -112,7 +112,7 @@ export function SloOverview({ return (
- + [...sloKeys.all, 'historicalSummary'] as const, historicalSummary: (list: Array<{ sloId: string; instanceId: string }>) => [...sloKeys.historicalSummaries(), list] as const, - definitions: (search: string) => [...sloKeys.all, 'definitions', search] as const, + definitions: (search: string, page: number, perPage: number, includeOutdatedOnly: boolean) => + [...sloKeys.all, 'definitions', search, page, perPage, includeOutdatedOnly] as const, globalDiagnosis: () => [...sloKeys.all, 'globalDiagnosis'] as const, burnRates: (sloId: string, instanceId: string | undefined) => [...sloKeys.all, 'burnRates', sloId, instanceId] as const, diff --git a/x-pack/plugins/observability/public/hooks/slo/use_clone_slo.ts b/x-pack/plugins/observability/public/hooks/slo/use_clone_slo.ts index 4363a18c07612..652b4cbaf52b6 100644 --- a/x-pack/plugins/observability/public/hooks/slo/use_clone_slo.ts +++ b/x-pack/plugins/observability/public/hooks/slo/use_clone_slo.ts @@ -5,83 +5,28 @@ * 2.0. */ -import { IHttpFetchError, ResponseErrorBody } from '@kbn/core/public'; -import { i18n } from '@kbn/i18n'; -import type { CreateSLOInput, CreateSLOResponse, FindSLOResponse } from '@kbn/slo-schema'; -import { QueryKey, useMutation, useQueryClient } from '@tanstack/react-query'; -import { v4 as uuidv4 } from 'uuid'; +import { encode } from '@kbn/rison'; +import { useCallback } from 'react'; +import { SLOWithSummaryResponse } from '@kbn/slo-schema'; +import { paths } from '../../../common/locators/paths'; import { useKibana } from '../../utils/kibana_react'; -import { sloKeys } from './query_key_factory'; - -type ServerError = IHttpFetchError; export function useCloneSlo() { const { - http, - notifications: { toasts }, + http: { basePath }, + application: { navigateToUrl }, } = useKibana().services; - const queryClient = useQueryClient(); - return useMutation< - CreateSLOResponse, - ServerError, - { slo: CreateSLOInput; originalSloId?: string }, - { previousData?: FindSLOResponse; queryKey?: QueryKey } - >( - ['cloneSlo'], - ({ slo }: { slo: CreateSLOInput; originalSloId?: string }) => { - const body = JSON.stringify(slo); - return http.post(`/api/observability/slos`, { body }); + return useCallback( + (slo: SLOWithSummaryResponse) => { + navigateToUrl( + basePath.prepend( + paths.observability.sloCreateWithEncodedForm( + encode({ ...slo, name: `[Copy] ${slo.name}`, id: undefined }) + ) + ) + ); }, - { - onMutate: async ({ slo, originalSloId }) => { - await queryClient.cancelQueries({ queryKey: sloKeys.lists(), exact: false }); - - const queriesData = queryClient.getQueriesData({ - queryKey: sloKeys.lists(), - exact: false, - }); - const [queryKey, previousData] = queriesData?.at(0) ?? []; - - const originalSlo = previousData?.results?.find((el) => el.id === originalSloId); - const optimisticUpdate = { - page: previousData?.page ?? 1, - perPage: previousData?.perPage ?? 25, - total: previousData?.total ? previousData.total + 1 : 1, - results: [ - ...(previousData?.results ?? []), - { ...originalSlo, name: slo.name, id: uuidv4(), summary: undefined }, - ], - }; - - if (queryKey) { - queryClient.setQueryData(queryKey, optimisticUpdate); - } - - return { queryKey, previousData }; - }, - // If the mutation fails, use the context returned from onMutate to roll back - onError: (error, { slo }, context) => { - if (context?.previousData && context?.queryKey) { - queryClient.setQueryData(context.queryKey, context.previousData); - } - - toasts.addError(new Error(error.body?.message ?? error.message), { - title: i18n.translate('xpack.observability.slo.clone.errorNotification', { - defaultMessage: 'Failed to clone {name}', - values: { name: slo.name }, - }), - }); - }, - onSuccess: (_data, { slo }) => { - toasts.addSuccess( - i18n.translate('xpack.observability.slo.clone.successNotification', { - defaultMessage: 'Successfully created {name}', - values: { name: slo.name }, - }) - ); - queryClient.invalidateQueries({ queryKey: sloKeys.lists(), exact: false }); - }, - } + [navigateToUrl, basePath] ); } diff --git a/x-pack/plugins/observability/public/hooks/slo/use_fetch_group_by_cardinality.ts b/x-pack/plugins/observability/public/hooks/slo/use_fetch_group_by_cardinality.ts new file mode 100644 index 0000000000000..928eb7e92482d --- /dev/null +++ b/x-pack/plugins/observability/public/hooks/slo/use_fetch_group_by_cardinality.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 { ALL_VALUE } from '@kbn/slo-schema'; +import { useQuery } from '@tanstack/react-query'; +import { lastValueFrom } from 'rxjs'; +import { useKibana } from '../../utils/kibana_react'; + +export interface UseFetchIndexPatternFieldsResponse { + isLoading: boolean; + isSuccess: boolean; + isError: boolean; + data?: { cardinality: number; isHighCardinality: boolean }; +} + +const HIGH_CARDINALITY_THRESHOLD = 1000; + +export function useFetchGroupByCardinality( + indexPattern: string, + timestampField: string, + groupBy: string +): UseFetchIndexPatternFieldsResponse { + const { data: dataService } = useKibana().services; + + const { isLoading, isError, isSuccess, data } = useQuery({ + queryKey: ['fetchGroupByCardinality', indexPattern, timestampField, groupBy], + queryFn: async ({ signal }) => { + try { + const result = await lastValueFrom( + dataService.search.search({ + params: { + index: indexPattern, + body: { + query: { + bool: { + filter: [{ range: { [timestampField]: { gte: 'now-24h' } } }], + }, + }, + aggs: { + groupByCardinality: { + cardinality: { + field: groupBy, + }, + }, + }, + }, + }, + }) + ); + + // @ts-expect-error Property 'value' does not exist on type 'AggregationsAggregate' + const cardinality = result.rawResponse?.aggregations?.groupByCardinality?.value ?? 0; + return { cardinality, isHighCardinality: cardinality > HIGH_CARDINALITY_THRESHOLD }; + } catch (error) { + throw new Error(`Something went wrong. Error: ${error}`); + } + }, + retry: false, + refetchOnWindowFocus: false, + enabled: + Boolean(indexPattern) && Boolean(timestampField) && Boolean(groupBy) && groupBy !== ALL_VALUE, + }); + + return { isLoading, isError, isSuccess, data }; +} diff --git a/x-pack/plugins/observability/public/hooks/slo/use_fetch_index_pattern_fields.ts b/x-pack/plugins/observability/public/hooks/slo/use_fetch_index_pattern_fields.ts index 3363d501fae22..5aedb92219da6 100644 --- a/x-pack/plugins/observability/public/hooks/slo/use_fetch_index_pattern_fields.ts +++ b/x-pack/plugins/observability/public/hooks/slo/use_fetch_index_pattern_fields.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { FieldSpec } from '@kbn/data-views-plugin/common'; import { useQuery } from '@tanstack/react-query'; import { useKibana } from '../../utils/kibana_react'; @@ -12,14 +13,7 @@ export interface UseFetchIndexPatternFieldsResponse { isLoading: boolean; isSuccess: boolean; isError: boolean; - data: Field[] | undefined; -} - -export interface Field { - name: string; - type: string; - aggregatable: boolean; - searchable: boolean; + data: FieldSpec[] | undefined; } export function useFetchIndexPatternFields( diff --git a/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_definitions.ts b/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_definitions.ts index e74b3570177e4..6b6893afb1189 100644 --- a/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_definitions.ts +++ b/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_definitions.ts @@ -5,46 +5,42 @@ * 2.0. */ -import { FindSloDefinitionsResponse, SLOResponse } from '@kbn/slo-schema'; -import { - QueryObserverResult, - RefetchOptions, - RefetchQueryFilters, - useQuery, -} from '@tanstack/react-query'; +import { FindSLODefinitionsResponse } from '@kbn/slo-schema'; +import { useQuery } from '@tanstack/react-query'; import { useKibana } from '../../utils/kibana_react'; import { sloKeys } from './query_key_factory'; export interface UseFetchSloDefinitionsResponse { + data: FindSLODefinitionsResponse | undefined; isLoading: boolean; isSuccess: boolean; isError: boolean; - data: SLOResponse[] | undefined; - refetch: ( - options?: (RefetchOptions & RefetchQueryFilters) | undefined - ) => Promise>; + refetch: () => void; } interface Params { name?: string; + includeOutdatedOnly?: boolean; + page?: number; + perPage?: number; } -export function useFetchSloDefinitions({ name = '' }: Params): UseFetchSloDefinitionsResponse { +export function useFetchSloDefinitions({ + name = '', + includeOutdatedOnly = false, + page = 1, + perPage = 100, +}: Params): UseFetchSloDefinitionsResponse { const { http } = useKibana().services; const search = name.endsWith('*') ? name : `${name}*`; const { isLoading, isError, isSuccess, data, refetch } = useQuery({ - queryKey: sloKeys.definitions(search), + queryKey: sloKeys.definitions(search, page, perPage, includeOutdatedOnly), queryFn: async ({ signal }) => { try { - const response = await http.get( - '/internal/observability/slos/_definitions', - { - query: { - search, - }, - signal, - } + const response = await http.get( + '/api/observability/slos/_definitions', + { query: { search, includeOutdatedOnly, page, perPage }, signal } ); return response; diff --git a/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_list.ts b/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_list.ts index f3f51de9d4890..5b1296fed67c2 100644 --- a/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_list.ts +++ b/x-pack/plugins/observability/public/hooks/slo/use_fetch_slo_list.ts @@ -9,6 +9,7 @@ import { i18n } from '@kbn/i18n'; import { FindSLOResponse } from '@kbn/slo-schema'; import { useQuery, useQueryClient } from '@tanstack/react-query'; import { useState } from 'react'; +import { DEFAULT_SLO_PAGE_SIZE } from '../../../common/slo/constants'; import { SLO_LONG_REFETCH_INTERVAL, SLO_SHORT_REFETCH_INTERVAL } from '../../constants'; import { useKibana } from '../../utils/kibana_react'; @@ -38,7 +39,7 @@ export function useFetchSloList({ sortBy = 'status', sortDirection = 'desc', shouldRefetch, - perPage, + perPage = DEFAULT_SLO_PAGE_SIZE, }: SLOListParams = {}): UseFetchSloListResponse { const { http, @@ -50,7 +51,7 @@ export function useFetchSloList({ ); const { isInitialLoading, isLoading, isError, isSuccess, isRefetching, data } = useQuery({ - queryKey: sloKeys.list({ kqlQuery, page, sortBy, sortDirection }), + queryKey: sloKeys.list({ kqlQuery, page, perPage, sortBy, sortDirection }), queryFn: async ({ signal }) => { const response = await http.get(`/api/observability/slos`, { query: { diff --git a/x-pack/plugins/observability/public/hooks/slo/use_reset_slo.ts b/x-pack/plugins/observability/public/hooks/slo/use_reset_slo.ts new file mode 100644 index 0000000000000..35f166e89b766 --- /dev/null +++ b/x-pack/plugins/observability/public/hooks/slo/use_reset_slo.ts @@ -0,0 +1,52 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { useMutation } from '@tanstack/react-query'; +import { i18n } from '@kbn/i18n'; +import { IHttpFetchError, ResponseErrorBody } from '@kbn/core/public'; +import { useKibana } from '../../utils/kibana_react'; + +type ServerError = IHttpFetchError; + +export function useResetSlo() { + const { + http, + notifications: { toasts }, + } = useKibana().services; + return useMutation( + ['resetSlo'], + ({ id, name }) => { + try { + return http.post(`/api/observability/slos/${id}/_reset`); + } catch (error) { + return Promise.reject( + i18n.translate('xpack.observability.slo.slo.reset.errorMessage', { + defaultMessage: 'Failed to reset {name} (id: {id}), something went wrong: {error}', + values: { error: String(error), name, id }, + }) + ); + } + }, + { + onError: (error, { name, id }) => { + toasts.addError(new Error(error.body?.message ?? error.message), { + title: i18n.translate('xpack.observability.slo.slo.reset.errorNotification', { + defaultMessage: 'Failed to reset {name} (id: {id})', + values: { name, id }, + }), + }); + }, + onSuccess: (_data, { name }) => { + toasts.addSuccess( + i18n.translate('xpack.observability.slo.slo.reset.successNotification', { + defaultMessage: '{name} reset successfully', + values: { name }, + }) + ); + }, + } + ); +} diff --git a/x-pack/plugins/observability/public/locators/slo_edit.test.ts b/x-pack/plugins/observability/public/locators/slo_edit.test.ts index a01a988dcdb55..cb485ea3e3877 100644 --- a/x-pack/plugins/observability/public/locators/slo_edit.test.ts +++ b/x-pack/plugins/observability/public/locators/slo_edit.test.ts @@ -20,7 +20,7 @@ describe('SloEditLocator', () => { it('should return correct url when slo is provided', async () => { const location = await locator.getLocation(buildSlo({ id: 'foo' })); expect(location.path).toEqual( - "/slos/edit/foo?_a=(budgetingMethod:occurrences,createdAt:'2022-12-29T10:11:12.000Z',description:'some%20description%20useful',enabled:!t,groupBy:'*',id:foo,indicator:(params:(filter:'baz:%20foo%20and%20bar%20%3E%202',good:'http_status:%202xx',index:some-index,timestampField:custom_timestamp,total:'a%20query'),type:sli.kql.custom),instanceId:'*',name:'super%20important%20level%20service',objective:(target:0.98),revision:1,settings:(frequency:'1m',syncDelay:'1m'),summary:(errorBudget:(consumed:0.064,initial:0.02,isEstimated:!f,remaining:0.936),sliValue:0.99872,status:HEALTHY),tags:!(k8s,production,critical),timeWindow:(duration:'30d',type:rolling),updatedAt:'2022-12-29T10:11:12.000Z')" + "/slos/edit/foo?_a=(budgetingMethod:occurrences,createdAt:'2022-12-29T10:11:12.000Z',description:'some%20description%20useful',enabled:!t,groupBy:'*',id:foo,indicator:(params:(filter:'baz:%20foo%20and%20bar%20%3E%202',good:'http_status:%202xx',index:some-index,timestampField:custom_timestamp,total:'a%20query'),type:sli.kql.custom),instanceId:'*',name:'super%20important%20level%20service',objective:(target:0.98),revision:1,settings:(frequency:'1m',syncDelay:'1m'),summary:(errorBudget:(consumed:0.064,initial:0.02,isEstimated:!f,remaining:0.936),sliValue:0.99872,status:HEALTHY),tags:!(k8s,production,critical),timeWindow:(duration:'30d',type:rolling),updatedAt:'2022-12-29T10:11:12.000Z',version:2)" ); }); }); diff --git a/x-pack/plugins/observability/public/locators/slo_list.test.ts b/x-pack/plugins/observability/public/locators/slo_list.test.ts index 68bf2c2589fea..677e831134496 100644 --- a/x-pack/plugins/observability/public/locators/slo_list.test.ts +++ b/x-pack/plugins/observability/public/locators/slo_list.test.ts @@ -13,8 +13,8 @@ describe('SloListLocator', () => { it("returns the correct url with the default search state when no 'kqlQuery' provided", async () => { const location = await locator.getLocation({}); expect(location.app).toEqual('observability'); - expect(location.path).toEqual( - "/slos?search=(kqlQuery:'',page:0,sort:(by:status,direction:desc),viewMode:compact)" + expect(location.path).toMatchInlineSnapshot( + `"/slos?search=(compact:!t,kqlQuery:'',page:0,perPage:25,sort:(by:status,direction:desc),view:cardView)"` ); }); @@ -23,8 +23,8 @@ describe('SloListLocator', () => { kqlQuery: 'slo.name: "Service Availability" and slo.indicator.type : "sli.kql.custom"', }); expect(location.app).toEqual('observability'); - expect(location.path).toEqual( - "/slos?search=(kqlQuery:'slo.name:%20%22Service%20Availability%22%20and%20slo.indicator.type%20:%20%22sli.kql.custom%22',page:0,sort:(by:status,direction:desc),viewMode:compact)" + expect(location.path).toMatchInlineSnapshot( + `"/slos?search=(compact:!t,kqlQuery:'slo.name:%20%22Service%20Availability%22%20and%20slo.indicator.type%20:%20%22sli.kql.custom%22',page:0,perPage:25,sort:(by:status,direction:desc),view:cardView)"` ); }); }); diff --git a/x-pack/plugins/observability/public/pages/alert_details/alert_details.test.tsx b/x-pack/plugins/observability/public/pages/alert_details/alert_details.test.tsx index 756f90ecd97cf..9d537453d4ead 100644 --- a/x-pack/plugins/observability/public/pages/alert_details/alert_details.test.tsx +++ b/x-pack/plugins/observability/public/pages/alert_details/alert_details.test.tsx @@ -13,6 +13,9 @@ import { casesPluginMock } from '@kbn/cases-plugin/public/mocks'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; import * as useUiSettingHook from '@kbn/kibana-react-plugin/public/ui_settings/use_ui_setting'; import { useBreadcrumbs } from '@kbn/observability-shared-plugin/public'; +import { observabilityAIAssistantPluginMock } from '@kbn/observability-ai-assistant-plugin/public/mock'; +import { ruleTypeRegistryMock } from '@kbn/triggers-actions-ui-plugin/public/application/rule_type_registry.mock'; +import { RuleTypeModel, ValidationResult } from '@kbn/triggers-actions-ui-plugin/public'; import { Subset } from '../../typings'; import { render } from '../../utils/test_helper'; @@ -22,8 +25,6 @@ import { useFetchAlertDetail } from '../../hooks/use_fetch_alert_detail'; import { AlertDetails } from './alert_details'; import { ConfigSchema } from '../../plugin'; import { alert, alertWithNoData } from './mock/alert'; -import { ruleTypeRegistryMock } from '@kbn/triggers-actions-ui-plugin/public/application/rule_type_registry.mock'; -import { RuleTypeModel, ValidationResult } from '@kbn/triggers-actions-ui-plugin/public'; jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), @@ -45,17 +46,20 @@ const ruleTypeRegistry = ruleTypeRegistryMock.create(); const useKibanaMock = useKibana as jest.Mock; +const mockObservabilityAIAssistant = observabilityAIAssistantPluginMock.createStartContract(); + const mockKibana = () => { useKibanaMock.mockReturnValue({ services: { ...kibanaStartMock.startContract(), - theme: {}, cases: casesPluginMock.createStartContract(), http: { basePath: { prepend: jest.fn(), }, }, + observabilityAIAssistant: mockObservabilityAIAssistant, + theme: {}, triggersActionsUi: { ruleTypeRegistry, }, @@ -90,7 +94,6 @@ const params = { const config: Subset = { unsafe: { alertDetails: { - logs: { enabled: true }, metrics: { enabled: true }, uptime: { enabled: true }, }, diff --git a/x-pack/plugins/observability/public/pages/alert_details/components/page_title.test.tsx b/x-pack/plugins/observability/public/pages/alert_details/components/page_title.test.tsx index 88492df63acf6..62438d5208504 100644 --- a/x-pack/plugins/observability/public/pages/alert_details/components/page_title.test.tsx +++ b/x-pack/plugins/observability/public/pages/alert_details/components/page_title.test.tsx @@ -8,7 +8,7 @@ import React from 'react'; import { render } from '@testing-library/react'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; -import { ALERT_RULE_CATEGORY } from '@kbn/rule-data-utils'; +import { ALERT_RULE_CATEGORY, ALERT_STATUS } from '@kbn/rule-data-utils'; import { PageTitle, PageTitleProps } from './page_title'; import { alert } from '../mock/alert'; @@ -66,16 +66,40 @@ describe('Page Title', () => { expect(getByTestId('ruleTypeId').textContent).toContain('Inventory threshold breached'); }); - it('should display an active badge when active is true', async () => { + it('should display an active badge when alert is active', async () => { const { getByText } = renderComp(defaultProps); expect(getByText('Active')).toBeTruthy(); }); - it('should display an inactive badge when active is false', async () => { - const updatedProps = { alert, dataTestSubj: defaultProps.dataTestSubj }; - updatedProps.alert.active = false; + it('should display a recovered badge when alert is recovered', async () => { + const updatedProps = { + alert: { + ...defaultProps.alert, + fields: { + ...defaultProps.alert.fields, + [ALERT_STATUS]: 'recovered', + }, + }, + dataTestSubj: defaultProps.dataTestSubj, + }; const { getByText } = renderComp({ ...updatedProps }); expect(getByText('Recovered')).toBeTruthy(); }); + + it('should display an untracked badge when alert is untracked', async () => { + const updatedProps = { + alert: { + ...defaultProps.alert, + fields: { + ...defaultProps.alert.fields, + [ALERT_STATUS]: 'untracked', + }, + }, + dataTestSubj: defaultProps.dataTestSubj, + }; + + const { getByText } = renderComp({ ...updatedProps }); + expect(getByText('Untracked')).toBeTruthy(); + }); }); diff --git a/x-pack/plugins/observability/public/pages/alert_details/components/page_title.tsx b/x-pack/plugins/observability/public/pages/alert_details/components/page_title.tsx index b5756e6db3edc..4216c49642272 100644 --- a/x-pack/plugins/observability/public/pages/alert_details/components/page_title.tsx +++ b/x-pack/plugins/observability/public/pages/alert_details/components/page_title.tsx @@ -22,8 +22,10 @@ import { ALERT_FLAPPING, ALERT_RULE_CATEGORY, ALERT_RULE_TYPE_ID, + ALERT_STATUS, ALERT_STATUS_ACTIVE, ALERT_STATUS_RECOVERED, + ALERT_STATUS_UNTRACKED, TIMESTAMP, } from '@kbn/rule-data-utils'; import moment from 'moment'; @@ -70,7 +72,13 @@ export function PageTitle({ alert, dataTestSubj }: PageTitleProps) { diff --git a/x-pack/plugins/observability/public/pages/alerts/alerts.test.tsx b/x-pack/plugins/observability/public/pages/alerts/alerts.test.tsx index 247a700acfa6c..8977e9ae3e4f0 100644 --- a/x-pack/plugins/observability/public/pages/alerts/alerts.test.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/alerts.test.tsx @@ -15,6 +15,7 @@ import { RUNNING_MAINTENANCE_WINDOW_1 } from '@kbn/alerts-ui-shared/src/maintena import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; import { __IntlProvider as IntlProvider } from '@kbn/i18n-react'; import { MAINTENANCE_WINDOW_FEATURE_ID } from '@kbn/alerting-plugin/common/maintenance_window'; +import { observabilityAIAssistantPluginMock } from '@kbn/observability-ai-assistant-plugin/public/mock'; import { ObservabilityPublicPluginsStart } from '../../plugin'; import { AlertsPage } from './alerts'; @@ -35,10 +36,19 @@ mockUseKibanaReturnValue.services.application.capabilities = { }, }; +const mockObservabilityAIAssistant = observabilityAIAssistantPluginMock.createStartContract(); + jest.mock('../../utils/kibana_react', () => ({ __esModule: true, - useKibana: jest.fn(() => mockUseKibanaReturnValue), + useKibana: jest.fn(() => ({ + ...mockUseKibanaReturnValue, + services: { + ...mockUseKibanaReturnValue.services, + observabilityAIAssistant: mockObservabilityAIAssistant, + }, + })), })); + jest.mock('@kbn/kibana-react-plugin/public', () => ({ __esModule: true, useKibana: jest.fn(() => mockUseKibanaReturnValue), @@ -53,12 +63,10 @@ jest.spyOn(pluginContext, 'usePluginContext').mockImplementation(() => ({ slo: { enabled: false }, alertDetails: { apm: { enabled: false }, - logs: { enabled: false }, metrics: { enabled: false }, uptime: { enabled: false }, observability: { enabled: false }, }, - thresholdRule: { enabled: false }, }, aiAssistant: { enabled: false, diff --git a/x-pack/plugins/observability/public/pages/alerts/alerts.tsx b/x-pack/plugins/observability/public/pages/alerts/alerts.tsx index c1b1493daa3d9..daf02e9f6fd09 100644 --- a/x-pack/plugins/observability/public/pages/alerts/alerts.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/alerts.tsx @@ -80,7 +80,6 @@ function InternalAlertsPage() { } }; const chartProps = { - theme: charts.theme.useChartsTheme(), baseTheme: charts.theme.useChartsBaseTheme(), onBrushEnd, }; diff --git a/x-pack/plugins/observability/public/pages/alerts/components/alert_actions.test.tsx b/x-pack/plugins/observability/public/pages/alerts/components/alert_actions.test.tsx index cbdb9aa068dad..ab7080e1d6d60 100644 --- a/x-pack/plugins/observability/public/pages/alerts/components/alert_actions.test.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/components/alert_actions.test.tsx @@ -4,11 +4,12 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import React from 'react'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { act } from '@testing-library/react-hooks'; -import { kibanaStartMock } from '../../../utils/kibana_react.mock'; -import React from 'react'; import { mountWithIntl, nextTick } from '@kbn/test-jest-helpers'; +import { kibanaStartMock } from '../../../utils/kibana_react.mock'; +import { observabilityAIAssistantPluginMock } from '@kbn/observability-ai-assistant-plugin/public/mock'; import { AlertActions, ObservabilityAlertActionsProps } from './alert_actions'; import { inventoryThresholdAlertEs } from '../../../rules/fixtures/example_alerts'; import { RULE_DETAILS_PAGE_ID } from '../../rule_details/constants'; @@ -42,6 +43,9 @@ mockUseKibanaReturnValue.services.cases.hooks.useCasesAddToExistingCaseModal.moc mockUseKibanaReturnValue.services.cases.helpers.canUseCases.mockReturnValue(allCasesPermissions()); +const { ObservabilityAIAssistantActionMenuItem, ObservabilityAIAssistantContextualInsight } = + observabilityAIAssistantPluginMock.createStartContract(); + jest.mock('../../../utils/kibana_react', () => ({ __esModule: true, useKibana: jest.fn(() => mockUseKibanaReturnValue), @@ -59,7 +63,6 @@ jest.mock('@kbn/triggers-actions-ui-plugin/public/common/lib/kibana/kibana_react const config = { unsafe: { alertDetails: { - logs: { enabled: false }, metrics: { enabled: false }, uptime: { enabled: false }, }, @@ -73,6 +76,8 @@ jest.spyOn(pluginContext, 'usePluginContext').mockImplementation(() => ({ plugins: {} as ObservabilityPublicPluginsStart, observabilityRuleTypeRegistry: createObservabilityRuleTypeRegistryMock(), ObservabilityPageTemplate: KibanaPageTemplate, + ObservabilityAIAssistantActionMenuItem, + ObservabilityAIAssistantContextualInsight, })); describe('ObservabilityActions component', () => { diff --git a/x-pack/plugins/observability/public/pages/alerts/components/alert_actions.tsx b/x-pack/plugins/observability/public/pages/alerts/components/alert_actions.tsx index c8f9e422f10be..ca5bdff47bac1 100644 --- a/x-pack/plugins/observability/public/pages/alerts/components/alert_actions.tsx +++ b/x-pack/plugins/observability/public/pages/alerts/components/alert_actions.tsx @@ -79,13 +79,13 @@ export function AlertActions({ const observabilityAlert = parseObservabilityAlert(alert); useEffect(() => { - const alertLink = alert.link as unknown as string; - if (!alert.hasBasePath) { + const alertLink = observabilityAlert.link as unknown as string; + if (!observabilityAlert.hasBasePath) { setViewInAppUrl(prepend(alertLink ?? '')); } else { setViewInAppUrl(alertLink); } - }, [alert.hasBasePath, alert.link, prepend]); + }, [observabilityAlert.hasBasePath, observabilityAlert.link, prepend]); const [isPopoverOpen, setIsPopoverOpen] = useState(false); diff --git a/x-pack/plugins/observability/public/pages/overview/components/header_menu/header_menu.tsx b/x-pack/plugins/observability/public/pages/overview/components/header_menu/header_menu.tsx index 111acb054e163..b57c512565fc1 100644 --- a/x-pack/plugins/observability/public/pages/overview/components/header_menu/header_menu.tsx +++ b/x-pack/plugins/observability/public/pages/overview/components/header_menu/header_menu.tsx @@ -8,22 +8,22 @@ import { EuiHeaderLink, EuiHeaderLinks } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React from 'react'; -import { - ObservabilityAIAssistantActionMenuItem, - useObservabilityAIAssistantOptional, -} from '@kbn/observability-ai-assistant-plugin/public'; + import { useKibana } from '../../../../utils/kibana_react'; import { usePluginContext } from '../../../../hooks/use_plugin_context'; import HeaderMenuPortal from './header_menu_portal'; export function HeaderMenu(): React.ReactElement | null { - const { http, theme } = useKibana().services; + const { + http, + theme, + observabilityAIAssistant: { ObservabilityAIAssistantActionMenuItem }, + } = useKibana().services; + const { appMountParameters: { setHeaderActionMenu }, } = usePluginContext(); - const aiAssistant = useObservabilityAIAssistantOptional(); - return ( @@ -34,7 +34,7 @@ export function HeaderMenu(): React.ReactElement | null { > {addDataLinkText} - {aiAssistant?.isEnabled() ? : null} + {ObservabilityAIAssistantActionMenuItem ? : null} ); diff --git a/x-pack/plugins/observability/public/pages/overview/components/sections/apm/apm_section.test.tsx b/x-pack/plugins/observability/public/pages/overview/components/sections/apm/apm_section.test.tsx index 6da498042f8fb..b5befe3c3083d 100644 --- a/x-pack/plugins/observability/public/pages/overview/components/sections/apm/apm_section.test.tsx +++ b/x-pack/plugins/observability/public/pages/overview/components/sections/apm/apm_section.test.tsx @@ -7,6 +7,7 @@ import React from 'react'; import * as fetcherHook from '@kbn/observability-shared-plugin/public/hooks/use_fetcher'; +import { observabilityAIAssistantPluginMock } from '@kbn/observability-ai-assistant-plugin/public/mock'; import { render, data as dataMock } from '../../../../../utils/test_helper'; import { CoreStart } from '@kbn/core/public'; import { ConfigSchema, ObservabilityPublicPluginsStart } from '../../../../../plugin'; @@ -27,6 +28,9 @@ jest.mock('react-router-dom', () => ({ useHistory: jest.fn(), })); +const { ObservabilityAIAssistantActionMenuItem, ObservabilityAIAssistantContextualInsight } = + observabilityAIAssistantPluginMock.createStartContract(); + describe('APMSection', () => { const bucketSize = { intervalString: '60s', bucketSize: 60, dateFormat: 'YYYY-MM-DD HH:mm' }; @@ -48,7 +52,6 @@ describe('APMSection', () => { const config = { unsafe: { alertDetails: { - logs: { enabled: false }, metrics: { enabled: false }, uptime: { enabled: false }, }, @@ -62,6 +65,8 @@ describe('APMSection', () => { plugins: {} as ObservabilityPublicPluginsStart, observabilityRuleTypeRegistry: createObservabilityRuleTypeRegistryMock(), ObservabilityPageTemplate: KibanaPageTemplate, + ObservabilityAIAssistantActionMenuItem, + ObservabilityAIAssistantContextualInsight, })); }); diff --git a/x-pack/plugins/observability/public/pages/overview/components/sections/apm/apm_section.tsx b/x-pack/plugins/observability/public/pages/overview/components/sections/apm/apm_section.tsx index cfbe160590401..ed6f55006c5e7 100644 --- a/x-pack/plugins/observability/public/pages/overview/components/sections/apm/apm_section.tsx +++ b/x-pack/plugins/observability/public/pages/overview/components/sections/apm/apm_section.tsx @@ -22,7 +22,7 @@ import moment from 'moment'; import React, { useContext } from 'react'; import { useHistory } from 'react-router-dom'; import { ThemeContext } from 'styled-components'; -import { useChartTheme, FETCH_STATUS, useFetcher } from '@kbn/observability-shared-plugin/public'; +import { useChartThemes, FETCH_STATUS, useFetcher } from '@kbn/observability-shared-plugin/public'; import { useDatePickerContext } from '../../../../../hooks/use_date_picker_context'; import { SectionContainer } from '../section_container'; import { getDataHandler } from '../../../../../context/has_data_context/data_handler'; @@ -55,7 +55,7 @@ function formatTpmStat(value?: number) { export function APMSection({ bucketSize }: Props) { const theme = useContext(ThemeContext); - const chartTheme = useChartTheme(); + const chartThemes = useChartThemes(); const history = useHistory(); const { forceUpdate, hasDataMap } = useHasData(); const { relativeStart, relativeEnd, absoluteStart, absoluteEnd, lastUpdated } = @@ -147,7 +147,7 @@ export function APMSection({ bucketSize }: Props) { onBrushEnd({ x: (event as XYBrushEvent).x, history })} - theme={chartTheme} + {...chartThemes} showLegend={false} xDomain={{ min, max }} locale={i18n.getLocale()} diff --git a/x-pack/plugins/observability/public/pages/overview/components/sections/logs/logs_section.tsx b/x-pack/plugins/observability/public/pages/overview/components/sections/logs/logs_section.tsx index 4097fbcb5bb87..63087aad6dd30 100644 --- a/x-pack/plugins/observability/public/pages/overview/components/sections/logs/logs_section.tsx +++ b/x-pack/plugins/observability/public/pages/overview/components/sections/logs/logs_section.tsx @@ -22,7 +22,7 @@ import { isEmpty } from 'lodash'; import moment from 'moment'; import React, { Fragment } from 'react'; import { useHistory } from 'react-router-dom'; -import { useChartTheme, FETCH_STATUS, useFetcher } from '@kbn/observability-shared-plugin/public'; +import { useChartThemes, FETCH_STATUS, useFetcher } from '@kbn/observability-shared-plugin/public'; import { SectionContainer } from '../section_container'; import { getDataHandler } from '../../../../../context/has_data_context/data_handler'; import { useHasData } from '../../../../../hooks/use_has_data'; @@ -55,7 +55,7 @@ function getColorPerItem(series?: LogsFetchDataResponse['series']) { export function LogsSection({ bucketSize }: Props) { const history = useHistory(); - const chartTheme = useChartTheme(); + const chartThemes = useChartThemes(); const { forceUpdate, hasDataMap } = useHasData(); const { relativeStart, relativeEnd, absoluteStart, absoluteEnd, lastUpdated } = useDatePickerContext(); @@ -138,7 +138,7 @@ export function LogsSection({ bucketSize }: Props) { onBrushEnd({ x: (event as XYBrushEvent).x, history })} - theme={chartTheme} + {...chartThemes} showLegend legendPosition={Position.Right} xDomain={{ min, max }} diff --git a/x-pack/plugins/observability/public/pages/overview/components/sections/metrics/metric_with_sparkline.tsx b/x-pack/plugins/observability/public/pages/overview/components/sections/metrics/metric_with_sparkline.tsx index bbd3b9acd224c..3e4b0476b5075 100644 --- a/x-pack/plugins/observability/public/pages/overview/components/sections/metrics/metric_with_sparkline.tsx +++ b/x-pack/plugins/observability/public/pages/overview/components/sections/metrics/metric_with_sparkline.tsx @@ -5,15 +5,19 @@ * 2.0. */ -import { Chart, Settings, AreaSeries, TooltipType, Tooltip } from '@elastic/charts'; +import { + Chart, + Settings, + AreaSeries, + TooltipType, + Tooltip, + LIGHT_THEME, + DARK_THEME, +} from '@elastic/charts'; import { EuiFlexItem, EuiFlexGroup, EuiIcon, EuiTextColor } from '@elastic/eui'; import React, { useContext } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; -import { - EUI_CHARTS_THEME_DARK, - EUI_CHARTS_THEME_LIGHT, - EUI_SPARKLINE_THEME_PARTIAL, -} from '@elastic/eui/dist/eui_charts_theme'; +import { EUI_SPARKLINE_THEME_PARTIAL } from '@elastic/eui/dist/eui_charts_theme'; import { ThemeContext } from 'styled-components'; import { i18n } from '@kbn/i18n'; @@ -29,10 +33,8 @@ interface Props { export function MetricWithSparkline({ id, formatter, value, timeseries, color }: Props) { const themeCTX = useContext(ThemeContext); const isDarkTheme = (themeCTX && themeCTX.darkMode) || false; - const theme = [ - EUI_SPARKLINE_THEME_PARTIAL, - isDarkTheme ? EUI_CHARTS_THEME_DARK.theme : EUI_CHARTS_THEME_LIGHT.theme, - ]; + const theme = [EUI_SPARKLINE_THEME_PARTIAL]; + const baseTheme = isDarkTheme ? DARK_THEME : LIGHT_THEME; const colors = theme[1].colors?.vizColors ?? []; @@ -53,7 +55,12 @@ export function MetricWithSparkline({ id, formatter, value, timeseries, color }: - + onBrushEnd({ x: (event as XYBrushEvent).x, history })} - theme={chartTheme} + {...chartThemes} showLegend={false} legendPosition={Position.Right} xDomain={{ min, max }} diff --git a/x-pack/plugins/observability/public/pages/overview/components/sections/ux/core_web_vitals/translations.ts b/x-pack/plugins/observability/public/pages/overview/components/sections/ux/core_web_vitals/translations.ts index d6445320a7978..bded2fd67cca8 100644 --- a/x-pack/plugins/observability/public/pages/overview/components/sections/ux/core_web_vitals/translations.ts +++ b/x-pack/plugins/observability/public/pages/overview/components/sections/ux/core_web_vitals/translations.ts @@ -16,7 +16,7 @@ export const LCP_LABEL = i18n.translate('xpack.observability.ux.coreVitals.lcp', }); export const INP_LABEL = i18n.translate('xpack.observability.ux.coreVitals.inp', { - defaultMessage: 'Interaction to Next Paint', + defaultMessage: 'Interaction to next paint', }); export const CLS_LABEL = i18n.translate('xpack.observability.ux.coreVitals.cls', { diff --git a/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx b/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx index d0937d1f2c72b..a1023f33f4313 100644 --- a/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx +++ b/x-pack/plugins/observability/public/pages/overview/overview.stories.tsx @@ -81,12 +81,10 @@ const withCore = makeDecorator({ const config: ConfigSchema = { unsafe: { alertDetails: { - logs: { enabled: false }, metrics: { enabled: false }, uptime: { enabled: false }, observability: { enabled: false }, }, - thresholdRule: { enabled: false }, }, }; diff --git a/x-pack/plugins/observability/public/pages/overview/overview.tsx b/x-pack/plugins/observability/public/pages/overview/overview.tsx index 6b623e1636dab..17121be51c668 100644 --- a/x-pack/plugins/observability/public/pages/overview/overview.tsx +++ b/x-pack/plugins/observability/public/pages/overview/overview.tsx @@ -102,7 +102,6 @@ export function OverviewPage() { ); const chartProps = { - theme: charts.theme.useChartsTheme(), baseTheme: charts.theme.useChartsBaseTheme(), }; diff --git a/x-pack/plugins/observability/public/pages/rule_details/helpers/get_health_color.ts b/x-pack/plugins/observability/public/pages/rule_details/helpers/get_health_color.ts index 52aab86039a3c..32b81e6dd9263 100644 --- a/x-pack/plugins/observability/public/pages/rule_details/helpers/get_health_color.ts +++ b/x-pack/plugins/observability/public/pages/rule_details/helpers/get_health_color.ts @@ -17,6 +17,8 @@ export function getHealthColor(status: RuleExecutionStatuses) { return 'primary'; case 'pending': return 'accent'; + case 'warning': + return 'warning'; default: return 'subdued'; } diff --git a/x-pack/plugins/observability/public/pages/rule_details/rule_details.tsx b/x-pack/plugins/observability/public/pages/rule_details/rule_details.tsx index b3b9ea17d5cac..6a9c38b1b1e80 100644 --- a/x-pack/plugins/observability/public/pages/rule_details/rule_details.tsx +++ b/x-pack/plugins/observability/public/pages/rule_details/rule_details.tsx @@ -50,7 +50,7 @@ export function RuleDetailsPage() { const { application: { capabilities, navigateToUrl }, charts: { - theme: { useChartsBaseTheme, useChartsTheme }, + theme: { useChartsBaseTheme }, }, http: { basePath }, share: { @@ -70,7 +70,6 @@ export function RuleDetailsPage() { const { ruleId } = useParams(); const { search } = useLocation(); - const theme = useChartsTheme(); const baseTheme = useChartsBaseTheme(); const { rule, isLoading, isError, refetch } = useFetchRule({ ruleId }); @@ -234,7 +233,7 @@ export function RuleDetailsPage() { ({ __esModule: true, - useKibana: jest.fn(() => mockUseKibanaReturnValue), + useKibana: jest.fn(() => ({ + ...mockUseKibanaReturnValue, + services: { + ...mockUseKibanaReturnValue.services, + observabilityAIAssistant: mockObservabilityAIAssistant, + }, + })), })); jest.mock('@kbn/observability-shared-plugin/public'); @@ -40,12 +48,10 @@ jest.spyOn(pluginContext, 'usePluginContext').mockImplementation(() => ({ slo: { enabled: false }, alertDetails: { apm: { enabled: false }, - logs: { enabled: false }, metrics: { enabled: false }, uptime: { enabled: false }, observability: { enabled: false }, }, - thresholdRule: { enabled: false }, }, }, observabilityRuleTypeRegistry: createObservabilityRuleTypeRegistryMock(), diff --git a/x-pack/plugins/observability/public/pages/slo_details/components/events_chart_panel.tsx b/x-pack/plugins/observability/public/pages/slo_details/components/events_chart_panel.tsx index 10655cd98b121..fdb911c80c206 100644 --- a/x-pack/plugins/observability/public/pages/slo_details/components/events_chart_panel.tsx +++ b/x-pack/plugins/observability/public/pages/slo_details/components/events_chart_panel.tsx @@ -46,7 +46,6 @@ export function EventsChartPanel({ slo, range }: Props) { const { charts, uiSettings } = useKibana().services; const { euiTheme } = useEuiTheme(); const { isLoading, data } = useGetPreviewData(true, slo.indicator, range); - const theme = charts.theme.useChartsTheme(); const baseTheme = charts.theme.useChartsBaseTheme(); const chartRef = useRef(null); const handleCursorUpdate = useActiveCursor(charts.activeCursor, chartRef, { @@ -88,7 +87,6 @@ export function EventsChartPanel({ slo, range }: Props) { showLegend showLegendExtra={false} legendPosition={Position.Left} - theme={[theme]} noResults={ } diff --git a/x-pack/plugins/observability/public/pages/slo_details/components/header_control.tsx b/x-pack/plugins/observability/public/pages/slo_details/components/header_control.tsx index 615edf3ef65f3..34eb1f278e7a3 100644 --- a/x-pack/plugins/observability/public/pages/slo_details/components/header_control.tsx +++ b/x-pack/plugins/observability/public/pages/slo_details/components/header_control.tsx @@ -10,20 +10,16 @@ import { i18n } from '@kbn/i18n'; import { EuiButton, EuiContextMenuItem, EuiContextMenuPanel, EuiPopover } from '@elastic/eui'; import { SLOWithSummaryResponse } from '@kbn/slo-schema'; +import { useCloneSlo } from '../../../hooks/slo/use_clone_slo'; import { SloDeleteConfirmationModal } from '../../../components/slo/delete_confirmation_modal/slo_delete_confirmation_modal'; import { useCapabilities } from '../../../hooks/slo/use_capabilities'; import { useKibana } from '../../../utils/kibana_react'; -import { useCloneSlo } from '../../../hooks/slo/use_clone_slo'; import { useDeleteSlo } from '../../../hooks/slo/use_delete_slo'; import { isApmIndicatorType } from '../../../utils/slo/indicator'; import { convertSliApmParamsToApmAppDeeplinkUrl } from '../../../utils/slo/convert_sli_apm_params_to_apm_app_deeplink_url'; import { SLO_BURN_RATE_RULE_TYPE_ID } from '../../../../common/constants'; import { rulesLocatorID, sloFeatureId } from '../../../../common'; import { paths } from '../../../../common/locators/paths'; -import { - transformSloResponseToCreateSloForm, - transformCreateSLOFormToCreateSLOInput, -} from '../../slo_edit/helpers/process_slo_form_values'; import type { RulesParams } from '../../../locators/rules'; export interface Props { @@ -47,7 +43,6 @@ export function HeaderControl({ isLoading, slo }: Props) { const [isRuleFlyoutVisible, setRuleFlyoutVisibility] = useState(false); const [isDeleteConfirmationModalOpen, setDeleteConfirmationModalOpen] = useState(false); - const { mutate: cloneSlo } = useCloneSlo(); const { mutate: deleteSlo } = useDeleteSlo(); const handleActionsClick = () => setIsPopoverOpen((value) => !value); @@ -101,17 +96,12 @@ export function HeaderControl({ isLoading, slo }: Props) { } }; + const navigateToClone = useCloneSlo(); + const handleClone = async () => { if (slo) { setIsPopoverOpen(false); - - const newSlo = transformCreateSLOFormToCreateSLOInput( - transformSloResponseToCreateSloForm({ ...slo, name: `[Copy] ${slo.name}` })! - ); - - cloneSlo({ slo: newSlo, originalSloId: slo.id }); - - navigate(basePath.prepend(paths.observability.slos)); + navigateToClone(slo); } }; diff --git a/x-pack/plugins/observability/public/pages/slo_details/components/wide_chart.tsx b/x-pack/plugins/observability/public/pages/slo_details/components/wide_chart.tsx index e6ebd1197c5c2..ee3a475e13110 100644 --- a/x-pack/plugins/observability/public/pages/slo_details/components/wide_chart.tsx +++ b/x-pack/plugins/observability/public/pages/slo_details/components/wide_chart.tsx @@ -40,7 +40,6 @@ export interface Props { export function WideChart({ chart, data, id, isLoading, state }: Props) { const { charts, uiSettings } = useKibana().services; - const theme = charts.theme.useChartsTheme(); const baseTheme = charts.theme.useChartsBaseTheme(); const { euiTheme } = useEuiTheme(); const dateFormat = uiSettings.get('dateFormat'); @@ -64,7 +63,6 @@ export function WideChart({ chart, data, id, isLoading, state }: Props) { } onPointerUpdate={handleCursorUpdate} externalPointerEvents={{ diff --git a/x-pack/plugins/observability/public/pages/slo_details/slo_details.test.tsx b/x-pack/plugins/observability/public/pages/slo_details/slo_details.test.tsx index de21fb145677f..22691aaa2b49c 100644 --- a/x-pack/plugins/observability/public/pages/slo_details/slo_details.test.tsx +++ b/x-pack/plugins/observability/public/pages/slo_details/slo_details.test.tsx @@ -8,6 +8,7 @@ import React from 'react'; import { fireEvent, screen, waitFor } from '@testing-library/react'; import type { Capabilities } from '@kbn/core/public'; +import { observabilityAIAssistantPluginMock } from '@kbn/observability-ai-assistant-plugin/public/mock'; import { useKibana } from '../../utils/kibana_react'; import { useParams, useLocation } from 'react-router-dom'; @@ -17,7 +18,6 @@ import { useFetchSloDetails } from '../../hooks/slo/use_fetch_slo_details'; import { useFetchHistoricalSummary } from '../../hooks/slo/use_fetch_historical_summary'; import { useFetchActiveAlerts } from '../../hooks/slo/use_fetch_active_alerts'; import { ActiveAlerts } from '../../hooks/slo/active_alerts'; -import { useCloneSlo } from '../../hooks/slo/use_clone_slo'; import { useDeleteSlo } from '../../hooks/slo/use_delete_slo'; import { render } from '../../utils/test_helper'; import { SloDetailsPage } from './slo_details'; @@ -30,6 +30,7 @@ import { import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; import { buildApmAvailabilityIndicator } from '../../data/slo/indicator'; import { ALL_VALUE } from '@kbn/slo-schema'; +import { encode } from '@kbn/rison'; jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), @@ -44,7 +45,6 @@ jest.mock('../../hooks/slo/use_capabilities'); jest.mock('../../hooks/slo/use_fetch_active_alerts'); jest.mock('../../hooks/slo/use_fetch_slo_details'); jest.mock('../../hooks/slo/use_fetch_historical_summary'); -jest.mock('../../hooks/slo/use_clone_slo'); jest.mock('../../hooks/slo/use_delete_slo'); const useKibanaMock = useKibana as jest.Mock; @@ -55,12 +55,10 @@ const useCapabilitiesMock = useCapabilities as jest.Mock; const useFetchActiveAlertsMock = useFetchActiveAlerts as jest.Mock; const useFetchSloDetailsMock = useFetchSloDetails as jest.Mock; const useFetchHistoricalSummaryMock = useFetchHistoricalSummary as jest.Mock; -const useCloneSloMock = useCloneSlo as jest.Mock; const useDeleteSloMock = useDeleteSlo as jest.Mock; const mockNavigate = jest.fn(); const mockLocator = jest.fn(); -const mockClone = jest.fn(); const mockDelete = jest.fn(); const mockCapabilities = { apm: { show: true }, @@ -87,6 +85,7 @@ const mockKibana = () => { addError: jest.fn(), }, }, + observabilityAIAssistant: observabilityAIAssistantPluginMock.createStartContract(), share: { url: { locators: { @@ -120,7 +119,6 @@ describe('SLO Details Page', () => { data: historicalSummaryData, }); useFetchActiveAlertsMock.mockReturnValue({ isLoading: false, data: new ActiveAlerts() }); - useCloneSloMock.mockReturnValue({ mutate: mockClone }); useDeleteSloMock.mockReturnValue({ mutate: mockDelete }); useLocationMock.mockReturnValue({ search: '' }); }); @@ -248,28 +246,12 @@ describe('SLO Details Page', () => { fireEvent.click(button!); - const { - id, - createdAt, - enabled, - revision, - summary, - settings, - updatedAt, - instanceId, - ...newSlo - } = slo; - - expect(mockClone).toBeCalledWith({ - originalSloId: slo.id, - slo: { - ...newSlo, - name: `[Copy] ${newSlo.name}`, - }, - }); - await waitFor(() => { - expect(mockNavigate).toBeCalledWith(paths.observability.slos); + expect(mockNavigate).toBeCalledWith( + paths.observability.sloCreateWithEncodedForm( + encode({ ...slo, name: `[Copy] ${slo.name}`, id: undefined }) + ) + ); }); }); diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/apm_availability/apm_availability_indicator_type_form.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/apm_availability/apm_availability_indicator_type_form.tsx index b58ee97634387..78afcc71cae58 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/apm_availability/apm_availability_indicator_type_form.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/apm_availability/apm_availability_indicator_type_form.tsx @@ -5,11 +5,12 @@ * 2.0. */ -import { EuiFlexGroup, EuiFlexItem, EuiIconTip } from '@elastic/eui'; +import { EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiIconTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { ALL_VALUE } from '@kbn/slo-schema/src/schema/common'; import React, { useEffect } from 'react'; import { useFormContext } from 'react-hook-form'; +import { useFetchGroupByCardinality } from '../../../../hooks/slo/use_fetch_group_by_cardinality'; import { useFetchApmIndex } from '../../../../hooks/slo/use_fetch_apm_indices'; import { useFetchIndexPatternFields } from '../../../../hooks/slo/use_fetch_index_pattern_fields'; import { CreateSLOForm } from '../../types'; @@ -27,10 +28,15 @@ export function ApmAvailabilityIndicatorTypeForm() { setValue('indicator.params.index', apmIndex); } }, [setValue, apmIndex]); + const timestampField = watch('indicator.params.timestampField'); + const groupByField = watch('groupBy'); const { isLoading: isIndexFieldsLoading, data: indexFields = [] } = useFetchIndexPatternFields(apmIndex); - const partitionByFields = indexFields.filter((field) => field.aggregatable); + + const { isLoading: isGroupByCardinalityLoading, data: groupByCardinality } = + useFetchGroupByCardinality(apmIndex, timestampField, groupByField); + const groupByFields = indexFields.filter((field) => field.aggregatable); return ( @@ -135,13 +141,13 @@ export function ApmAvailabilityIndicatorTypeForm() { {i18n.translate('xpack.observability.slo.sloEdit.groupBy.label', { - defaultMessage: 'Partition by', + defaultMessage: 'Group by', })}{' '} } placeholder={i18n.translate('xpack.observability.slo.sloEdit.groupBy.placeholder', { - defaultMessage: 'Select an optional field to partition by', + defaultMessage: 'Select an optional field to group by', })} isLoading={!!apmIndex && isIndexFieldsLoading} isDisabled={!apmIndex} /> + {!isGroupByCardinalityLoading && !!groupByCardinality && ( + + )} + ); diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/apm_latency/apm_latency_indicator_type_form.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/apm_latency/apm_latency_indicator_type_form.tsx index 32b5729762abd..dcb4be69d272d 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/apm_latency/apm_latency_indicator_type_form.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/apm_latency/apm_latency_indicator_type_form.tsx @@ -5,11 +5,19 @@ * 2.0. */ -import { EuiFieldNumber, EuiFlexGroup, EuiFlexItem, EuiFormRow, EuiIconTip } from '@elastic/eui'; +import { + EuiCallOut, + EuiFieldNumber, + EuiFlexGroup, + EuiFlexItem, + EuiFormRow, + EuiIconTip, +} from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { ALL_VALUE } from '@kbn/slo-schema/src/schema/common'; import React, { useEffect } from 'react'; import { Controller, useFormContext } from 'react-hook-form'; +import { useFetchGroupByCardinality } from '../../../../hooks/slo/use_fetch_group_by_cardinality'; import { useFetchApmIndex } from '../../../../hooks/slo/use_fetch_apm_indices'; import { useFetchIndexPatternFields } from '../../../../hooks/slo/use_fetch_index_pattern_fields'; import { CreateSLOForm } from '../../types'; @@ -28,9 +36,15 @@ export function ApmLatencyIndicatorTypeForm() { } }, [setValue, apmIndex]); + const timestampField = watch('indicator.params.timestampField'); + const groupByField = watch('groupBy'); + const { isLoading: isIndexFieldsLoading, data: indexFields = [] } = useFetchIndexPatternFields(apmIndex); - const partitionByFields = indexFields.filter((field) => field.aggregatable); + + const { isLoading: isGroupByCardinalityLoading, data: groupByCardinality } = + useFetchGroupByCardinality(apmIndex, timestampField, groupByField); + const groupByFields = indexFields.filter((field) => field.aggregatable); return ( @@ -178,13 +192,13 @@ export function ApmLatencyIndicatorTypeForm() { {i18n.translate('xpack.observability.slo.sloEdit.groupBy.label', { - defaultMessage: 'Partition by', + defaultMessage: 'Group by', })}{' '} } placeholder={i18n.translate('xpack.observability.slo.sloEdit.groupBy.placeholder', { - defaultMessage: 'Select an optional field to partition by', + defaultMessage: 'Select an optional field to group by', })} isLoading={!!apmIndex && isIndexFieldsLoading} isDisabled={!apmIndex} /> + {!isGroupByCardinalityLoading && !!groupByCardinality && ( + + )} + ); diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/common/data_preview_chart.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/common/data_preview_chart.tsx index dfa52e6e572a0..81556c2feb0f7 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/common/data_preview_chart.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/common/data_preview_chart.tsx @@ -77,7 +77,6 @@ export function DataPreviewChart({ isError, } = useDebouncedGetPreviewData(isIndicatorSectionValid, watch('indicator'), range); - const theme = charts.theme.useChartsTheme(); const baseTheme = charts.theme.useChartsBaseTheme(); const dateFormat = uiSettings.get('dateFormat'); const numberFormat = @@ -196,7 +195,6 @@ export function DataPreviewChart({ showLegend={false} theme={[ { - ...theme, lineSeriesStyle: { point: { visible: false }, }, @@ -231,7 +229,7 @@ export function DataPreviewChart({ timeAxisLayerCount={2} gridLine={{ visible: true }} style={{ - tickLine: { size: 0.0001, padding: 4, visible: true }, + tickLine: { size: 0, padding: 4, visible: true }, tickLabel: { alignment: { horizontal: Position.Left, diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/common/index_field_selector.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/common/index_field_selector.tsx index 367ff255edde0..0a07515a8915f 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/common/index_field_selector.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/common/index_field_selector.tsx @@ -6,14 +6,14 @@ */ import { EuiComboBox, EuiComboBoxOptionOption, EuiFlexItem, EuiFormRow } from '@elastic/eui'; +import { FieldSpec } from '@kbn/data-views-plugin/common'; import React, { useEffect, useState } from 'react'; import { Controller, useFormContext } from 'react-hook-form'; -import { Field } from '../../../../hooks/slo/use_fetch_index_pattern_fields'; import { createOptionsFromFields, Option } from '../../helpers/create_options'; import { CreateSLOForm } from '../../types'; interface Props { - indexFields: Field[]; + indexFields: FieldSpec[]; name: 'groupBy' | 'indicator.params.timestampField'; label: React.ReactNode | string; placeholder: string; diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/custom_kql_indicator_type_form.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/custom_kql_indicator_type_form.tsx index 05b58ab2aa198..42130cf204df6 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/custom_kql_indicator_type_form.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/custom_kql/custom_kql_indicator_type_form.tsx @@ -5,11 +5,12 @@ * 2.0. */ -import { EuiFlexGroup, EuiFlexItem, EuiIconTip } from '@elastic/eui'; +import { EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiIconTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { ALL_VALUE } from '@kbn/slo-schema/src/schema/common'; import React from 'react'; import { useFormContext } from 'react-hook-form'; +import { useFetchGroupByCardinality } from '../../../../hooks/slo/use_fetch_group_by_cardinality'; import { useFetchIndexPatternFields } from '../../../../hooks/slo/use_fetch_index_pattern_fields'; import { CreateSLOForm } from '../../types'; import { DataPreviewChart } from '../common/data_preview_chart'; @@ -20,10 +21,16 @@ import { IndexSelection } from '../custom_common/index_selection'; export function CustomKqlIndicatorTypeForm() { const { watch } = useFormContext(); const index = watch('indicator.params.index'); + const timestampField = watch('indicator.params.timestampField'); + const groupByField = watch('groupBy'); + const { isLoading: isIndexFieldsLoading, data: indexFields = [] } = useFetchIndexPatternFields(index); const timestampFields = indexFields.filter((field) => field.type === 'date'); - const partitionByFields = indexFields.filter((field) => field.aggregatable); + const groupByFields = indexFields.filter((field) => field.aggregatable); + + const { isLoading: isGroupByCardinalityLoading, data: groupByCardinality } = + useFetchGroupByCardinality(index, timestampField, groupByField); return ( @@ -135,13 +142,13 @@ export function CustomKqlIndicatorTypeForm() { {i18n.translate('xpack.observability.slo.sloEdit.groupBy.label', { - defaultMessage: 'Partition by', + defaultMessage: 'Group by', })}{' '} } placeholder={i18n.translate('xpack.observability.slo.sloEdit.groupBy.placeholder', { - defaultMessage: 'Select an optional field to partition by', + defaultMessage: 'Select an optional field to group by', })} isLoading={!!index && isIndexFieldsLoading} isDisabled={!index} /> + {!isGroupByCardinalityLoading && !!groupByCardinality && ( + + )} + ); diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/custom_metric/custom_metric_type_form.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/custom_metric/custom_metric_type_form.tsx index d69619d1121ef..2db17479ffd2d 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/custom_metric/custom_metric_type_form.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/custom_metric/custom_metric_type_form.tsx @@ -6,6 +6,7 @@ */ import { + EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, @@ -18,6 +19,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { ALL_VALUE } from '@kbn/slo-schema/src/schema/common'; import React from 'react'; import { useFormContext } from 'react-hook-form'; +import { useFetchGroupByCardinality } from '../../../../hooks/slo/use_fetch_group_by_cardinality'; import { useFetchIndexPatternFields } from '../../../../hooks/slo/use_fetch_index_pattern_fields'; import { CreateSLOForm } from '../../types'; import { DataPreviewChart } from '../common/data_preview_chart'; @@ -33,10 +35,16 @@ const SUPPORTED_METRIC_FIELD_TYPES = ['number', 'histogram']; export function CustomMetricIndicatorTypeForm() { const { watch } = useFormContext(); const index = watch('indicator.params.index'); + const timestampField = watch('indicator.params.timestampField'); + const groupByField = watch('groupBy'); + const { isLoading: isIndexFieldsLoading, data: indexFields = [] } = useFetchIndexPatternFields(index); + const { isLoading: isGroupByCardinalityLoading, data: groupByCardinality } = + useFetchGroupByCardinality(index, timestampField, groupByField); + const timestampFields = indexFields.filter((field) => field.type === 'date'); - const partitionByFields = indexFields.filter((field) => field.aggregatable); + const groupByFields = indexFields.filter((field) => field.aggregatable); const metricFields = indexFields.filter((field) => SUPPORTED_METRIC_FIELD_TYPES.includes(field.type) ); @@ -152,13 +160,13 @@ export function CustomMetricIndicatorTypeForm() { {i18n.translate('xpack.observability.slo.sloEdit.groupBy.label', { - defaultMessage: 'Partition by', + defaultMessage: 'Group by', })}{' '} } placeholder={i18n.translate('xpack.observability.slo.sloEdit.groupBy.placeholder', { - defaultMessage: 'Select an optional field to partition by', + defaultMessage: 'Select an optional field to group by', })} isLoading={!!index && isIndexFieldsLoading} isDisabled={!index} /> + {!isGroupByCardinalityLoading && !!groupByCardinality && ( + + )} + diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/custom_metric/metric_indicator.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/custom_metric/metric_indicator.tsx index 262e6e6d2249a..3e077ab2280a6 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/custom_metric/metric_indicator.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/custom_metric/metric_indicator.tsx @@ -16,12 +16,12 @@ import { EuiIconTip, EuiSpacer, } from '@elastic/eui'; +import { FieldSpec } from '@kbn/data-views-plugin/common'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { first, range, xor } from 'lodash'; import React, { useEffect, useState } from 'react'; import { Controller, useFieldArray, useFormContext } from 'react-hook-form'; -import { Field } from '../../../../hooks/slo/use_fetch_index_pattern_fields'; import { aggValueToLabel, CUSTOM_METRIC_AGGREGATION_OPTIONS, @@ -32,7 +32,7 @@ import { QueryBuilder } from '../common/query_builder'; interface MetricIndicatorProps { type: 'good' | 'total'; - metricFields: Field[]; + metricFields: FieldSpec[]; isLoadingIndex: boolean; } diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/histogram/histogram_indicator.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/histogram/histogram_indicator.tsx index 1362674828d74..4efcc1bfb5b8b 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/histogram/histogram_indicator.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/histogram/histogram_indicator.tsx @@ -15,17 +15,17 @@ import { EuiIconTip, EuiSpacer, } from '@elastic/eui'; +import { FieldSpec } from '@kbn/data-views-plugin/common'; import { i18n } from '@kbn/i18n'; import React, { Fragment, useEffect, useState } from 'react'; import { Controller, useFormContext } from 'react-hook-form'; -import { Field } from '../../../../hooks/slo/use_fetch_index_pattern_fields'; import { createOptionsFromFields, Option } from '../../helpers/create_options'; import { CreateSLOForm } from '../../types'; import { QueryBuilder } from '../common/query_builder'; interface HistogramIndicatorProps { type: 'good' | 'total'; - histogramFields: Field[]; + histogramFields: FieldSpec[]; isLoadingIndex: boolean; } diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/histogram/histogram_indicator_type_form.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/histogram/histogram_indicator_type_form.tsx index 50a84c121d73c..992547dc2802f 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/histogram/histogram_indicator_type_form.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/histogram/histogram_indicator_type_form.tsx @@ -6,6 +6,7 @@ */ import { + EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, @@ -18,6 +19,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { ALL_VALUE } from '@kbn/slo-schema/src/schema/common'; import React from 'react'; import { useFormContext } from 'react-hook-form'; +import { useFetchGroupByCardinality } from '../../../../hooks/slo/use_fetch_group_by_cardinality'; import { useFetchIndexPatternFields } from '../../../../hooks/slo/use_fetch_index_pattern_fields'; import { CreateSLOForm } from '../../types'; import { DataPreviewChart } from '../common/data_preview_chart'; @@ -29,12 +31,17 @@ import { HistogramIndicator } from './histogram_indicator'; export function HistogramIndicatorTypeForm() { const { watch } = useFormContext(); const index = watch('indicator.params.index'); + const timestampField = watch('indicator.params.timestampField'); + const groupByField = watch('groupBy'); const { isLoading: isIndexFieldsLoading, data: indexFields = [] } = useFetchIndexPatternFields(index); + const { isLoading: isGroupByCardinalityLoading, data: groupByCardinality } = + useFetchGroupByCardinality(index, timestampField, groupByField); + const histogramFields = indexFields.filter((field) => field.type === 'histogram'); const timestampFields = indexFields.filter((field) => field.type === 'date'); - const partitionByFields = indexFields.filter((field) => field.aggregatable); + const groupByFields = indexFields.filter((field) => field.aggregatable); return ( <> @@ -139,13 +146,13 @@ export function HistogramIndicatorTypeForm() { {i18n.translate('xpack.observability.slo.sloEdit.groupBy.label', { - defaultMessage: 'Partition by', + defaultMessage: 'Group by', })}{' '} } placeholder={i18n.translate('xpack.observability.slo.sloEdit.groupBy.placeholder', { - defaultMessage: 'Select an optional field to partition by', + defaultMessage: 'Select an optional field to group by', })} isLoading={!!index && isIndexFieldsLoading} isDisabled={!index} /> + {!isGroupByCardinalityLoading && !!groupByCardinality && ( + + )} + 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 6627f910a7c27..e5f8e9b02ce2a 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 @@ -18,8 +18,7 @@ import { i18n } from '@kbn/i18n'; import type { GetSLOResponse } from '@kbn/slo-schema'; import React, { useCallback, useEffect, useState } from 'react'; import { FormProvider, useForm } from 'react-hook-form'; -import { sloFeatureId } from '../../../../common'; -import { SLO_BURN_RATE_RULE_TYPE_ID } from '../../../../common/constants'; +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'; @@ -54,7 +53,6 @@ export function SloEditForm({ slo }: Props) { const { application: { navigateToUrl }, http: { basePath }, - triggersActionsUi: { getAddRuleFlyout: AddRuleFlyout }, } = useKibana().services; const isEditMode = slo !== undefined; @@ -146,10 +144,6 @@ export function SloEditForm({ slo }: Props) { setIsCreateRuleCheckboxChecked(!isCreateRuleCheckboxChecked); }; - const handleCloseRuleFlyout = async () => { - navigateToUrl(basePath.prepend(paths.observability.slos)); - }; - return ( <> @@ -256,17 +250,11 @@ export function SloEditForm({ slo }: Props) { - {isAddRuleFlyoutOpen && slo ? ( - - ) : null} + ); } 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 14cce99bb5d86..b8105814b852e 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 @@ -17,18 +17,18 @@ import { EuiSpacer, EuiText, } from '@elastic/eui'; +import { FieldSpec } from '@kbn/data-views-plugin/common'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { first, range, xor } from 'lodash'; import React from 'react'; import { Controller, useFieldArray, useFormContext } from 'react-hook-form'; -import { Field } from '../../../../hooks/slo/use_fetch_index_pattern_fields'; import { COMPARATOR_OPTIONS } from '../../constants'; import { CreateSLOForm } from '../../types'; import { MetricInput } from './metric_input'; interface MetricIndicatorProps { - indexFields: Field[]; + indexFields: FieldSpec[]; isLoadingIndex: boolean; } diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/timeslice_metric/metric_input.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/timeslice_metric/metric_input.tsx index 95cc277556020..22cbc42366baa 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/timeslice_metric/metric_input.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/timeslice_metric/metric_input.tsx @@ -12,10 +12,10 @@ import { EuiFormRow, EuiIconTip, } from '@elastic/eui'; +import { FieldSpec } from '@kbn/data-views-plugin/common'; import { i18n } from '@kbn/i18n'; import React, { useEffect, useState } from 'react'; import { Controller, useFormContext } from 'react-hook-form'; -import { Field } from '../../../../hooks/slo/use_fetch_index_pattern_fields'; import { AGGREGATION_OPTIONS, aggValueToLabel } from '../../helpers/aggregation_options'; import { createOptionsFromFields, Option } from '../../helpers/create_options'; import { CreateSLOForm } from '../../types'; @@ -55,7 +55,7 @@ interface MetricInputProps { metricIndex: number; indexPattern: string; isLoadingIndex: boolean; - indexFields: Field[]; + indexFields: FieldSpec[]; } export function MetricInput({ diff --git a/x-pack/plugins/observability/public/pages/slo_edit/components/timeslice_metric/timeslice_metric_indicator.tsx b/x-pack/plugins/observability/public/pages/slo_edit/components/timeslice_metric/timeslice_metric_indicator.tsx index b2d788bf28654..1c01219ffe1a2 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/components/timeslice_metric/timeslice_metric_indicator.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/components/timeslice_metric/timeslice_metric_indicator.tsx @@ -6,6 +6,7 @@ */ import { + EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, @@ -19,6 +20,7 @@ import { FormattedMessage } from '@kbn/i18n-react'; import React from 'react'; import { useFormContext } from 'react-hook-form'; import { ALL_VALUE } from '@kbn/slo-schema/src/schema/common'; +import { useFetchGroupByCardinality } from '../../../../hooks/slo/use_fetch_group_by_cardinality'; import { useFetchIndexPatternFields } from '../../../../hooks/slo/use_fetch_index_pattern_fields'; import { CreateSLOForm } from '../../types'; import { DataPreviewChart } from '../common/data_preview_chart'; @@ -34,10 +36,16 @@ export { NEW_TIMESLICE_METRIC } from './metric_indicator'; export function TimesliceMetricIndicatorTypeForm() { const { watch } = useFormContext(); const index = watch('indicator.params.index'); + const timestampField = watch('indicator.params.timestampField'); + const groupByField = watch('groupBy'); + const { isLoading: isIndexFieldsLoading, data: indexFields = [] } = useFetchIndexPatternFields(index); + const { isLoading: isGroupByCardinalityLoading, data: groupByCardinality } = + useFetchGroupByCardinality(index, timestampField, groupByField); + const timestampFields = indexFields.filter((field) => field.type === 'date'); - const partitionByFields = indexFields.filter((field) => field.aggregatable); + const groupByFields = indexFields.filter((field) => field.aggregatable); const { uiSettings } = useKibana().services; const threshold = watch('indicator.params.metric.threshold'); const comparator = watch('indicator.params.metric.comparator'); @@ -129,13 +137,13 @@ export function TimesliceMetricIndicatorTypeForm() { {i18n.translate('xpack.observability.slo.sloEdit.groupBy.label', { - defaultMessage: 'Partition by', + defaultMessage: 'Group by', })}{' '} } placeholder={i18n.translate('xpack.observability.slo.sloEdit.groupBy.placeholder', { - defaultMessage: 'Select an optional field to partition by', + defaultMessage: 'Select an optional field to group by', })} isLoading={!!index && isIndexFieldsLoading} isDisabled={!index} /> + {!isGroupByCardinalityLoading && !!groupByCardinality && ( + + )} + boolean ): Option[] { const options = fields 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 8d4d45a08d709..553969637129d 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 @@ -9,6 +9,7 @@ import { fireEvent, waitFor, cleanup } from '@testing-library/react'; import { createBrowserHistory } from 'history'; import React from 'react'; import Router from 'react-router-dom'; +import { observabilityAIAssistantPluginMock } from '@kbn/observability-ai-assistant-plugin/public/mock'; import { paths } from '../../../common/locators/paths'; import { buildSlo } from '../../data/slo/slo'; @@ -74,7 +75,6 @@ const mockKibana = () => { }, charts: { theme: { - useChartsTheme: () => {}, useChartsBaseTheme: () => {}, }, }, @@ -103,6 +103,7 @@ const mockKibana = () => { addSuccess: mockAddSuccess, }, }, + observabilityAIAssistant: observabilityAIAssistantPluginMock.createStartContract(), storage: { get: () => {}, }, @@ -128,6 +129,8 @@ describe('SLO Edit Page', () => { const mockCreate = jest.fn(); const mockUpdate = jest.fn(); + const history = createBrowserHistory(); + beforeEach(() => { jest.clearAllMocks(); mockKibana(); @@ -136,9 +139,8 @@ describe('SLO Edit Page', () => { jest.spyOn(console, 'warn').mockImplementation(() => {}); jest.spyOn(console, 'error').mockImplementation(() => {}); - const history = createBrowserHistory(); history.replace(''); - jest.spyOn(Router, 'useHistory').mockReturnValueOnce(history); + jest.spyOn(Router, 'useHistory').mockReturnValue(history); useFetchDataViewsMock.mockReturnValue({ isLoading: false, @@ -256,11 +258,9 @@ describe('SLO Edit Page', () => { it('prefills the form with values from URL', () => { jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: undefined }); - const history = createBrowserHistory(); history.replace( '/slos/create?_a=(indicator:(params:(environment:prod,service:cartService),type:sli.apm.transactionDuration))' ); - jest.spyOn(Router, 'useHistory').mockReturnValueOnce(history); jest .spyOn(Router, 'useLocation') .mockReturnValue({ pathname: 'foo', search: '', state: '', hash: '' }); @@ -336,11 +336,9 @@ describe('SLO Edit Page', () => { const slo = buildSlo({ id: '123' }); jest.spyOn(Router, 'useParams').mockReturnValue({ sloId: '123' }); - const history = createBrowserHistory(); history.push( '/slos/123/edit?_a=(name:%27updated-name%27,indicator:(params:(environment:prod,service:cartService),type:sli.apm.transactionDuration),objective:(target:0.92))' ); - jest.spyOn(Router, 'useHistory').mockReturnValueOnce(history); jest .spyOn(Router, 'useLocation') .mockReturnValue({ pathname: 'foo', search: '', state: '', hash: '' }); diff --git a/x-pack/plugins/observability/public/pages/slo_edit/slo_edit.tsx b/x-pack/plugins/observability/public/pages/slo_edit/slo_edit.tsx index 27fda5cfc61fa..485ee068fc6f7 100644 --- a/x-pack/plugins/observability/public/pages/slo_edit/slo_edit.tsx +++ b/x-pack/plugins/observability/public/pages/slo_edit/slo_edit.tsx @@ -33,7 +33,7 @@ export function SloEditPage() { const { sloId } = useParams<{ sloId: string | undefined }>(); const { hasAtLeast } = useLicense(); const hasRightLicense = hasAtLeast('platinum'); - const { data: slo, isInitialLoading } = useFetchSloDetails({ sloId }); + const { data: slo } = useFetchSloDetails({ sloId }); useBreadcrumbs([ { @@ -66,10 +66,6 @@ export function SloEditPage() { navigateToUrl(basePath.prepend(paths.observability.slos)); } - if (sloId && isInitialLoading) { - return null; - } - return ( (''); + const [activePage, setActivePage] = useState(0); + const [perPage, setPerPage] = useState(10); + + const handlePerPageChange = (perPageNumber: number) => { + setPerPage(perPageNumber); + setActivePage(0); + }; + + const { hasAtLeast } = useLicense(); + + const { isLoading, data, refetch } = useFetchSloDefinitions({ + name: search, + includeOutdatedOnly: true, + page: activePage + 1, + perPage, + }); + const { total } = data ?? { total: 0 }; + + const hasRequiredWritePrivileges = + !!globalDiagnosis?.userPrivileges.write.has_all_requested && hasWriteCapabilities; + + const hasPlatinumLicense = hasAtLeast('platinum') === true; + + const hasSlosAndHasPermissions = hasPlatinumLicense && hasRequiredWritePrivileges; + + const errors = !hasRequiredWritePrivileges ? ( + + {i18n.translate('xpack.observability.slo.slosOutdatedDefinitions.sloPermissionsError', { + defaultMessage: 'You must have write permissions for SLOs to access this page', + })} + + ) : !hasPlatinumLicense ? ( + + {i18n.translate('xpack.observability.slo.slosOutdatedDefinitions.licenseError', { + defaultMessage: 'You must have atleast a platinum license to access this page', + })} + + ) : null; + + return ( + + + + {!hasSlosAndHasPermissions ? ( + errors + ) : ( + <> +

+ {i18n.translate('xpack.observability.slo.slosOutdatedDefinitions.description', { + defaultMessage: + 'The following SLOs are from a previous version and need to either be reset to upgrade to the latest version OR deleted and removed from the system. When you reset the SLO, the transform will be updated to the latest version and the historical data will be regenerated from the source data.', + })} +

+ + + + + + {!isLoading && total === 0 && } + {!isLoading && + total > 0 && + data && + data.results.map((slo) => ( + + ))} + + + {!isLoading && data && ( + + )} + + )} +
+ ); +} diff --git a/x-pack/plugins/observability/public/pages/slo_outdated_definitions/outdated_slo.tsx b/x-pack/plugins/observability/public/pages/slo_outdated_definitions/outdated_slo.tsx new file mode 100644 index 0000000000000..f8f5cfeb46848 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slo_outdated_definitions/outdated_slo.tsx @@ -0,0 +1,124 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React, { useState } from 'react'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiText, EuiButton } from '@elastic/eui'; +import { SLOResponse } from '@kbn/slo-schema'; +import { SloDeleteConfirmationModal } from '../../components/slo/delete_confirmation_modal/slo_delete_confirmation_modal'; +import { SloTimeWindowBadge } from '../slos/components/badges/slo_time_window_badge'; +import { SloIndicatorTypeBadge } from '../slos/components/badges/slo_indicator_type_badge'; +import { useDeleteSlo } from '../../hooks/slo/use_delete_slo'; +import { useResetSlo } from '../../hooks/slo/use_reset_slo'; +import { SloResetConfirmationModal } from '../../components/slo/reset_confirmation_modal/slo_reset_confirmation_modal'; + +interface OutdatedSloProps { + slo: SLOResponse; + onReset: () => void; + onDelete: () => void; +} + +export function OutdatedSlo({ slo, onReset, onDelete }: OutdatedSloProps) { + const { mutateAsync: resetSlo, isLoading: isResetLoading } = useResetSlo(); + const { mutateAsync: deleteSlo, isLoading: isDeleteLoading } = useDeleteSlo(); + const [isDeleteConfirmationModalOpen, setDeleteConfirmationModalOpen] = useState(false); + const [isResetConfirmationModalOpen, setResetConfirmationModalOpen] = useState(false); + + const handleDelete = () => { + setDeleteConfirmationModalOpen(true); + }; + + const handleDeleteConfirm = async () => { + setDeleteConfirmationModalOpen(false); + await deleteSlo({ id: slo.id, name: slo.name }); + onDelete(); + }; + + const handleDeleteCancel = () => { + setDeleteConfirmationModalOpen(false); + }; + + const handleReset = () => { + setResetConfirmationModalOpen(true); + }; + + const handleResetConfirm = async () => { + setResetConfirmationModalOpen(false); + await resetSlo({ id: slo.id, name: slo.name }); + onReset(); + }; + + const handleResetCancel = () => { + setResetConfirmationModalOpen(false); + }; + return ( + + + + + + + {slo.name} + + + + + + + + + + + + + + + + + + + + {isDeleteConfirmationModalOpen ? ( + + ) : null} + {isResetConfirmationModalOpen ? ( + + ) : null} + + ); +} diff --git a/x-pack/plugins/observability/public/pages/slo_outdated_definitions/outdated_slo_search_bar.tsx b/x-pack/plugins/observability/public/pages/slo_outdated_definitions/outdated_slo_search_bar.tsx new file mode 100644 index 0000000000000..7e491f4933e20 --- /dev/null +++ b/x-pack/plugins/observability/public/pages/slo_outdated_definitions/outdated_slo_search_bar.tsx @@ -0,0 +1,82 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React, { useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import { EuiFlexGroup, EuiFlexItem, EuiButton, EuiFieldSearch } from '@elastic/eui'; + +interface OutdatedSloSearchBarProps { + initialSearch?: string; + onRefresh: () => void; + onSearch: (search: string) => void; +} + +export function OutdatedSloSearchBar({ + onSearch, + onRefresh, + initialSearch = '', +}: OutdatedSloSearchBarProps) { + const [tempSearch, setTempSearch] = useState(initialSearch); + const [search, setSearch] = useState(initialSearch); + + const refreshOrUpdateSearch = () => { + if (tempSearch !== search) { + setSearch(tempSearch); + onSearch(tempSearch); + } else { + onRefresh(); + } + }; + + const handleClick = (event: React.ChangeEvent) => + setTempSearch(event.target.value); + + const handleKeyPress = (event: React.KeyboardEvent) => { + if (event.key === 'Enter') { + refreshOrUpdateSearch(); + } + }; + + return ( + + + + + + {search === tempSearch && ( + + {i18n.translate('xpack.observability.slosOutdatedDefinitions.refreshButtonLabel', { + defaultMessage: 'Refresh', + })} + + )} + {search !== tempSearch && ( + + {i18n.translate('xpack.observability.slosOutdatedDefinitions.updateButtonLabel', { + defaultMessage: 'Update', + })} + + )} + + + ); +} 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 c85eb6776680b..316298cd8d44c 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 @@ -7,10 +7,9 @@ import React from 'react'; import { EuiBadge, EuiFlexItem, EuiToolTip, EuiBadgeProps } from '@elastic/eui'; -import { SLOWithSummaryResponse } from '@kbn/slo-schema'; -import { i18n } from '@kbn/i18n'; - +import { SLOResponse, SLOWithSummaryResponse } from '@kbn/slo-schema'; import { euiLightVars } from '@kbn/ui-theme'; +import { i18n } from '@kbn/i18n'; import { useKibana } from '../../../../utils/kibana_react'; import { convertSliApmParamsToApmAppDeeplinkUrl } from '../../../../utils/slo/convert_sli_apm_params_to_apm_app_deeplink_url'; import { isApmIndicatorType } from '../../../../utils/slo/indicator'; @@ -18,7 +17,7 @@ import { toIndicatorTypeLabel } from '../../../../utils/slo/labels'; export interface Props { color?: EuiBadgeProps['color']; - slo: SLOWithSummaryResponse; + slo: SLOWithSummaryResponse | SLOResponse; } export function SloIndicatorTypeBadge({ slo, color }: Props) { diff --git a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_time_window_badge.tsx b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_time_window_badge.tsx index b5d4ecd0224fe..384c17f251b01 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/badges/slo_time_window_badge.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/badges/slo_time_window_badge.tsx @@ -7,7 +7,7 @@ import { EuiBadge, EuiBadgeProps, EuiFlexItem } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { rollingTimeWindowTypeSchema, SLOWithSummaryResponse } from '@kbn/slo-schema'; +import { rollingTimeWindowTypeSchema, SLOResponse, SLOWithSummaryResponse } from '@kbn/slo-schema'; import { euiLightVars } from '@kbn/ui-theme'; import moment from 'moment'; import React from 'react'; @@ -16,7 +16,7 @@ import { toDurationLabel } from '../../../../utils/slo/labels'; export interface Props { color?: EuiBadgeProps['color']; - slo: SLOWithSummaryResponse; + slo: SLOWithSummaryResponse | SLOResponse; } export function SloTimeWindowBadge({ slo, color }: Props) { diff --git a/x-pack/plugins/observability/public/pages/slos/components/card_view/slo_card_item.tsx b/x-pack/plugins/observability/public/pages/slos/components/card_view/slo_card_item.tsx index 8da351e5ba0e3..ccdfe39481558 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/card_view/slo_card_item.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/card_view/slo_card_item.tsx @@ -9,8 +9,8 @@ import React, { useState } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { Chart, - DARK_THEME, isMetricElementEvent, + LEGACY_DARK_THEME, Metric, MetricTrendShape, Settings, @@ -19,6 +19,11 @@ import { EuiIcon, EuiPanel, useEuiBackgroundColor } from '@elastic/eui'; import { ALL_VALUE, HistoricalSummaryResponse, SLOWithSummaryResponse } from '@kbn/slo-schema'; import { Rule } from '@kbn/triggers-actions-ui-plugin/public'; import { i18n } from '@kbn/i18n'; +import { css } from '@emotion/react'; +import { + LazySavedObjectSaveModalDashboard, + withSuspense, +} from '@kbn/presentation-util-plugin/public'; import { SloCardBadgesPortal } from './badges_portal'; import { useSloListActions } from '../../hooks/use_slo_list_actions'; import { BurnRateRuleFlyout } from '../common/burn_rate_rule_flyout'; @@ -29,7 +34,7 @@ import { SloCardItemActions } from './slo_card_item_actions'; import { SloRule } from '../../../../hooks/slo/use_fetch_rules_for_slo'; import { SloDeleteConfirmationModal } from '../../../../components/slo/delete_confirmation_modal/slo_delete_confirmation_modal'; import { SloCardItemBadges } from './slo_card_item_badges'; - +const SavedObjectSaveModalDashboard = withSuspense(LazySavedObjectSaveModalDashboard); export interface Props { slo: SLOWithSummaryResponse; rules: Array> | undefined; @@ -52,7 +57,7 @@ export const useSloCardColor = (status?: SLOWithSummaryResponse['summary']['stat return colors[status ?? 'NO_DATA']; }; -const getSubTitle = (slo: SLOWithSummaryResponse, cardsPerRow: number) => { +const getSubTitle = (slo: SLOWithSummaryResponse) => { return slo.groupBy && slo.groupBy !== ALL_VALUE ? `${slo.groupBy}: ${slo.instanceId}` : ''; }; @@ -63,15 +68,17 @@ export function SloCardItem({ slo, rules, activeAlerts, historicalSummary, cards const [isActionsPopoverOpen, setIsActionsPopoverOpen] = useState(false); const [isAddRuleFlyoutOpen, setIsAddRuleFlyoutOpen] = useState(false); const [isDeleteConfirmationModalOpen, setDeleteConfirmationModalOpen] = useState(false); - + const [isDashboardAttachmentReady, setDashboardAttachmentReady] = useState(false); const historicalSliData = formatHistoricalData(historicalSummary, 'sli_value'); - const { handleCreateRule, handleDeleteCancel, handleDeleteConfirm } = useSloListActions({ - slo, - setDeleteConfirmationModalOpen, - setIsActionsPopoverOpen, - setIsAddRuleFlyoutOpen, - }); + const { handleCreateRule, handleDeleteCancel, handleDeleteConfirm, handleAttachToDashboardSave } = + useSloListActions({ + slo, + setDeleteConfirmationModalOpen, + setIsActionsPopoverOpen, + setIsAddRuleFlyoutOpen, + setDashboardAttachmentReady, + }); return ( <> @@ -88,14 +95,14 @@ export function SloCardItem({ slo, rules, activeAlerts, historicalSummary, cards } }} paddingSize="none" - style={{ - height: '182px', - overflow: 'hidden', - position: 'relative', - }} + css={css` + height: 182px; + overflow: hidden; + position: relative; + `} title={slo.summary.status} > - + {(isMouseOver || isActionsPopoverOpen) && ( )} @@ -129,17 +137,34 @@ export function SloCardItem({ slo, rules, activeAlerts, historicalSummary, cards onConfirm={handleDeleteConfirm} /> ) : null} + {isDashboardAttachmentReady ? ( + { + setDashboardAttachmentReady(false); + }} + onSave={handleAttachToDashboardSave} + /> + ) : null} ); } export function SloCardChart({ slo, - cardsPerRow, historicalSliData, }: { slo: SLOWithSummaryResponse; - cardsPerRow: number; historicalSliData?: Array<{ key?: number; value?: number }>; }) { const { @@ -147,13 +172,14 @@ export function SloCardChart({ } = useKibana().services; const cardColor = useSloCardColor(slo.summary.status); - const subTitle = getSubTitle(slo, cardsPerRow); + const subTitle = getSubTitle(slo); const { sliValue, sloTarget, sloDetailsUrl } = useSloFormattedSummary(slo); return ( { if (isMetricElementEvent(d)) { navigateToUrl(sloDetailsUrl); diff --git a/x-pack/plugins/observability/public/pages/slos/components/card_view/slo_card_item_actions.tsx b/x-pack/plugins/observability/public/pages/slos/components/card_view/slo_card_item_actions.tsx index 51d1887d433fb..ff4d7f363caee 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/card_view/slo_card_item_actions.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/card_view/slo_card_item_actions.tsx @@ -41,6 +41,7 @@ interface Props { setIsActionsPopoverOpen: (value: boolean) => void; setDeleteConfirmationModalOpen: (value: boolean) => void; setIsAddRuleFlyoutOpen: (value: boolean) => void; + setDashboardAttachmentReady: (value: boolean) => void; } export function SloCardItemActions(props: Props) { 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 a8d43c35217d1..3e1a80f147f2e 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 @@ -68,27 +68,29 @@ export function SloListCardView({ } return ( - - {sloList.map((slo) => ( - - - historicalSummary.sloId === slo.id && - historicalSummary.instanceId === (slo.instanceId ?? ALL_VALUE) - )?.data - } - historicalSummaryLoading={historicalSummaryLoading} - cardsPerRow={Number(cardsPerRow)} - /> - - ))} + + {sloList + .filter((slo) => slo.summary) + .map((slo) => ( + + + historicalSummary.sloId === slo.id && + historicalSummary.instanceId === (slo.instanceId ?? ALL_VALUE) + )?.data + } + historicalSummaryLoading={historicalSummaryLoading} + cardsPerRow={Number(cardsPerRow)} + /> + + ))} ); } diff --git a/x-pack/plugins/observability/public/pages/slos/components/common/burn_rate_rule_flyout.tsx b/x-pack/plugins/observability/public/pages/slos/components/common/burn_rate_rule_flyout.tsx index a02730231ae5f..871c11d39bdde 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/common/burn_rate_rule_flyout.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/common/burn_rate_rule_flyout.tsx @@ -8,6 +8,7 @@ import React from 'react'; import { SLOWithSummaryResponse } from '@kbn/slo-schema'; import { useQueryClient } from '@tanstack/react-query'; +import { paths } from '../../../../../common/locators/paths'; import { useGetFilteredRuleTypes } from '../../../../hooks/use_get_filtered_rule_types'; import { sloKeys } from '../../../../hooks/slo/query_key_factory'; import { useKibana } from '../../../../utils/kibana_react'; @@ -17,13 +18,17 @@ import { sloFeatureId } from '../../../../../common'; export function BurnRateRuleFlyout({ slo, isAddRuleFlyoutOpen, + canChangeTrigger, setIsAddRuleFlyoutOpen, }: { - slo: SLOWithSummaryResponse; + slo?: SLOWithSummaryResponse; isAddRuleFlyoutOpen: boolean; - setIsAddRuleFlyoutOpen: (value: boolean) => void; + canChangeTrigger?: boolean; + setIsAddRuleFlyoutOpen?: (value: boolean) => void; }) { const { + application: { navigateToUrl }, + http: { basePath }, triggersActionsUi: { getAddRuleFlyout: AddRuleFlyout }, } = useKibana().services; @@ -32,19 +37,30 @@ export function BurnRateRuleFlyout({ const queryClient = useQueryClient(); const handleSavedRule = async () => { - queryClient.invalidateQueries({ queryKey: sloKeys.rules(), exact: false }); + if (setIsAddRuleFlyoutOpen) { + queryClient.invalidateQueries({ queryKey: sloKeys.rules(), exact: false }); + } else { + navigateToUrl(basePath.prepend(paths.observability.slos)); + } }; - return isAddRuleFlyoutOpen ? ( + const handleCloseRuleFlyout = async () => { + if (setIsAddRuleFlyoutOpen) { + setIsAddRuleFlyoutOpen(false); + } else { + navigateToUrl(basePath.prepend(paths.observability.slos)); + } + }; + + return isAddRuleFlyoutOpen && slo ? ( { - setIsAddRuleFlyoutOpen(false); - }} + onClose={handleCloseRuleFlyout} useRuleProducer /> ) : null; 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 6052007abc328..d884de55aed54 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 { useCloneSlo } from '../../../../hooks/slo/use_clone_slo'; import { rulesLocatorID, sloFeatureId } from '../../../../../common'; import { SLO_BURN_RATE_RULE_TYPE_ID } from '../../../../../common/constants'; import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n'; @@ -28,7 +29,6 @@ import { SloStatusBadge } from '../../../../components/slo/slo_status_badge'; import { SloActiveAlertsBadge } from '../../../../components/slo/slo_status_badge/slo_active_alerts_badge'; import { sloKeys } from '../../../../hooks/slo/query_key_factory'; import { useCapabilities } from '../../../../hooks/slo/use_capabilities'; -import { useCloneSlo } from '../../../../hooks/slo/use_clone_slo'; import { useDeleteSlo } from '../../../../hooks/slo/use_delete_slo'; import { useFetchActiveAlerts } from '../../../../hooks/slo/use_fetch_active_alerts'; import { useFetchHistoricalSummary } from '../../../../hooks/slo/use_fetch_historical_summary'; @@ -37,10 +37,6 @@ import { useGetFilteredRuleTypes } from '../../../../hooks/use_get_filtered_rule import { RulesParams } from '../../../../locators/rules'; import { useKibana } from '../../../../utils/kibana_react'; import { formatHistoricalData } from '../../../../utils/slo/chart_data_formatter'; -import { - transformCreateSLOFormToCreateSLOInput, - transformSloResponseToCreateSloForm, -} from '../../../slo_edit/helpers/process_slo_form_values'; import { SloRulesBadge } from '../badges/slo_rules_badge'; import { SloListEmpty } from '../slo_list_empty'; import { SloListError } from '../slo_list_error'; @@ -72,7 +68,6 @@ export function SloListCompactView({ sloList, loading, error }: Props) { const filteredRuleTypes = useGetFilteredRuleTypes(); const queryClient = useQueryClient(); - const { mutate: cloneSlo } = useCloneSlo(); const { mutate: deleteSlo } = useDeleteSlo(); const [sloToAddRule, setSloToAddRule] = useState(undefined); @@ -102,6 +97,8 @@ export function SloListCompactView({ sloList, loading, error }: Props) { list: sloList.map((slo) => ({ sloId: slo.id, instanceId: slo.instanceId ?? ALL_VALUE })), }); + const navigateToClone = useCloneSlo(); + const actions: Array> = [ { type: 'icon', @@ -180,11 +177,7 @@ export function SloListCompactView({ sloList, loading, error }: Props) { 'data-test-subj': 'sloActionsClone', enabled: (_) => hasWriteCapabilities, onClick: (slo: SLOWithSummaryResponse) => { - const newSlo = transformCreateSLOFormToCreateSLOInput( - transformSloResponseToCreateSloForm({ ...slo, name: `[Copy] ${slo.name}` })! - ); - - cloneSlo({ slo: newSlo, originalSloId: slo.id }); + navigateToClone(slo); }, }, { @@ -272,10 +265,10 @@ export function SloListCompactView({ sloList, loading, error }: Props) { slo.groupBy !== ALL_VALUE ? ( void; setDeleteConfirmationModalOpen: (value: boolean) => void; setIsAddRuleFlyoutOpen: (value: boolean) => void; + setDashboardAttachmentReady?: (value: boolean) => void; btnProps?: Partial; } const CustomShadowPanel = styled(EuiPanel)<{ shadow: string }>` @@ -63,6 +60,7 @@ export function SloItemActions({ setIsActionsPopoverOpen, setIsAddRuleFlyoutOpen, setDeleteConfirmationModalOpen, + setDashboardAttachmentReady, btnProps, }: Props) { const { @@ -73,7 +71,6 @@ export function SloItemActions({ }, } = useKibana().services; const { hasWriteCapabilities } = useCapabilities(); - const { mutate: cloneSlo } = useCloneSlo(); const sloDetailsUrl = basePath.prepend( paths.observability.sloDetails( @@ -94,18 +91,15 @@ export function SloItemActions({ navigateToUrl(basePath.prepend(paths.observability.sloEdit(slo.id))); }; - const handleNavigateToRules = async () => { - const locator = locators.get(rulesLocatorID); - locator?.navigate({ params: { sloId: slo.id } }, { replace: false }); - }; + const navigateToClone = useCloneSlo(); const handleClone = () => { - const newSlo = transformCreateSLOFormToCreateSLOInput( - transformSloResponseToCreateSloForm({ ...slo, name: `[Copy] ${slo.name}` })! - ); + navigateToClone(slo); + }; - cloneSlo({ slo: newSlo, originalSloId: slo.id }); - setIsActionsPopoverOpen(false); + const handleNavigateToRules = async () => { + const locator = locators.get(rulesLocatorID); + locator?.navigate({ params: { sloId: slo.id } }, { replace: false }); }; const handleDelete = () => { @@ -118,6 +112,13 @@ export function SloItemActions({ setIsAddRuleFlyoutOpen(true); }; + const handleAttachToDashboard = () => { + setIsActionsPopoverOpen(false); + if (setDashboardAttachmentReady) { + setDashboardAttachmentReady(true); + } + }; + const btn = ( , + + {i18n.translate('xpack.observability.slo.item.actions.attachToDashboard', { + defaultMessage: 'Attach to Dashboard', + })} + , ]} /> 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 4714cd4c2355f..a25aae4afd4f9 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 @@ -5,16 +5,14 @@ * 2.0. */ -import { EuiFlexGroup, EuiFlexItem, EuiPagination } from '@elastic/eui'; +import { EuiFlexGroup, EuiFlexItem, EuiTablePagination } from '@elastic/eui'; import { useIsMutating } from '@tanstack/react-query'; import React, { useState } from 'react'; -import useLocalStorage from 'react-use/lib/useLocalStorage'; -import { SlosView } from './slos_view'; -import { SLO_LIST_IS_COMPACT } from './slo_view_settings'; -import { SLOViewType, ToggleSLOView } from './toggle_slo_view'; import { useFetchSloList } from '../../../hooks/slo/use_fetch_slo_list'; import { useUrlSearchState } from '../hooks/use_url_search_state'; -import { SloListSearchBar, SortField } from './slo_list_search_bar'; +import { SlosView } from './slos_view'; +import { SloListSearchBar, SortDirection, SortField } from './slo_list_search_bar'; +import { SLOView, ToggleSLOView } from './toggle_slo_view'; export interface Props { autoRefresh: boolean; @@ -23,10 +21,12 @@ export interface Props { export function SloList({ autoRefresh }: Props) { const { state, store: storeState } = useUrlSearchState(); const [page, setPage] = useState(state.page); + const [perPage, setPerPage] = useState(state.perPage); const [query, setQuery] = useState(state.kqlQuery); const [sort, setSort] = useState(state.sort.by); - const [direction] = useState<'asc' | 'desc'>(state.sort.direction); - const [sloView, setSLOView] = useState('cardView'); + const [direction] = useState(state.sort.direction); + const [view, setView] = useState(state.view); + const [isCompact, setCompact] = useState(state.compact); const { isLoading, @@ -34,6 +34,7 @@ export function SloList({ autoRefresh }: Props) { isError, data: sloList, } = useFetchSloList({ + perPage, page: page + 1, kqlQuery: query, sortBy: sort, @@ -41,14 +42,12 @@ export function SloList({ autoRefresh }: Props) { shouldRefetch: autoRefresh, }); - const { results = [], total = 0, perPage = 0 } = sloList ?? {}; + const { results = [], total = 0 } = sloList ?? {}; const isCreatingSlo = Boolean(useIsMutating(['creatingSlo'])); const isCloningSlo = Boolean(useIsMutating(['cloningSlo'])); const isUpdatingSlo = Boolean(useIsMutating(['updatingSlo'])); const isDeletingSlo = Boolean(useIsMutating(['deleteSlo'])); - const [isCompact, setIsCompact] = useLocalStorage<'true' | 'false'>(SLO_LIST_IS_COMPACT, 'true'); - const isCompactView = isCompact === 'true'; const handlePageClick = (pageNumber: number) => { setPage(pageNumber); @@ -67,6 +66,17 @@ export function SloList({ autoRefresh }: Props) { storeState({ page: 0, sort: { by: newSort, direction: state.sort.direction } }); }; + const handleChangeView = (newView: SLOView) => { + setView(newView); + storeState({ view: newView }); + }; + + const handleToggleCompactView = () => { + const newCompact = !isCompact; + setCompact(newCompact); + storeState({ compact: newCompact }); + }; + return ( @@ -79,33 +89,33 @@ export function SloList({ autoRefresh }: Props) { - isCompact === 'true' ? setIsCompact('false') : setIsCompact('true') - } - isCompact={isCompactView} + sloView={view} + onChangeView={handleChangeView} + onToggleCompactView={handleToggleCompactView} + isCompact={isCompact} /> {total > 0 ? ( - - - - - + { + setPerPage(newPerPage); + storeState({ perPage: newPerPage }); + }} + /> ) : null} diff --git a/x-pack/plugins/observability/public/pages/slos/components/slo_list_item.tsx b/x-pack/plugins/observability/public/pages/slos/components/slo_list_item.tsx index 31455cea01905..dc3865af00050 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/slo_list_item.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/slo_list_item.tsx @@ -9,6 +9,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiText } from '@elastic/eui'; import { HistoricalSummaryResponse, SLOWithSummaryResponse } from '@kbn/slo-schema'; import type { Rule } from '@kbn/triggers-actions-ui-plugin/public'; import React, { useState } from 'react'; + import { SloDeleteConfirmationModal } from '../../../components/slo/delete_confirmation_modal/slo_delete_confirmation_modal'; import { useSloFormattedSummary } from '../hooks/use_slo_summary'; import { BurnRateRuleFlyout } from './common/burn_rate_rule_flyout'; 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 f4d251d063490..b06a2f3fea478 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 @@ -32,6 +32,7 @@ export interface Props { } export type SortField = 'sli_value' | 'error_budget_consumed' | 'error_budget_remaining' | 'status'; +export type SortDirection = 'asc' | 'desc'; export type Item = EuiSelectableOption & { label: string; diff --git a/x-pack/plugins/observability/public/pages/slos/components/slo_sparkline.tsx b/x-pack/plugins/observability/public/pages/slos/components/slo_sparkline.tsx index 0beb36f587ff1..0bb96b39d0649 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/slo_sparkline.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/slo_sparkline.tsx @@ -41,7 +41,6 @@ export interface Props { export function SloSparkline({ chart, data, id, isLoading, size, state }: Props) { const charts = useKibana().services.charts; - const theme = charts.theme.useChartsTheme(); const baseTheme = charts.theme.useChartsBaseTheme(); const { euiTheme } = useEuiTheme(); @@ -61,7 +60,7 @@ export function SloSparkline({ chart, data, id, isLoading, size, state }: Props) diff --git a/x-pack/plugins/observability/public/pages/slos/components/toggle_slo_view.tsx b/x-pack/plugins/observability/public/pages/slos/components/toggle_slo_view.tsx index d7e3f6df00422..e5881e8f1ff4c 100644 --- a/x-pack/plugins/observability/public/pages/slos/components/toggle_slo_view.tsx +++ b/x-pack/plugins/observability/public/pages/slos/components/toggle_slo_view.tsx @@ -10,14 +10,15 @@ import { i18n } from '@kbn/i18n'; import { EuiButtonGroup, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import { SLOViewSettings } from './slo_view_settings'; -export type SLOViewType = 'cardView' | 'listView'; +export type SLOView = 'cardView' | 'listView'; interface Props { - toggleCompactView: () => void; + onToggleCompactView: () => void; + onChangeView: (view: SLOView) => void; isCompact: boolean; - setSLOView: (view: SLOViewType) => void; - sloView: SLOViewType; + sloView: SLOView; } + const toggleButtonsIcons = [ { id: `cardView`, @@ -33,7 +34,12 @@ const toggleButtonsIcons = [ }, ]; -export function ToggleSLOView({ sloView, setSLOView, toggleCompactView, isCompact = true }: Props) { +export function ToggleSLOView({ + sloView, + onChangeView, + onToggleCompactView, + isCompact = true, +}: Props) { return ( @@ -43,12 +49,12 @@ export function ToggleSLOView({ sloView, setSLOView, toggleCompactView, isCompac })} options={toggleButtonsIcons} idSelected={sloView} - onChange={(id) => setSLOView(id as SLOViewType)} + onChange={(id) => onChangeView(id as SLOView)} isIconOnly /> - + ); diff --git a/x-pack/plugins/observability/public/pages/slos/hooks/use_slo_list_actions.ts b/x-pack/plugins/observability/public/pages/slos/hooks/use_slo_list_actions.ts index 169e1e54c5222..bdc6cf3a4c49d 100644 --- a/x-pack/plugins/observability/public/pages/slos/hooks/use_slo_list_actions.ts +++ b/x-pack/plugins/observability/public/pages/slos/hooks/use_slo_list_actions.ts @@ -6,19 +6,26 @@ */ import { SLOWithSummaryResponse } from '@kbn/slo-schema'; +import { SaveModalDashboardProps } from '@kbn/presentation-util-plugin/public'; +import { useCallback } from 'react'; import { useDeleteSlo } from '../../../hooks/slo/use_delete_slo'; +import { SLO_EMBEDDABLE } from '../../../embeddable/slo/overview/slo_embeddable'; +import { useKibana } from '../../../utils/kibana_react'; export function useSloListActions({ slo, setIsAddRuleFlyoutOpen, setIsActionsPopoverOpen, setDeleteConfirmationModalOpen, + setDashboardAttachmentReady, }: { slo: SLOWithSummaryResponse; setIsActionsPopoverOpen: (val: boolean) => void; setIsAddRuleFlyoutOpen: (val: boolean) => void; setDeleteConfirmationModalOpen: (val: boolean) => void; + setDashboardAttachmentReady?: (val: boolean) => void; }) { + const { embeddable } = useKibana().services; const { mutate: deleteSlo } = useDeleteSlo(); const handleDeleteConfirm = () => { @@ -34,9 +41,35 @@ export function useSloListActions({ setIsAddRuleFlyoutOpen(true); }; + const handleAttachToDashboardSave: SaveModalDashboardProps['onSave'] = useCallback( + ({ dashboardId, newTitle, newDescription }) => { + const stateTransfer = embeddable!.getStateTransfer(); + const embeddableInput = { + title: newTitle, + description: newDescription, + sloId: slo.id, + sloInstanceId: slo.instanceId, + }; + + const state = { + input: embeddableInput, + type: SLO_EMBEDDABLE, + }; + + const path = dashboardId === 'new' ? '#/create' : `#/view/${dashboardId}`; + + stateTransfer.navigateToWithEmbeddablePackage('dashboards', { + state, + path, + }); + }, + [embeddable, slo.id, slo.instanceId] + ); + return { handleDeleteConfirm, handleDeleteCancel, handleCreateRule, + handleAttachToDashboardSave, }; } 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 e3c8449444a1d..0e95dfe225579 100644 --- a/x-pack/plugins/observability/public/pages/slos/hooks/use_url_search_state.ts +++ b/x-pack/plugins/observability/public/pages/slos/hooks/use_url_search_state.ts @@ -8,25 +8,31 @@ import { createKbnUrlStateStorage } from '@kbn/kibana-utils-plugin/public'; import deepmerge from 'deepmerge'; import { useHistory } from 'react-router-dom'; -import type { SortField, ViewMode } from '../components/slo_list_search_bar'; +import { DEFAULT_SLO_PAGE_SIZE } from '../../../../common/slo/constants'; +import type { SortField, SortDirection } from '../components/slo_list_search_bar'; +import type { SLOView } from '../components/toggle_slo_view'; export const SLO_LIST_SEARCH_URL_STORAGE_KEY = 'search'; export interface SearchState { kqlQuery: string; page: number; + perPage: number; sort: { by: SortField; - direction: 'asc' | 'desc'; + direction: SortDirection; }; - viewMode: ViewMode; + view: SLOView; + compact: boolean; } export const DEFAULT_STATE = { kqlQuery: '', page: 0, + perPage: DEFAULT_SLO_PAGE_SIZE, sort: { by: 'status' as const, direction: 'desc' as const }, - viewMode: 'compact' as const, + view: 'cardView' as const, + compact: true, }; export function useUrlSearchState(): { diff --git a/x-pack/plugins/observability/public/pages/slos/slos.test.tsx b/x-pack/plugins/observability/public/pages/slos/slos.test.tsx index 40fd12d4b003e..61e6190b79956 100644 --- a/x-pack/plugins/observability/public/pages/slos/slos.test.tsx +++ b/x-pack/plugins/observability/public/pages/slos/slos.test.tsx @@ -7,7 +7,7 @@ import { act, fireEvent, screen, waitFor } from '@testing-library/react'; import React from 'react'; - +import { observabilityAIAssistantPluginMock } from '@kbn/observability-ai-assistant-plugin/public/mock'; import { waitForEuiPopoverOpen } from '@elastic/eui/lib/test/rtl'; import { chartPluginMock } from '@kbn/charts-plugin/public/mocks'; @@ -15,7 +15,6 @@ import { paths } from '../../../common/locators/paths'; import { historicalSummaryData } from '../../data/slo/historical_summary_data'; import { emptySloList, sloList } from '../../data/slo/slo'; import { useCapabilities } from '../../hooks/slo/use_capabilities'; -import { useCloneSlo } from '../../hooks/slo/use_clone_slo'; import { useCreateSlo } from '../../hooks/slo/use_create_slo'; import { useDeleteSlo } from '../../hooks/slo/use_delete_slo'; import { useFetchHistoricalSummary } from '../../hooks/slo/use_fetch_historical_summary'; @@ -24,6 +23,7 @@ import { useLicense } from '../../hooks/use_license'; import { useKibana } from '../../utils/kibana_react'; import { render } from '../../utils/test_helper'; import { SlosPage } from './slos'; +import { encode } from '@kbn/rison'; jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), @@ -35,7 +35,6 @@ jest.mock('../../utils/kibana_react'); jest.mock('../../hooks/use_license'); jest.mock('../../hooks/slo/use_fetch_slo_list'); jest.mock('../../hooks/slo/use_create_slo'); -jest.mock('../../hooks/slo/use_clone_slo'); jest.mock('../../hooks/slo/use_delete_slo'); jest.mock('../../hooks/slo/use_fetch_historical_summary'); jest.mock('../../hooks/slo/use_capabilities'); @@ -44,17 +43,14 @@ const useKibanaMock = useKibana as jest.Mock; const useLicenseMock = useLicense as jest.Mock; const useFetchSloListMock = useFetchSloList as jest.Mock; const useCreateSloMock = useCreateSlo as jest.Mock; -const useCloneSloMock = useCloneSlo as jest.Mock; const useDeleteSloMock = useDeleteSlo as jest.Mock; const useFetchHistoricalSummaryMock = useFetchHistoricalSummary as jest.Mock; const useCapabilitiesMock = useCapabilities as jest.Mock; const mockCreateSlo = jest.fn(); -const mockCloneSlo = jest.fn(); const mockDeleteSlo = jest.fn(); useCreateSloMock.mockReturnValue({ mutate: mockCreateSlo }); -useCloneSloMock.mockReturnValue({ mutate: mockCloneSlo }); useDeleteSloMock.mockReturnValue({ mutate: mockDeleteSlo }); const mockNavigate = jest.fn(); @@ -94,6 +90,7 @@ const mockKibana = () => { addError: mockAddError, }, }, + observabilityAIAssistant: observabilityAIAssistantPluginMock.createStartContract(), share: { url: { locators: { @@ -358,7 +355,14 @@ describe('SLOs Page', () => { button.click(); - expect(mockCloneSlo).toBeCalled(); + await waitFor(() => { + const slo = sloList.results.at(0); + expect(mockNavigate).toBeCalledWith( + paths.observability.sloCreateWithEncodedForm( + encode({ ...slo, name: `[Copy] ${slo!.name}`, id: undefined }) + ) + ); + }); }); }); }); diff --git a/x-pack/plugins/observability/public/pages/slos/slos.tsx b/x-pack/plugins/observability/public/pages/slos/slos.tsx index e010df224ce1c..4ea3f27fade3c 100644 --- a/x-pack/plugins/observability/public/pages/slos/slos.tsx +++ b/x-pack/plugins/observability/public/pages/slos/slos.tsx @@ -6,7 +6,7 @@ */ import React, { useEffect, useState } from 'react'; -import { EuiButton } from '@elastic/eui'; +import { EuiButton, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { useBreadcrumbs } from '@kbn/observability-shared-plugin/public'; @@ -22,6 +22,7 @@ import { FeedbackButton } from '../../components/slo/feedback_button/feedback_bu import { paths } from '../../../common/locators/paths'; import { useAutoRefreshStorage } from '../../components/slo/auto_refresh_button/hooks/use_auto_refresh_storage'; import { HeaderMenu } from '../overview/components/header_menu/header_menu'; +import { SloOutdatedCallout } from '../../components/slo/slo_outdated_callout'; export function SlosPage() { const { @@ -90,6 +91,8 @@ export function SlosPage() { data-test-subj="slosPage" > + +
); diff --git a/x-pack/plugins/observability/public/pages/slos_welcome/slos_welcome.test.tsx b/x-pack/plugins/observability/public/pages/slos_welcome/slos_welcome.test.tsx index a6b4f671d306c..8290ecc2988be 100644 --- a/x-pack/plugins/observability/public/pages/slos_welcome/slos_welcome.test.tsx +++ b/x-pack/plugins/observability/public/pages/slos_welcome/slos_welcome.test.tsx @@ -17,6 +17,7 @@ import { SlosWelcomePage } from './slos_welcome'; import { emptySloList, sloList } from '../../data/slo/slo'; import { useCapabilities } from '../../hooks/slo/use_capabilities'; import { paths } from '../../../common/locators/paths'; +import { observabilityAIAssistantPluginMock } from '@kbn/observability-ai-assistant-plugin/public/mock'; jest.mock('@kbn/observability-shared-plugin/public'); jest.mock('../../utils/kibana_react'); @@ -33,16 +34,19 @@ const useGlobalDiagnosisMock = useFetchSloGlobalDiagnosis as jest.Mock; const mockNavigate = jest.fn(); +const mockObservabilityAIAssistant = observabilityAIAssistantPluginMock.createStartContract(); + const mockKibana = () => { useKibanaMock.mockReturnValue({ services: { - theme: {}, application: { navigateToUrl: mockNavigate }, + theme: {}, http: { basePath: { prepend: (url: string) => url, }, }, + observabilityAIAssistant: mockObservabilityAIAssistant, }, }); }; diff --git a/x-pack/plugins/observability/public/pages/slos_welcome/slos_welcome.tsx b/x-pack/plugins/observability/public/pages/slos_welcome/slos_welcome.tsx index 16f4a75974453..a27762d8553f9 100644 --- a/x-pack/plugins/observability/public/pages/slos_welcome/slos_welcome.tsx +++ b/x-pack/plugins/observability/public/pages/slos_welcome/slos_welcome.tsx @@ -27,6 +27,7 @@ import { paths } from '../../../common/locators/paths'; import illustration from './assets/illustration.svg'; import { useFetchSloGlobalDiagnosis } from '../../hooks/slo/use_fetch_global_diagnosis'; import { HeaderMenu } from '../overview/components/header_menu/header_menu'; +import { SloOutdatedCallout } from '../../components/slo/slo_outdated_callout'; export function SlosWelcomePage() { const { @@ -62,6 +63,7 @@ export function SlosWelcomePage() { return hasSlosAndHasPermissions || isLoading ? null : ( + diff --git a/x-pack/plugins/observability/public/plugin.ts b/x-pack/plugins/observability/public/plugin.ts index 12848e1bb4974..808573579cb99 100644 --- a/x-pack/plugins/observability/public/plugin.ts +++ b/x-pack/plugins/observability/public/plugin.ts @@ -11,6 +11,7 @@ import type { CloudStart } from '@kbn/cloud-plugin/public'; import type { IUiSettingsClient } from '@kbn/core/public'; import type { ContentManagementPublicStart } from '@kbn/content-management-plugin/public'; import { + App, AppDeepLink, AppMountParameters, AppNavLinkStatus, @@ -62,10 +63,11 @@ import { } from '@kbn/triggers-actions-ui-plugin/public'; import { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public'; import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public'; -import { ServerlessPluginStart } from '@kbn/serverless/public'; +import { ServerlessPluginSetup, ServerlessPluginStart } from '@kbn/serverless/public'; import type { UiActionsStart, UiActionsSetup } from '@kbn/ui-actions-plugin/public'; import { firstValueFrom } from 'rxjs'; +import type { PresentationUtilPluginStart } from '@kbn/presentation-util-plugin/public'; import { observabilityAppId, observabilityFeatureId } from '../common'; import { ALERTS_PATH, @@ -94,7 +96,7 @@ export interface ConfigSchema { metrics: { enabled: boolean; }; - logs: { + logs?: { enabled: boolean; }; uptime: { @@ -104,7 +106,7 @@ export interface ConfigSchema { enabled: boolean; }; }; - thresholdRule: { + thresholdRule?: { enabled: boolean; }; }; @@ -121,6 +123,8 @@ export interface ObservabilityPublicPluginsSetup { embeddable: EmbeddableSetup; uiActions: UiActionsSetup; licensing: LicensingPluginSetup; + serverless?: ServerlessPluginSetup; + presentationUtil?: PresentationUtilPluginStart; } export interface ObservabilityPublicPluginsStart { actionTypeRegistry: ActionTypeRegistryContract; @@ -151,6 +155,7 @@ export interface ObservabilityPublicPluginsStart { serverless?: ServerlessPluginStart; uiSettings: IUiSettingsClient; uiActions: UiActionsStart; + presentationUtil?: PresentationUtilPluginStart; } export type ObservabilityPublicStart = ReturnType; @@ -270,7 +275,8 @@ export class Plugin }; const appUpdater$ = this.appUpdater$; - const app = { + + const app: App = { appRoute: OBSERVABILITY_BASE_PATH, category, deepLinks: this.deepLinks, @@ -296,6 +302,7 @@ export class Plugin 'user', 'experience', ], + searchable: !Boolean(pluginsSetup.serverless), }; coreSetup.application.register(app); @@ -369,13 +376,17 @@ export class Plugin map((value) => { const deepLinks = value(app)?.deepLinks ?? []; - const overviewLink = { - label: i18n.translate('xpack.observability.overviewLinkTitle', { - defaultMessage: 'Overview', - }), - app: observabilityAppId, - path: OVERVIEW_PATH, - }; + const overviewLink = !Boolean(pluginsSetup.serverless) + ? [ + { + label: i18n.translate('xpack.observability.overviewLinkTitle', { + defaultMessage: 'Overview', + }), + app: observabilityAppId, + path: OVERVIEW_PATH, + }, + ] + : []; // Reformat the visible links to be NavigationEntry objects instead of // AppDeepLink objects. @@ -395,15 +406,13 @@ export class Plugin path: link.path ?? '', })); - const sections = [ + return [ { label: '', sortKey: 100, - entries: [overviewLink, ...otherLinks], + entries: [...overviewLink, ...otherLinks], }, ]; - - return sections; }) ) ); diff --git a/x-pack/plugins/observability/public/routes/routes.tsx b/x-pack/plugins/observability/public/routes/routes.tsx index 87fc22c094344..7bfd2c2b25e08 100644 --- a/x-pack/plugins/observability/public/routes/routes.tsx +++ b/x-pack/plugins/observability/public/routes/routes.tsx @@ -31,6 +31,7 @@ import { RULES_LOGS_PATH, RULES_PATH, RULE_DETAIL_PATH, + SLOS_OUTDATED_DEFINITIONS_PATH, SLOS_PATH, SLOS_WELCOME_PATH, SLO_CREATE_PATH, @@ -38,6 +39,7 @@ import { SLO_EDIT_PATH, } from '../../common/locators/paths'; import { HasDataContextProvider } from '../context/has_data_context/has_data_context'; +import { SlosOutdatedDefinitions } from '../pages/slo_outdated_definitions'; // Note: React Router DOM component was not working here // so I've recreated this simple version for this purpose. @@ -158,6 +160,13 @@ export const routes = { params: {}, exact: true, }, + [SLOS_OUTDATED_DEFINITIONS_PATH]: { + handler: () => { + return ; + }, + params: {}, + exact: true, + }, [SLO_EDIT_PATH]: { handler: () => { return ; 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 a1faec27f6c83..643d5ffd4f337 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 @@ -115,52 +115,49 @@ export const registerObservabilityRuleTypes = async ( priority: 100, }); - if (config.unsafe.thresholdRule.enabled) { - observabilityRuleTypeRegistry.register({ - id: OBSERVABILITY_THRESHOLD_RULE_TYPE_ID, - description: i18n.translate( - 'xpack.observability.customThreshold.rule.alertFlyout.alertDescription', - { - defaultMessage: - 'Alert when any Observability data type reaches or exceeds a given value.', - } - ), - iconClass: 'bell', - documentationUrl(docLinks) { - return `${docLinks.links.observability.customThreshold}`; - }, - ruleParamsExpression: lazy( - () => import('../components/custom_threshold/custom_threshold_rule_expression') - ), - validate: validateCustomThreshold, - defaultActionMessage: thresholdDefaultActionMessage, - defaultRecoveryMessage: thresholdDefaultRecoveryMessage, - requiresAppContext: false, - format: ({ fields }) => { - const searchConfiguration = fields[ALERT_RULE_PARAMETERS]?.searchConfiguration as - | SerializedSearchSourceFields - | undefined; - const criteria = fields[ALERT_RULE_PARAMETERS]?.criteria as MetricExpression[]; - const metrics: CustomThresholdExpressionMetric[] = - criteria.length === 1 ? criteria[0].metrics : []; - - const dataViewId = getDataViewId(searchConfiguration); - return { - reason: fields[ALERT_REASON] ?? '-', - link: getViewInAppUrl( - metrics, - fields[ALERT_START], - logExplorerLocator, - (searchConfiguration?.query as { query: string }).query, - dataViewId - ), - hasBasePath: true, - }; - }, - alertDetailsAppSection: lazy( - () => import('../components/custom_threshold/components/alert_details_app_section') - ), - priority: 5, - }); - } + observabilityRuleTypeRegistry.register({ + id: OBSERVABILITY_THRESHOLD_RULE_TYPE_ID, + description: i18n.translate( + 'xpack.observability.customThreshold.rule.alertFlyout.alertDescription', + { + defaultMessage: 'Alert when any Observability data type reaches or exceeds a given value.', + } + ), + iconClass: 'bell', + documentationUrl(docLinks) { + return `${docLinks.links.observability.customThreshold}`; + }, + ruleParamsExpression: lazy( + () => import('../components/custom_threshold/custom_threshold_rule_expression') + ), + validate: validateCustomThreshold, + defaultActionMessage: thresholdDefaultActionMessage, + defaultRecoveryMessage: thresholdDefaultRecoveryMessage, + requiresAppContext: false, + format: ({ fields }) => { + const searchConfiguration = fields[ALERT_RULE_PARAMETERS]?.searchConfiguration as + | SerializedSearchSourceFields + | undefined; + const criteria = fields[ALERT_RULE_PARAMETERS]?.criteria as MetricExpression[]; + const metrics: CustomThresholdExpressionMetric[] = + criteria.length === 1 ? criteria[0].metrics : []; + + const dataViewId = getDataViewId(searchConfiguration); + return { + reason: fields[ALERT_REASON] ?? '-', + link: getViewInAppUrl( + metrics, + fields[ALERT_START], + logExplorerLocator, + (searchConfiguration?.query as { query: string }).query, + dataViewId + ), + hasBasePath: true, + }; + }, + alertDetailsAppSection: lazy( + () => import('../components/custom_threshold/components/alert_details_app_section') + ), + priority: 5, + }); }; diff --git a/x-pack/plugins/observability/public/utils/format_alert_evaluation_value.test.ts b/x-pack/plugins/observability/public/utils/format_alert_evaluation_value.test.ts index ddef182541466..9aef93d6333a5 100644 --- a/x-pack/plugins/observability/public/utils/format_alert_evaluation_value.test.ts +++ b/x-pack/plugins/observability/public/utils/format_alert_evaluation_value.test.ts @@ -11,6 +11,13 @@ describe('formatAlertEvaluationValue', () => { it('returns - when there is no evaluationValue passed', () => { expect(formatAlertEvaluationValue('apm.transaction_error_rate', undefined)).toBe('-'); }); + it('returns - when there is null evaluationValue passed', () => { + // @ts-expect-error + expect(formatAlertEvaluationValue('apm.transaction_error_rate', null)).toBe('-'); + }); + it('returns the evaluation value when the value is 0', () => { + expect(formatAlertEvaluationValue('.es-query', 0)).toBe(0); + }); it('returns the evaluation value when ruleTypeId in unknown aka unformatted', () => { expect(formatAlertEvaluationValue('unknown.rule.type', 2000)).toBe(2000); }); diff --git a/x-pack/plugins/observability/public/utils/format_alert_evaluation_value.ts b/x-pack/plugins/observability/public/utils/format_alert_evaluation_value.ts index 92629934f2cea..7d0c5664568ec 100644 --- a/x-pack/plugins/observability/public/utils/format_alert_evaluation_value.ts +++ b/x-pack/plugins/observability/public/utils/format_alert_evaluation_value.ts @@ -12,7 +12,7 @@ import { } from './get_alert_evaluation_unit_type_by_rule_type_id'; export const formatAlertEvaluationValue = (ruleTypeId?: string, evaluationValue?: number) => { - if (!evaluationValue || !ruleTypeId) return '-'; + if (null == evaluationValue || !ruleTypeId) return '-'; const unitType = getAlertEvaluationUnitTypeByRuleTypeId(ruleTypeId); switch (unitType) { case ALERT_EVALUATION_UNIT_TYPE.DURATION: diff --git a/x-pack/plugins/observability/public/utils/is_alert_details_enabled.test.ts b/x-pack/plugins/observability/public/utils/is_alert_details_enabled.test.ts index a39c4cd00b576..101e10a314451 100644 --- a/x-pack/plugins/observability/public/utils/is_alert_details_enabled.test.ts +++ b/x-pack/plugins/observability/public/utils/is_alert_details_enabled.test.ts @@ -31,7 +31,6 @@ import type { TopAlert } from '../typings/alerts'; const defaultConfig = { unsafe: { alertDetails: { - logs: { enabled: false }, metrics: { enabled: false }, uptime: { enabled: false }, }, @@ -63,15 +62,10 @@ describe('isAlertDetailsEnabled', () => { start: 1630587249674, lastUpdated: 1630588131750, } as unknown as TopAlert; - it('returns FALSE when logs: { enabled: false }', () => { - expect(isAlertDetailsEnabledPerApp(logsAlert, defaultConfig)).toBeFalsy(); - }); - - it('returns TRUE when logs: { enabled: true }', () => { + it('returns TRUE when rule type is logs.alert.document.count', () => { const updatedConfig = { unsafe: { alertDetails: { - logs: { enabled: true }, metrics: { enabled: false }, uptime: { enabled: false }, }, @@ -113,7 +107,6 @@ describe('isAlertDetailsEnabled', () => { const updatedConfig = { unsafe: { alertDetails: { - logs: { enabled: false }, metrics: { enabled: false }, uptime: { enabled: false }, }, @@ -159,7 +152,6 @@ describe('isAlertDetailsEnabled', () => { const updatedConfig = { unsafe: { alertDetails: { - logs: { enabled: false }, metrics: { enabled: true }, uptime: { enabled: false }, }, @@ -201,7 +193,6 @@ describe('isAlertDetailsEnabled', () => { const updatedConfig = { unsafe: { alertDetails: { - logs: { enabled: false }, metrics: { enabled: false }, uptime: { enabled: true }, }, @@ -243,7 +234,6 @@ describe('isAlertDetailsEnabled', () => { const updatedConfig = { unsafe: { alertDetails: { - logs: { enabled: true }, metrics: { enabled: true }, uptime: { enabled: true }, }, @@ -255,7 +245,6 @@ describe('isAlertDetailsEnabled', () => { const updatedConfig = { unsafe: { alertDetails: { - logs: { enabled: true }, metrics: { enabled: true }, uptime: { enabled: true }, }, diff --git a/x-pack/plugins/observability/public/utils/is_alert_details_enabled.ts b/x-pack/plugins/observability/public/utils/is_alert_details_enabled.ts index 15b9589be60f6..2b628f26ee794 100644 --- a/x-pack/plugins/observability/public/utils/is_alert_details_enabled.ts +++ b/x-pack/plugins/observability/public/utils/is_alert_details_enabled.ts @@ -9,12 +9,12 @@ import { ALERT_RULE_TYPE_ID } from '@kbn/rule-data-utils'; import type { ConfigSchema } from '../plugin'; import type { TopAlert } from '../typings/alerts'; -const ALLOWED_RULE_TYPES = ['apm.transaction_duration']; +const ALLOWED_RULE_TYPES = ['apm.transaction_duration', 'logs.alert.document.count']; const isUnsafeAlertDetailsFlag = ( subject: string -): subject is keyof ConfigSchema['unsafe']['alertDetails'] => - ['uptime', 'logs', 'metrics', 'observability'].includes(subject); +): subject is keyof Omit => + ['uptime', 'metrics', 'observability'].includes(subject); // We are mapping the ruleTypeId from the feature flag with the ruleTypeId from the alert // to know whether the feature flag is enabled or not. diff --git a/x-pack/plugins/observability/public/utils/kibana_react.storybook_decorator.tsx b/x-pack/plugins/observability/public/utils/kibana_react.storybook_decorator.tsx index d5714924bdc97..3794fa1e269c6 100644 --- a/x-pack/plugins/observability/public/utils/kibana_react.storybook_decorator.tsx +++ b/x-pack/plugins/observability/public/utils/kibana_react.storybook_decorator.tsx @@ -28,12 +28,10 @@ export function KibanaReactStorybookDecorator(Story: ComponentType) { const config: ConfigSchema = { unsafe: { alertDetails: { - logs: { enabled: false }, metrics: { enabled: false }, uptime: { enabled: false }, observability: { enabled: false }, }, - thresholdRule: { enabled: false }, }, }; @@ -68,7 +66,6 @@ export function KibanaReactStorybookDecorator(Story: ComponentType) { charts: { theme: { useChartsBaseTheme: () => {}, - useChartsTheme: () => {}, }, activeCursor: () => {}, }, diff --git a/x-pack/plugins/observability/public/utils/test_helper.tsx b/x-pack/plugins/observability/public/utils/test_helper.tsx index 6c1610ac059c9..61fb7bbade53f 100644 --- a/x-pack/plugins/observability/public/utils/test_helper.tsx +++ b/x-pack/plugins/observability/public/utils/test_helper.tsx @@ -32,12 +32,10 @@ export const data = dataPluginMock.createStartContract(); const defaultConfig: ConfigSchema = { unsafe: { alertDetails: { - logs: { enabled: false }, metrics: { enabled: false }, uptime: { enabled: false }, observability: { enabled: false }, }, - thresholdRule: { enabled: false }, }, }; @@ -64,6 +62,7 @@ export const render = (component: React.ReactNode, config: Subset exploratoryView: { createExploratoryViewUrl: jest.fn(), getAppDataView: jest.fn(), + // eslint-disable-next-line @kbn/i18n/strings_should_be_translated_with_i18n ExploratoryViewEmbeddable: () =>
Embeddable exploratory view
, }, }} diff --git a/x-pack/plugins/observability/server/assets/component_templates/slo_mappings_template.ts b/x-pack/plugins/observability/server/assets/component_templates/slo_mappings_template.ts index b4aef3c39a80d..5e1778596ce16 100644 --- a/x-pack/plugins/observability/server/assets/component_templates/slo_mappings_template.ts +++ b/x-pack/plugins/observability/server/assets/component_templates/slo_mappings_template.ts @@ -12,6 +12,14 @@ export const getSLOMappingsTemplate = (name: string) => ({ template: { mappings: { properties: { + event: { + properties: { + ingested: { + type: 'date', + format: 'strict_date_optional_time', + }, + }, + }, '@timestamp': { type: 'date', format: 'date_optional_time||epoch_millis', @@ -21,11 +29,9 @@ export const getSLOMappingsTemplate = (name: string) => ({ properties: { name: { type: 'keyword', - ignore_above: 256, }, environment: { type: 'keyword', - ignore_above: 256, }, }, }, @@ -33,11 +39,9 @@ export const getSLOMappingsTemplate = (name: string) => ({ properties: { name: { type: 'keyword', - ignore_above: 256, }, type: { type: 'keyword', - ignore_above: 256, }, }, }, @@ -50,56 +54,8 @@ export const getSLOMappingsTemplate = (name: string) => ({ revision: { type: 'long', }, - groupBy: { - type: 'keyword', - ignore_above: 256, - }, instanceId: { type: 'keyword', - ignore_above: 256, - }, - name: { - type: 'keyword', - ignore_above: 256, - }, - description: { - type: 'keyword', - ignore_above: 256, - }, - tags: { - type: 'keyword', - ignore_above: 256, - }, - indicator: { - properties: { - type: { - type: 'keyword', - ignore_above: 256, - }, - }, - }, - objective: { - properties: { - target: { - type: 'double', - }, - sliceDurationInSeconds: { - type: 'long', - }, - }, - }, - budgetingMethod: { - type: 'keyword', - }, - timeWindow: { - properties: { - duration: { - type: 'keyword', - }, - type: { - type: 'keyword', - }, - }, }, numerator: { type: 'long', @@ -110,6 +66,9 @@ export const getSLOMappingsTemplate = (name: string) => ({ isGoodSlice: { type: 'byte', }, + groupings: { + type: 'flattened', + }, }, }, }, diff --git a/x-pack/plugins/observability/server/assets/component_templates/slo_summary_mappings_template.ts b/x-pack/plugins/observability/server/assets/component_templates/slo_summary_mappings_template.ts index 9641b64f5f1d0..c75eb8586334a 100644 --- a/x-pack/plugins/observability/server/assets/component_templates/slo_summary_mappings_template.ts +++ b/x-pack/plugins/observability/server/assets/component_templates/slo_summary_mappings_template.ts @@ -17,11 +17,9 @@ export const getSLOSummaryMappingsTemplate = (name: string) => ({ properties: { name: { type: 'keyword', - ignore_above: 256, }, environment: { type: 'keyword', - ignore_above: 256, }, }, }, @@ -29,11 +27,9 @@ export const getSLOSummaryMappingsTemplate = (name: string) => ({ properties: { name: { type: 'keyword', - ignore_above: 256, }, type: { type: 'keyword', - ignore_above: 256, }, }, }, @@ -48,29 +44,49 @@ export const getSLOSummaryMappingsTemplate = (name: string) => ({ }, groupBy: { type: 'keyword', - ignore_above: 256, + }, + groupings: { + type: 'flattened', }, instanceId: { type: 'keyword', - ignore_above: 256, + fields: { + text: { + type: 'text', + }, + }, }, name: { - type: 'keyword', - ignore_above: 256, + type: 'text', + fields: { + keyword: { + type: 'keyword', + }, + }, }, description: { - type: 'keyword', - ignore_above: 256, + type: 'text', }, tags: { type: 'keyword', - ignore_above: 256, }, indicator: { properties: { type: { type: 'keyword', - ignore_above: 256, + }, + }, + }, + objective: { + properties: { + target: { + type: 'double', + }, + timesliceTarget: { + type: 'double', + }, + timesliceWindow: { + type: 'keyword', }, }, }, @@ -115,11 +131,21 @@ export const getSLOSummaryMappingsTemplate = (name: string) => ({ }, status: { type: 'keyword', - ignore_above: 32, }, isTempDoc: { type: 'boolean', }, + latestSliTimestamp: { + type: 'date', + format: 'date_optional_time||epoch_millis', + }, + summaryUpdatedAt: { + type: 'date', + format: 'date_optional_time||epoch_millis', + }, + spaceId: { + type: 'keyword', + }, }, }, }, diff --git a/x-pack/plugins/observability/server/assets/ingest_templates/slo_pipeline_template.ts b/x-pack/plugins/observability/server/assets/ingest_templates/slo_pipeline_template.ts index 30724989925d7..6a3a6684ab191 100644 --- a/x-pack/plugins/observability/server/assets/ingest_templates/slo_pipeline_template.ts +++ b/x-pack/plugins/observability/server/assets/ingest_templates/slo_pipeline_template.ts @@ -9,18 +9,25 @@ import { SLO_RESOURCES_VERSION } from '../../../common/slo/constants'; export const getSLOPipelineTemplate = (id: string, indexNamePrefix: string) => ({ id, - description: 'Monthly date-time index naming for SLO data', + description: 'Ingest pipeline for SLO rollup data', processors: [ + { + set: { + field: 'event.ingested', + value: '{{{_ingest.timestamp}}}', + }, + }, { date_index_name: { field: '@timestamp', index_name_prefix: indexNamePrefix, date_rounding: 'M', + date_formats: ['UNIX_MS', 'ISO8601', "yyyy-MM-dd'T'HH:mm:ss.SSSXX"], }, }, ], _meta: { - description: 'SLO ingest pipeline', + description: 'Ingest pipeline for SLO rollup data', version: SLO_RESOURCES_VERSION, managed: true, managed_by: 'observability', diff --git a/x-pack/plugins/observability/server/assets/ingest_templates/slo_summary_pipeline_template.ts b/x-pack/plugins/observability/server/assets/ingest_templates/slo_summary_pipeline_template.ts index 8246504ac216c..d279c925f86bf 100644 --- a/x-pack/plugins/observability/server/assets/ingest_templates/slo_summary_pipeline_template.ts +++ b/x-pack/plugins/observability/server/assets/ingest_templates/slo_summary_pipeline_template.ts @@ -5,56 +5,167 @@ * 2.0. */ -import { SLO_RESOURCES_VERSION } from '../../../common/slo/constants'; +import { timeslicesBudgetingMethodSchema } from '@kbn/slo-schema'; +import { getSLOSummaryPipelineId, SLO_RESOURCES_VERSION } from '../../../common/slo/constants'; +import { SLO } from '../../domain/models'; -export const getSLOSummaryPipelineTemplate = (id: string) => ({ - id, - description: 'SLO summary ingest pipeline', - processors: [ - { - split: { - description: 'Split comma separated list of tags into an array', - field: 'slo.tags', - separator: ',', +export const getSLOSummaryPipelineTemplate = (slo: SLO, spaceId: string) => { + const errorBudgetEstimated = + slo.budgetingMethod === 'occurrences' && slo.timeWindow.type === 'calendarAligned'; + + const optionalObjectiveTimesliceProcessors = timeslicesBudgetingMethodSchema.is( + slo.budgetingMethod + ) + ? [ + { + set: { + description: 'Set objective.timesliceTarget field', + field: 'slo.objective.timesliceTarget', + value: slo.objective.timesliceTarget, + }, + }, + { + set: { + description: 'Set objective.timesliceWindow field', + field: 'slo.objective.timesliceWindow', + value: slo.objective.timesliceWindow!.format(), + }, + }, + ] + : []; + + return { + id: getSLOSummaryPipelineId(slo.id, slo.revision), + description: `Ingest pipeline for SLO summary data [id: ${slo.id}, revision: ${slo.revision}]`, + processors: [ + { + set: { + description: 'Set errorBudgetEstimated field', + field: 'errorBudgetEstimated', + value: errorBudgetEstimated, + }, }, - }, - { - set: { - description: "if 'statusCode == 0', set status to NO_DATA", - if: 'ctx.statusCode == 0', - field: 'status', - value: 'NO_DATA', + { + set: { + description: 'Set isTempDoc field', + field: 'isTempDoc', + value: false, + }, }, - }, - { - set: { - description: "if 'statusCode == 1', set statusLabel to VIOLATED", - if: 'ctx.statusCode == 1', - field: 'status', - value: 'VIOLATED', + { + set: { + description: 'Set groupBy field', + field: 'slo.groupBy', + value: slo.groupBy, + }, }, - }, - { - set: { - description: "if 'statusCode == 2', set status to DEGRADING", - if: 'ctx.statusCode == 2', - field: 'status', - value: 'DEGRADING', + { + set: { + description: 'Set name field', + field: 'slo.name', + value: slo.name, + }, }, - }, - { - set: { - description: "if 'statusCode == 4', set status to HEALTHY", - if: 'ctx.statusCode == 4', - field: 'status', - value: 'HEALTHY', + { + set: { + description: 'Set description field', + field: 'slo.description', + value: slo.description, + }, + }, + { + set: { + description: 'Set tags field', + field: 'slo.tags', + value: slo.tags, + }, + }, + { + set: { + description: 'Set indicator.type field', + field: 'slo.indicator.type', + value: slo.indicator.type, + }, + }, + { + set: { + description: 'Set budgetingMethod field', + field: 'slo.budgetingMethod', + value: slo.budgetingMethod, + }, + }, + { + set: { + description: 'Set timeWindow.duration field', + field: 'slo.timeWindow.duration', + value: slo.timeWindow.duration.format(), + }, + }, + { + set: { + description: 'Set timeWindow.type field', + field: 'slo.timeWindow.type', + value: slo.timeWindow.type, + }, + }, + { + set: { + description: 'Set objective.target field', + field: 'slo.objective.target', + value: slo.objective.target, + }, + }, + ...optionalObjectiveTimesliceProcessors, + { + set: { + description: "if 'statusCode == 0', set status to NO_DATA", + if: 'ctx.statusCode == 0', + field: 'status', + value: 'NO_DATA', + }, + }, + { + set: { + description: "if 'statusCode == 1', set statusLabel to VIOLATED", + if: 'ctx.statusCode == 1', + field: 'status', + value: 'VIOLATED', + }, + }, + { + set: { + description: "if 'statusCode == 2', set status to DEGRADING", + if: 'ctx.statusCode == 2', + field: 'status', + value: 'DEGRADING', + }, + }, + { + set: { + description: "if 'statusCode == 4', set status to HEALTHY", + if: 'ctx.statusCode == 4', + field: 'status', + value: 'HEALTHY', + }, + }, + { + set: { + field: 'summaryUpdatedAt', + value: '{{{_ingest.timestamp}}}', + }, + }, + { + set: { + field: 'spaceId', + value: spaceId, + }, }, + ], + _meta: { + description: `Ingest pipeline for SLO summary data [id: ${slo.id}, revision: ${slo.revision}]`, + version: SLO_RESOURCES_VERSION, + managed: true, + managed_by: 'observability', }, - ], - _meta: { - description: 'SLO summary ingest pipeline', - version: SLO_RESOURCES_VERSION, - managed: true, - managed_by: 'observability', - }, -}); + }; +}; diff --git a/x-pack/plugins/observability/server/index.ts b/x-pack/plugins/observability/server/index.ts index ce2279db23203..3d9e87ccadf00 100644 --- a/x-pack/plugins/observability/server/index.ts +++ b/x-pack/plugins/observability/server/index.ts @@ -36,8 +36,7 @@ const configSchema = schema.object({ enabled: schema.boolean({ defaultValue: false }), }), logs: schema.object({ - // Enable it by default: https://github.com/elastic/kibana/issues/159945 - enabled: schema.boolean({ defaultValue: true }), + enabled: schema.boolean({ defaultValue: false }), }), uptime: schema.object({ enabled: schema.boolean({ defaultValue: false }), @@ -48,8 +47,8 @@ const configSchema = schema.object({ }), thresholdRule: schema.object({ enabled: offeringBasedSchema({ - serverless: schema.boolean({ defaultValue: true }), - traditional: schema.boolean({ defaultValue: true }), + serverless: schema.boolean({ defaultValue: false }), + traditional: schema.boolean({ defaultValue: false }), }), }), }), @@ -58,6 +57,7 @@ const configSchema = schema.object({ }), enabled: schema.boolean({ defaultValue: true }), createO11yGenericFeatureId: schema.boolean({ defaultValue: false }), + sloOrphanSummaryCleanUpTaskEnabled: schema.boolean({ defaultValue: true }), }); export const config: PluginConfigDescriptor = { @@ -71,6 +71,10 @@ export const config: PluginConfigDescriptor = { }, }, schema: configSchema, + deprecations: ({ unused }) => [ + unused('unsafe.thresholdRule.enabled', { level: 'warning' }), + unused('unsafe.alertDetails.logs.enabled', { level: 'warning' }), + ], }; export type ObservabilityConfig = TypeOf; diff --git a/x-pack/plugins/observability/server/lib/rules/custom_threshold/constants.ts b/x-pack/plugins/observability/server/lib/rules/custom_threshold/constants.ts index 084c039609899..f2f24232efe34 100644 --- a/x-pack/plugins/observability/server/lib/rules/custom_threshold/constants.ts +++ b/x-pack/plugins/observability/server/lib/rules/custom_threshold/constants.ts @@ -28,3 +28,12 @@ export const NO_DATA_ACTION = { } ), }; + +export const CUSTOM_THRESHOLD_AAD_FIELDS = [ + 'cloud.*', + 'host.*', + 'orchestrator.*', + 'container.*', + 'labels.*', + 'tags', +]; diff --git a/x-pack/plugins/observability/server/lib/rules/custom_threshold/custom_threshold_executor.ts b/x-pack/plugins/observability/server/lib/rules/custom_threshold/custom_threshold_executor.ts index ac052a6b8f4d3..2aa9ee09a073d 100644 --- a/x-pack/plugins/observability/server/lib/rules/custom_threshold/custom_threshold_executor.ts +++ b/x-pack/plugins/observability/server/lib/rules/custom_threshold/custom_threshold_executor.ts @@ -99,6 +99,7 @@ export const createCustomThresholdExecutor = ({ getAlertByAlertUuid, getAlertStartedDate, searchSourceClient, + alertFactory: baseAlertFactory, } = services; const alertFactory: CustomThresholdAlertFactory = ( @@ -177,8 +178,17 @@ export const createCustomThresholdExecutor = ({ const hasGroups = !isEqual(groups, [UNGROUPED_FACTORY_KEY]); let scheduledActionsCount = 0; + const alertLimit = baseAlertFactory.alertLimit.getValue(); + let hasReachedLimit = false; + // The key of `groups` is the alert instance ID. for (const group of groups) { + if (scheduledActionsCount >= alertLimit) { + // need to set this so that warning is displayed in the UI and in the logs + hasReachedLimit = true; + break; // once limit is reached, we break out of the loop and don't schedule any more alerts + } + // AND logic; all criteria must be across the threshold const shouldAlertFire = alertResults.every((result) => result[group]?.shouldFire); // AND logic; because we need to evaluate all criteria, if one of them reports no data then the @@ -296,6 +306,8 @@ export const createCustomThresholdExecutor = ({ }); } } + + baseAlertFactory.alertLimit.setLimitReached(hasReachedLimit); const { getRecoveredAlerts } = services.alertFactory.done(); const recoveredAlerts = getRecoveredAlerts(); 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 cf27867125cad..56dc9fd721e02 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 @@ -40,7 +40,7 @@ import { createCustomThresholdExecutor, CustomThresholdLocators, } from './custom_threshold_executor'; -import { FIRED_ACTION, NO_DATA_ACTION } from './constants'; +import { CUSTOM_THRESHOLD_AAD_FIELDS, FIRED_ACTION, NO_DATA_ACTION } from './constants'; import { ObservabilityConfig } from '../../..'; export const MetricsRulesTypeAlertDefinition: IRuleTypeAlerts = { @@ -110,6 +110,7 @@ export function thresholdRuleType( name: i18n.translate('xpack.observability.threshold.ruleName', { defaultMessage: 'Custom threshold (Beta)', }), + fieldsForAAD: CUSTOM_THRESHOLD_AAD_FIELDS, validate: { params: schema.object( { diff --git a/x-pack/plugins/observability/server/lib/rules/register_rule_types.ts b/x-pack/plugins/observability/server/lib/rules/register_rule_types.ts index c90ee35f86552..f705cf6416459 100644 --- a/x-pack/plugins/observability/server/lib/rules/register_rule_types.ts +++ b/x-pack/plugins/observability/server/lib/rules/register_rule_types.ts @@ -58,35 +58,32 @@ export function registerRuleTypes( sloBurnRateRuleType(createLifecycleRuleExecutorSLO, basePath, locators.alertsLocator) ); - // Threshold RULE - if (config.unsafe.thresholdRule.enabled) { - const ruleDataClientThreshold = ruleDataService.initializeIndex({ - feature: observabilityFeatureId, - registrationContext: THRESHOLD_RULE_REGISTRATION_CONTEXT, - dataset: Dataset.alerts, - componentTemplateRefs: [], - componentTemplates: [ - { - name: 'mappings', - mappings: mappingFromFieldMap({ ...legacyExperimentalFieldMap }, 'strict'), - }, - ], - }); + const ruleDataClientThreshold = ruleDataService.initializeIndex({ + feature: observabilityFeatureId, + registrationContext: THRESHOLD_RULE_REGISTRATION_CONTEXT, + dataset: Dataset.alerts, + componentTemplateRefs: [], + componentTemplates: [ + { + name: 'mappings', + mappings: mappingFromFieldMap({ ...legacyExperimentalFieldMap }, 'strict'), + }, + ], + }); - const createLifecycleRuleExecutorThreshold = createLifecycleExecutor( - logger.get('rules'), - ruleDataClientThreshold - ); + const createLifecycleRuleExecutorThreshold = createLifecycleExecutor( + logger.get('rules'), + ruleDataClientThreshold + ); - alertingPlugin.registerType( - thresholdRuleType( - createLifecycleRuleExecutorThreshold, - basePath, - config, - logger, - ruleDataClientThreshold, - locators - ) - ); - } + alertingPlugin.registerType( + thresholdRuleType( + createLifecycleRuleExecutorThreshold, + basePath, + config, + logger, + ruleDataClientThreshold, + locators + ) + ); } diff --git a/x-pack/plugins/observability/server/lib/rules/slo_burn_rate/executor.ts b/x-pack/plugins/observability/server/lib/rules/slo_burn_rate/executor.ts index 5c7c7c323a300..e1891e68af58b 100644 --- a/x-pack/plugins/observability/server/lib/rules/slo_burn_rate/executor.ts +++ b/x-pack/plugins/observability/server/lib/rules/slo_burn_rate/executor.ts @@ -95,6 +95,9 @@ export const getRuleExecutor = ({ const results = await evaluate(esClient.asCurrentUser, slo, params, new Date(dateEnd)); if (results.length > 0) { + const alertLimit = alertFactory.alertLimit.getValue(); + let hasReachedLimit = false; + let scheduledActionsCount = 0; for (const result of results) { const { instanceId, @@ -113,6 +116,11 @@ export const getRuleExecutor = ({ `/app/observability/slos/${slo.id}${urlQuery}` ); if (shouldAlert) { + if (scheduledActionsCount >= alertLimit) { + // need to set this so that warning is displayed in the UI and in the logs + hasReachedLimit = true; + break; // once limit is reached, we break out of the loop and don't schedule any more alerts + } const reason = buildReason( instanceId, windowDef.actionGroup, @@ -160,41 +168,43 @@ export const getRuleExecutor = ({ alert.scheduleActions(windowDef.actionGroup, context); alert.replaceState({ alertState: AlertStates.ALERT }); + scheduledActionsCount++; } } + alertFactory.alertLimit.setLimitReached(hasReachedLimit); + } - const { getRecoveredAlerts } = alertFactory.done(); - const recoveredAlerts = getRecoveredAlerts(); - for (const recoveredAlert of recoveredAlerts) { - const alertId = recoveredAlert.getId(); - const indexedStartedAt = getAlertStartedDate(alertId) ?? startedAt.toISOString(); - const alertUuid = recoveredAlert.getUuid(); - const alertDetailsUrl = await getAlertUrl( - alertUuid, - spaceId, - indexedStartedAt, - alertsLocator, - basePath.publicBaseUrl - ); + const { getRecoveredAlerts } = alertFactory.done(); + const recoveredAlerts = getRecoveredAlerts(); + for (const recoveredAlert of recoveredAlerts) { + const alertId = recoveredAlert.getId(); + const indexedStartedAt = getAlertStartedDate(alertId) ?? startedAt.toISOString(); + const alertUuid = recoveredAlert.getUuid(); + const alertDetailsUrl = await getAlertUrl( + alertUuid, + spaceId, + indexedStartedAt, + alertsLocator, + basePath.publicBaseUrl + ); - const urlQuery = alertId === ALL_VALUE ? '' : `?instanceId=${alertId}`; - const viewInAppUrl = addSpaceIdToPath( - basePath.publicBaseUrl, - spaceId, - `/app/observability/slos/${slo.id}${urlQuery}` - ); + const urlQuery = alertId === ALL_VALUE ? '' : `?instanceId=${alertId}`; + const viewInAppUrl = addSpaceIdToPath( + basePath.publicBaseUrl, + spaceId, + `/app/observability/slos/${slo.id}${urlQuery}` + ); - const context = { - timestamp: startedAt.toISOString(), - viewInAppUrl, - alertDetailsUrl, - sloId: slo.id, - sloName: slo.name, - sloInstanceId: alertId, - }; + const context = { + timestamp: startedAt.toISOString(), + viewInAppUrl, + alertDetailsUrl, + sloId: slo.id, + sloName: slo.name, + sloInstanceId: alertId, + }; - recoveredAlert.setContext(context); - } + recoveredAlert.setContext(context); } return { state: {} }; diff --git a/x-pack/plugins/observability/server/lib/rules/slo_burn_rate/register.ts b/x-pack/plugins/observability/server/lib/rules/slo_burn_rate/register.ts index c454df48e485c..40873338d5e9f 100644 --- a/x-pack/plugins/observability/server/lib/rules/slo_burn_rate/register.ts +++ b/x-pack/plugins/observability/server/lib/rules/slo_burn_rate/register.ts @@ -14,6 +14,7 @@ import { createLifecycleExecutor } from '@kbn/rule-registry-plugin/server'; import { legacyExperimentalFieldMap } from '@kbn/alerts-as-data-utils'; import { IBasePath } from '@kbn/core/server'; import { LocatorPublic } from '@kbn/share-plugin/common'; +import { SLO_BURN_RATE_AAD_FIELDS } from '../../../../common/field_names/slo'; import { AlertsLocatorParams, observabilityPaths, sloFeatureId } from '../../../../common'; import { SLO_RULE_REGISTRATION_CONTEXT } from '../../../common/constants'; @@ -54,6 +55,7 @@ export function sloBurnRateRuleType( name: i18n.translate('xpack.observability.slo.rules.burnRate.name', { defaultMessage: 'SLO burn rate', }), + fieldsForAAD: SLO_BURN_RATE_AAD_FIELDS, validate: { params: schema.object({ sloId: schema.string(), diff --git a/x-pack/plugins/observability/server/plugin.ts b/x-pack/plugins/observability/server/plugin.ts index 6726b8abfe178..791ddc8108312 100644 --- a/x-pack/plugins/observability/server/plugin.ts +++ b/x-pack/plugins/observability/server/plugin.ts @@ -13,26 +13,33 @@ import { import { CloudSetup } from '@kbn/cloud-plugin/server'; import { CoreSetup, + CoreStart, DEFAULT_APP_CATEGORIES, Logger, Plugin, PluginInitializerContext, + SavedObjectsClient, } from '@kbn/core/server'; -import { LOG_EXPLORER_LOCATOR_ID, LogExplorerLocatorParams } from '@kbn/deeplinks-observability'; +import { LogExplorerLocatorParams, LOG_EXPLORER_LOCATOR_ID } from '@kbn/deeplinks-observability'; import { PluginSetupContract as FeaturesSetup } from '@kbn/features-plugin/server'; import { hiddenTypes as filesSavedObjectTypes } from '@kbn/files-plugin/server/saved_objects'; import type { GuidedOnboardingPluginSetup } from '@kbn/guided-onboarding-plugin/server'; import { i18n } from '@kbn/i18n'; -import { RuleRegistryPluginSetupContract } from '@kbn/rule-registry-plugin/server'; -import { SharePluginSetup } from '@kbn/share-plugin/server'; -import { SpacesPluginSetup } from '@kbn/spaces-plugin/server'; -import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server'; import { ApmRuleType, ES_QUERY_ID, METRIC_INVENTORY_THRESHOLD_ALERT_TYPE_ID, OBSERVABILITY_THRESHOLD_RULE_TYPE_ID, } from '@kbn/rule-data-utils'; +import { + TaskManagerSetupContract, + TaskManagerStartContract, +} from '@kbn/task-manager-plugin/server'; +import { RuleRegistryPluginSetupContract } from '@kbn/rule-registry-plugin/server'; +import { SharePluginSetup } from '@kbn/share-plugin/server'; +import { SpacesPluginSetup, SpacesPluginStart } from '@kbn/spaces-plugin/server'; +import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/server'; +import { SloOrphanSummaryCleanupTask } from './services/slo/tasks/orphan_summary_cleanup_task'; import { ObservabilityConfig } from '.'; import { casesFeatureId, observabilityFeatureId, sloFeatureId } from '../common'; import { SLO_BURN_RATE_RULE_TYPE_ID } from '../common/constants'; @@ -52,11 +59,7 @@ import { getObservabilityServerRouteRepository } from './routes/get_global_obser import { registerRoutes } from './routes/register_routes'; import { slo, SO_SLO_TYPE } from './saved_objects'; import { threshold } from './saved_objects/threshold'; -import { - DefaultResourceInstaller, - DefaultSLOInstaller, - DefaultSummaryTransformInstaller, -} from './services/slo'; +import { DefaultResourceInstaller, DefaultSLOInstaller } from './services/slo'; import { uiSettings } from './ui_settings'; @@ -71,10 +74,13 @@ interface PluginSetup { spaces?: SpacesPluginSetup; usageCollection?: UsageCollectionSetup; cloud?: CloudSetup; + taskManager: TaskManagerSetupContract; } interface PluginStart { alerting: PluginStartContract; + taskManager: TaskManagerStartContract; + spaces?: SpacesPluginStart; } const sloRuleTypes = [SLO_BURN_RATE_RULE_TYPE_ID]; @@ -89,6 +95,7 @@ const o11yRuleTypes = [ export class ObservabilityPlugin implements Plugin { private logger: Logger; + private sloOrphanCleanupTask?: SloOrphanSummaryCleanupTask; constructor(private readonly initContext: PluginInitializerContext) { this.initContext = initContext; @@ -350,6 +357,7 @@ export class ObservabilityPlugin implements Plugin { ...plugins, core, }, + spaces: pluginStart.spaces, ruleDataService, getRulesClientWithRequest: pluginStart.alerting.getRulesClientWithRequest, }, @@ -360,23 +368,20 @@ export class ObservabilityPlugin implements Plugin { const esInternalClient = coreStart.elasticsearch.client.asInternalUser; const sloResourceInstaller = new DefaultResourceInstaller(esInternalClient, this.logger); - const sloSummaryInstaller = new DefaultSummaryTransformInstaller( - esInternalClient, - this.logger - ); - const sloInstaller = new DefaultSLOInstaller( - sloResourceInstaller, - sloSummaryInstaller, - this.logger - ); + const sloInstaller = new DefaultSLOInstaller(sloResourceInstaller, this.logger); sloInstaller.install(); }); - /** * Register a config for the observability guide */ plugins.guidedOnboarding?.registerGuideConfig(kubernetesGuideId, kubernetesGuideConfig); + this.sloOrphanCleanupTask = new SloOrphanSummaryCleanupTask( + plugins.taskManager, + this.logger, + config + ); + return { getAlertDetailsConfig() { return config.unsafe.alertDetails; @@ -389,7 +394,12 @@ export class ObservabilityPlugin implements Plugin { }; } - public start() {} + public start(core: CoreStart, plugins: PluginStart) { + const internalSoClient = new SavedObjectsClient(core.savedObjects.createInternalRepository()); + const internalEsClient = core.elasticsearch.client.asInternalUser; + + this.sloOrphanCleanupTask?.start(plugins.taskManager, internalSoClient, internalEsClient); + } public stop() {} } diff --git a/x-pack/plugins/observability/server/routes/register_routes.ts b/x-pack/plugins/observability/server/routes/register_routes.ts index 7726e54793d32..92980f20c4646 100644 --- a/x-pack/plugins/observability/server/routes/register_routes.ts +++ b/x-pack/plugins/observability/server/routes/register_routes.ts @@ -14,6 +14,7 @@ import { parseEndpoint, routeValidationObject, } from '@kbn/server-route-repository'; +import { SpacesPluginStart } from '@kbn/spaces-plugin/server'; import axios from 'axios'; import * as t from 'io-ts'; import { ObservabilityConfig } from '..'; @@ -33,6 +34,7 @@ export interface RegisterRoutesDependencies { pluginsSetup: { core: CoreSetup; }; + spaces?: SpacesPluginStart; ruleDataService: RuleDataPluginService; getRulesClientWithRequest: (request: KibanaRequest) => RulesClientApi; } diff --git a/x-pack/plugins/observability/server/routes/slo/route.ts b/x-pack/plugins/observability/server/routes/slo/route.ts index ade3f1714ddfb..7ad4b7c36dcc7 100644 --- a/x-pack/plugins/observability/server/routes/slo/route.ts +++ b/x-pack/plugins/observability/server/routes/slo/route.ts @@ -19,12 +19,14 @@ import { getSLOInstancesParamsSchema, getSLOParamsSchema, manageSLOParamsSchema, + resetSLOParamsSchema, updateSLOParamsSchema, } from '@kbn/slo-schema'; import type { IndicatorTypes } from '../../domain/models'; import { CreateSLO, DefaultSummaryClient, + DefaultSummaryTransformManager, DefaultTransformManager, DeleteSLO, DeleteSLOInstances, @@ -41,15 +43,17 @@ import { GetPreviewData } from '../../services/slo/get_preview_data'; import { GetSLOInstances } from '../../services/slo/get_slo_instances'; import { DefaultHistoricalSummaryClient } from '../../services/slo/historical_summary_client'; import { ManageSLO } from '../../services/slo/manage_slo'; +import { ResetSLO } from '../../services/slo/reset_slo'; import { DefaultSummarySearchClient } from '../../services/slo/summary_search_client'; +import { DefaultSummaryTransformGenerator } from '../../services/slo/summary_transform_generator/summary_transform_generator'; import { ApmTransactionDurationTransformGenerator, ApmTransactionErrorRateTransformGenerator, HistogramTransformGenerator, KQLCustomTransformGenerator, MetricCustomTransformGenerator, - TransformGenerator, TimesliceMetricTransformGenerator, + TransformGenerator, } from '../../services/slo/transform_generators'; import type { ObservabilityRequestHandlerContext } from '../../types'; import { createObservabilityServerRoute } from '../create_observability_server_route'; @@ -79,14 +83,30 @@ const createSLORoute = createObservabilityServerRoute({ access: 'public', }, params: createSLOParamsSchema, - handler: async ({ context, params, logger }) => { + handler: async ({ context, params, logger, dependencies, request }) => { await assertPlatinumLicense(context); + const spaceId = + (await dependencies.spaces?.spacesService?.getActiveSpace(request))?.id ?? 'default'; + const esClient = (await context.core).elasticsearch.client.asCurrentUser; const soClient = (await context.core).savedObjects.client; const repository = new KibanaSavedObjectsSLORepository(soClient); const transformManager = new DefaultTransformManager(transformGenerators, esClient, logger); - const createSLO = new CreateSLO(esClient, repository, transformManager); + const summaryTransformManager = new DefaultSummaryTransformManager( + new DefaultSummaryTransformGenerator(), + esClient, + logger + ); + + const createSLO = new CreateSLO( + esClient, + repository, + transformManager, + summaryTransformManager, + logger, + spaceId + ); const response = await createSLO.execute(params.body); @@ -101,15 +121,30 @@ const updateSLORoute = createObservabilityServerRoute({ access: 'public', }, params: updateSLOParamsSchema, - handler: async ({ context, params, logger }) => { + handler: async ({ context, request, params, logger, dependencies }) => { await assertPlatinumLicense(context); + const spaceId = + (await dependencies.spaces?.spacesService?.getActiveSpace(request))?.id ?? 'default'; const esClient = (await context.core).elasticsearch.client.asCurrentUser; const soClient = (await context.core).savedObjects.client; const repository = new KibanaSavedObjectsSLORepository(soClient); const transformManager = new DefaultTransformManager(transformGenerators, esClient, logger); - const updateSLO = new UpdateSLO(repository, transformManager, esClient); + const summaryTransformManager = new DefaultSummaryTransformManager( + new DefaultSummaryTransformGenerator(), + esClient, + logger + ); + + const updateSLO = new UpdateSLO( + repository, + transformManager, + summaryTransformManager, + esClient, + logger, + spaceId + ); const response = await updateSLO.execute(params.path.id, params.body); @@ -140,7 +175,19 @@ const deleteSLORoute = createObservabilityServerRoute({ const repository = new KibanaSavedObjectsSLORepository(soClient); const transformManager = new DefaultTransformManager(transformGenerators, esClient, logger); - const deleteSLO = new DeleteSLO(repository, transformManager, esClient, rulesClient); + const summaryTransformManager = new DefaultSummaryTransformManager( + new DefaultSummaryTransformGenerator(), + esClient, + logger + ); + + const deleteSLO = new DeleteSLO( + repository, + transformManager, + summaryTransformManager, + esClient, + rulesClient + ); await deleteSLO.execute(params.path.id); }, @@ -183,7 +230,13 @@ const enableSLORoute = createObservabilityServerRoute({ const repository = new KibanaSavedObjectsSLORepository(soClient); const transformManager = new DefaultTransformManager(transformGenerators, esClient, logger); - const manageSLO = new ManageSLO(repository, transformManager); + const summaryTransformManager = new DefaultSummaryTransformManager( + new DefaultSummaryTransformGenerator(), + esClient, + logger + ); + + const manageSLO = new ManageSLO(repository, transformManager, summaryTransformManager); const response = await manageSLO.enable(params.path.id); @@ -206,7 +259,13 @@ const disableSLORoute = createObservabilityServerRoute({ const repository = new KibanaSavedObjectsSLORepository(soClient); const transformManager = new DefaultTransformManager(transformGenerators, esClient, logger); - const manageSLO = new ManageSLO(repository, transformManager); + const summaryTransformManager = new DefaultSummaryTransformManager( + new DefaultSummaryTransformGenerator(), + esClient, + logger + ); + + const manageSLO = new ManageSLO(repository, transformManager, summaryTransformManager); const response = await manageSLO.disable(params.path.id); @@ -214,6 +273,44 @@ const disableSLORoute = createObservabilityServerRoute({ }, }); +const resetSLORoute = createObservabilityServerRoute({ + endpoint: 'POST /api/observability/slos/{id}/_reset 2023-10-31', + options: { + tags: ['access:slo_write'], + access: 'public', + }, + params: resetSLOParamsSchema, + handler: async ({ context, request, params, logger, dependencies }) => { + await assertPlatinumLicense(context); + + const spaceId = + (await dependencies.spaces?.spacesService?.getActiveSpace(request))?.id ?? 'default'; + const soClient = (await context.core).savedObjects.client; + const esClient = (await context.core).elasticsearch.client.asCurrentUser; + + const repository = new KibanaSavedObjectsSLORepository(soClient); + const transformManager = new DefaultTransformManager(transformGenerators, esClient, logger); + const summaryTransformManager = new DefaultSummaryTransformManager( + new DefaultSummaryTransformGenerator(), + esClient, + logger + ); + + const resetSLO = new ResetSLO( + esClient, + repository, + transformManager, + summaryTransformManager, + logger, + spaceId + ); + + const response = await resetSLO.execute(params.path.id); + + return response; + }, +}); + const findSLORoute = createObservabilityServerRoute({ endpoint: 'GET /api/observability/slos 2023-10-31', options: { @@ -221,13 +318,16 @@ const findSLORoute = createObservabilityServerRoute({ access: 'public', }, params: findSLOParamsSchema, - handler: async ({ context, params, logger }) => { + handler: async ({ context, request, params, logger, dependencies }) => { await assertPlatinumLicense(context); + const spaceId = + (await dependencies.spaces?.spacesService?.getActiveSpace(request))?.id ?? 'default'; + const soClient = (await context.core).savedObjects.client; const esClient = (await context.core).elasticsearch.client.asCurrentUser; const repository = new KibanaSavedObjectsSLORepository(soClient); - const summarySearchClient = new DefaultSummarySearchClient(esClient, logger); + const summarySearchClient = new DefaultSummarySearchClient(esClient, logger, spaceId); const findSLO = new FindSLO(repository, summarySearchClient); const response = await findSLO.execute(params?.query ?? {}); @@ -253,10 +353,9 @@ const deleteSloInstancesRoute = createObservabilityServerRoute({ }); const findSloDefinitionsRoute = createObservabilityServerRoute({ - endpoint: 'GET /internal/observability/slos/_definitions', + endpoint: 'GET /api/observability/slos/_definitions 2023-10-31', options: { tags: ['access:slo_read'], - access: 'internal', }, params: findSloDefinitionsParamsSchema, handler: async ({ context, params }) => { @@ -266,7 +365,7 @@ const findSloDefinitionsRoute = createObservabilityServerRoute({ const repository = new KibanaSavedObjectsSLORepository(soClient); const findSloDefinitions = new FindSLODefinitions(repository); - const response = await findSloDefinitions.execute(params.query.search); + const response = await findSloDefinitions.execute(params?.query ?? {}); return response; }, @@ -395,4 +494,5 @@ export const sloRouteRepository = { ...getSloBurnRates, ...getPreviewData, ...getSLOInstancesRoute, + ...resetSLORoute, }; diff --git a/x-pack/plugins/observability/server/saved_objects/slo.ts b/x-pack/plugins/observability/server/saved_objects/slo.ts index 41cb509d83755..058596e160fd7 100644 --- a/x-pack/plugins/observability/server/saved_objects/slo.ts +++ b/x-pack/plugins/observability/server/saved_objects/slo.ts @@ -17,7 +17,6 @@ type StoredSLOBefore890 = StoredSLO & { isCalendar?: boolean; }; }; - const migrateSlo890: SavedObjectMigrationFn = (doc) => { const { timeWindow, ...other } = doc.attributes; return { @@ -38,6 +37,21 @@ export const slo: SavedObjectsType = { name: SO_SLO_TYPE, hidden: false, namespaceType: 'multiple-isolated', + switchToModelVersionAt: '8.10.0', + modelVersions: { + 1: { + changes: [ + { type: 'mappings_addition', addedMappings: { version: { type: 'long' } } }, + { + type: 'data_backfill', + backfillFn: (doc) => { + // we explicitely set the version to 1, so we know which SLOs requires a migration to the following version. + return { attributes: { version: doc.attributes.version ?? 1 } }; + }, + }, + ], + }, + }, mappings: { dynamic: false, properties: { @@ -53,6 +67,7 @@ export const slo: SavedObjectsType = { budgetingMethod: { type: 'keyword' }, enabled: { type: 'boolean' }, tags: { type: 'keyword' }, + version: { type: 'long' }, }, }, management: { diff --git a/x-pack/plugins/observability/server/services/slo/__snapshots__/create_slo.test.ts.snap b/x-pack/plugins/observability/server/services/slo/__snapshots__/create_slo.test.ts.snap index e66f1f8124a11..8c2cfc3b0d1f5 100644 --- a/x-pack/plugins/observability/server/services/slo/__snapshots__/create_slo.test.ts.snap +++ b/x-pack/plugins/observability/server/services/slo/__snapshots__/create_slo.test.ts.snap @@ -1,6 +1,144 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`CreateSLO happy path calls the expected services 1`] = ` +Array [ + Object { + "_meta": Object { + "description": "Ingest pipeline for SLO summary data [id: unique-id, revision: 1]", + "managed": true, + "managed_by": "observability", + "version": 3, + }, + "description": "Ingest pipeline for SLO summary data [id: unique-id, revision: 1]", + "id": ".slo-observability.summary.pipeline-unique-id-1", + "processors": Array [ + Object { + "set": Object { + "description": "Set errorBudgetEstimated field", + "field": "errorBudgetEstimated", + "value": false, + }, + }, + Object { + "set": Object { + "description": "Set isTempDoc field", + "field": "isTempDoc", + "value": false, + }, + }, + Object { + "set": Object { + "description": "Set groupBy field", + "field": "slo.groupBy", + "value": "*", + }, + }, + Object { + "set": Object { + "description": "Set name field", + "field": "slo.name", + "value": "irrelevant", + }, + }, + Object { + "set": Object { + "description": "Set description field", + "field": "slo.description", + "value": "irrelevant", + }, + }, + Object { + "set": Object { + "description": "Set tags field", + "field": "slo.tags", + "value": Array [], + }, + }, + Object { + "set": Object { + "description": "Set indicator.type field", + "field": "slo.indicator.type", + "value": "sli.apm.transactionErrorRate", + }, + }, + Object { + "set": Object { + "description": "Set budgetingMethod field", + "field": "slo.budgetingMethod", + "value": "occurrences", + }, + }, + Object { + "set": Object { + "description": "Set timeWindow.duration field", + "field": "slo.timeWindow.duration", + "value": "7d", + }, + }, + Object { + "set": Object { + "description": "Set timeWindow.type field", + "field": "slo.timeWindow.type", + "value": "rolling", + }, + }, + Object { + "set": Object { + "description": "Set objective.target field", + "field": "slo.objective.target", + "value": 0.99, + }, + }, + Object { + "set": Object { + "description": "if 'statusCode == 0', set status to NO_DATA", + "field": "status", + "if": "ctx.statusCode == 0", + "value": "NO_DATA", + }, + }, + Object { + "set": Object { + "description": "if 'statusCode == 1', set statusLabel to VIOLATED", + "field": "status", + "if": "ctx.statusCode == 1", + "value": "VIOLATED", + }, + }, + Object { + "set": Object { + "description": "if 'statusCode == 2', set status to DEGRADING", + "field": "status", + "if": "ctx.statusCode == 2", + "value": "DEGRADING", + }, + }, + Object { + "set": Object { + "description": "if 'statusCode == 4', set status to HEALTHY", + "field": "status", + "if": "ctx.statusCode == 4", + "value": "HEALTHY", + }, + }, + Object { + "set": Object { + "field": "summaryUpdatedAt", + "value": "{{{_ingest.timestamp}}}", + }, + }, + Object { + "set": Object { + "field": "spaceId", + "value": "some-space", + }, + }, + ], + }, +] +`; + +exports[`CreateSLO happy path calls the expected services 2`] = ` Array [ Object { "document": Object { @@ -25,6 +163,11 @@ Array [ }, "instanceId": "*", "name": "irrelevant", + "objective": Object { + "target": 0.99, + "timesliceTarget": null, + "timesliceWindow": null, + }, "revision": 1, "tags": Array [], "timeWindow": Object { @@ -32,6 +175,7 @@ Array [ "type": "rolling", }, }, + "spaceId": "some-space", "status": "NO_DATA", "statusCode": 0, "totalEvents": 0, @@ -41,7 +185,7 @@ Array [ }, }, "id": "slo-unique-id", - "index": ".slo-observability.summary-v2.temp", + "index": ".slo-observability.summary-v3.temp", "refresh": true, }, ] diff --git a/x-pack/plugins/observability/server/services/slo/__snapshots__/delete_slo.test.ts.snap b/x-pack/plugins/observability/server/services/slo/__snapshots__/delete_slo.test.ts.snap new file mode 100644 index 0000000000000..3edfb0cd194b0 --- /dev/null +++ b/x-pack/plugins/observability/server/services/slo/__snapshots__/delete_slo.test.ts.snap @@ -0,0 +1,177 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`DeleteSLO happy path removes all resources associatde to the slo 1`] = ` +[MockFunction] { + "calls": Array [ + Array [ + "irrelevant", + ], + ], + "results": Array [ + Object { + "type": "return", + "value": Promise {}, + }, + ], +} +`; + +exports[`DeleteSLO happy path removes all resources associatde to the slo 2`] = ` +[MockFunction] { + "calls": Array [ + Array [ + "slo-summary-irrelevant-1", + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], +} +`; + +exports[`DeleteSLO happy path removes all resources associatde to the slo 3`] = ` +[MockFunction] { + "calls": Array [ + Array [ + "slo-summary-irrelevant-1", + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], +} +`; + +exports[`DeleteSLO happy path removes all resources associatde to the slo 4`] = ` +[MockFunction] { + "calls": Array [ + Array [ + "slo-irrelevant-1", + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], +} +`; + +exports[`DeleteSLO happy path removes all resources associatde to the slo 5`] = ` +[MockFunction] { + "calls": Array [ + Array [ + "slo-irrelevant-1", + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], +} +`; + +exports[`DeleteSLO happy path removes all resources associatde to the slo 6`] = ` +[MockFunction] { + "calls": Array [ + Array [ + Object { + "id": ".slo-observability.summary.pipeline-irrelevant-1", + }, + Object { + "ignore": Array [ + 404, + ], + }, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": Promise {}, + }, + ], +} +`; + +exports[`DeleteSLO happy path removes all resources associatde to the slo 7`] = ` +[MockFunction] { + "calls": Array [ + Array [ + Object { + "index": ".slo-observability.sli-v3*", + "query": Object { + "match": Object { + "slo.id": "irrelevant", + }, + }, + "wait_for_completion": false, + }, + ], + Array [ + Object { + "index": ".slo-observability.summary-v3*", + "query": Object { + "match": Object { + "slo.id": "irrelevant", + }, + }, + "refresh": true, + }, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": Promise {}, + }, + Object { + "type": "return", + "value": Promise {}, + }, + ], +} +`; + +exports[`DeleteSLO happy path removes all resources associatde to the slo 8`] = ` +[MockFunction] { + "calls": Array [ + Array [ + Object { + "filter": "alert.attributes.params.sloId:irrelevant", + }, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], +} +`; + +exports[`DeleteSLO happy path removes all resources associatde to the slo 9`] = ` +[MockFunction] { + "calls": Array [ + Array [ + "irrelevant", + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], +} +`; diff --git a/x-pack/plugins/observability/server/services/slo/__snapshots__/get_slo_instances.test.ts.snap b/x-pack/plugins/observability/server/services/slo/__snapshots__/get_slo_instances.test.ts.snap index be3b681db0af8..8ad9792a22b24 100644 --- a/x-pack/plugins/observability/server/services/slo/__snapshots__/get_slo_instances.test.ts.snap +++ b/x-pack/plugins/observability/server/services/slo/__snapshots__/get_slo_instances.test.ts.snap @@ -11,7 +11,7 @@ Array [ }, }, }, - "index": ".slo-observability.sli-v2*", + "index": ".slo-observability.sli-v3*", "query": Object { "bool": Object { "filter": Array [ diff --git a/x-pack/plugins/observability/server/services/slo/__snapshots__/manage_slo.test.ts.snap b/x-pack/plugins/observability/server/services/slo/__snapshots__/manage_slo.test.ts.snap new file mode 100644 index 0000000000000..aff53e4882818 --- /dev/null +++ b/x-pack/plugins/observability/server/services/slo/__snapshots__/manage_slo.test.ts.snap @@ -0,0 +1,65 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ManageSLO Disable disables the slo when enabled 1`] = ` +[MockFunction] { + "calls": Array [ + Array [ + "slo-irrelevant-1", + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], +} +`; + +exports[`ManageSLO Disable disables the slo when enabled 2`] = ` +[MockFunction] { + "calls": Array [ + Array [ + "slo-summary-irrelevant-1", + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], +} +`; + +exports[`ManageSLO Enable enables the slo when disabled 1`] = ` +[MockFunction] { + "calls": Array [ + Array [ + "slo-irrelevant-1", + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], +} +`; + +exports[`ManageSLO Enable enables the slo when disabled 2`] = ` +[MockFunction] { + "calls": Array [ + Array [ + "slo-summary-irrelevant-1", + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], +} +`; diff --git a/x-pack/plugins/observability/server/services/slo/__snapshots__/reset_slo.test.ts.snap b/x-pack/plugins/observability/server/services/slo/__snapshots__/reset_slo.test.ts.snap new file mode 100644 index 0000000000000..9ad1d09bd1ef8 --- /dev/null +++ b/x-pack/plugins/observability/server/services/slo/__snapshots__/reset_slo.test.ts.snap @@ -0,0 +1,489 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ResetSLO resets all associated resources 1`] = ` +[MockFunction] { + "calls": Array [ + Array [ + "slo-summary-irrelevant-1", + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], +} +`; + +exports[`ResetSLO resets all associated resources 2`] = ` +[MockFunction] { + "calls": Array [ + Array [ + "slo-summary-irrelevant-1", + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], +} +`; + +exports[`ResetSLO resets all associated resources 3`] = ` +[MockFunction] { + "calls": Array [ + Array [ + "slo-irrelevant-1", + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], +} +`; + +exports[`ResetSLO resets all associated resources 4`] = ` +[MockFunction] { + "calls": Array [ + Array [ + "slo-irrelevant-1", + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], +} +`; + +exports[`ResetSLO resets all associated resources 5`] = ` +[MockFunction] { + "calls": Array [ + Array [ + Object { + "index": ".slo-observability.sli-v3*", + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "term": Object { + "slo.id": "irrelevant", + }, + }, + ], + }, + }, + "refresh": true, + }, + ], + Array [ + Object { + "index": ".slo-observability.summary-v3*", + "query": Object { + "bool": Object { + "filter": Array [ + Object { + "term": Object { + "slo.id": "irrelevant", + }, + }, + ], + }, + }, + "refresh": true, + }, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": Promise {}, + }, + Object { + "type": "return", + "value": Promise {}, + }, + ], +} +`; + +exports[`ResetSLO resets all associated resources 6`] = ` +[MockFunction] { + "calls": Array [ + Array [ + Object { + "budgetingMethod": "occurrences", + "createdAt": 2023-01-01T00:00:00.000Z, + "description": "irrelevant", + "enabled": true, + "groupBy": "*", + "id": "irrelevant", + "indicator": Object { + "params": Object { + "environment": "irrelevant", + "index": "metrics-apm*", + "service": "irrelevant", + "threshold": 500, + "transactionName": "irrelevant", + "transactionType": "irrelevant", + }, + "type": "sli.apm.transactionDuration", + }, + "name": "irrelevant", + "objective": Object { + "target": 0.999, + }, + "revision": 1, + "settings": Object { + "frequency": Duration { + "unit": "m", + "value": 1, + }, + "syncDelay": Duration { + "unit": "m", + "value": 1, + }, + }, + "tags": Array [ + "critical", + "k8s", + ], + "timeWindow": Object { + "duration": Duration { + "unit": "d", + "value": 7, + }, + "type": "rolling", + }, + "updatedAt": 2023-01-01T00:00:00.000Z, + "version": 1, + }, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], +} +`; + +exports[`ResetSLO resets all associated resources 7`] = ` +[MockFunction] { + "calls": Array [ + Array [ + "slo-summary-irrelevant-1", + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], +} +`; + +exports[`ResetSLO resets all associated resources 8`] = ` +[MockFunction] { + "calls": Array [ + Array [ + Object { + "_meta": Object { + "description": "Ingest pipeline for SLO summary data [id: irrelevant, revision: 1]", + "managed": true, + "managed_by": "observability", + "version": 3, + }, + "description": "Ingest pipeline for SLO summary data [id: irrelevant, revision: 1]", + "id": ".slo-observability.summary.pipeline-irrelevant-1", + "processors": Array [ + Object { + "set": Object { + "description": "Set errorBudgetEstimated field", + "field": "errorBudgetEstimated", + "value": false, + }, + }, + Object { + "set": Object { + "description": "Set isTempDoc field", + "field": "isTempDoc", + "value": false, + }, + }, + Object { + "set": Object { + "description": "Set groupBy field", + "field": "slo.groupBy", + "value": "*", + }, + }, + Object { + "set": Object { + "description": "Set name field", + "field": "slo.name", + "value": "irrelevant", + }, + }, + Object { + "set": Object { + "description": "Set description field", + "field": "slo.description", + "value": "irrelevant", + }, + }, + Object { + "set": Object { + "description": "Set tags field", + "field": "slo.tags", + "value": Array [ + "critical", + "k8s", + ], + }, + }, + Object { + "set": Object { + "description": "Set indicator.type field", + "field": "slo.indicator.type", + "value": "sli.apm.transactionDuration", + }, + }, + Object { + "set": Object { + "description": "Set budgetingMethod field", + "field": "slo.budgetingMethod", + "value": "occurrences", + }, + }, + Object { + "set": Object { + "description": "Set timeWindow.duration field", + "field": "slo.timeWindow.duration", + "value": "7d", + }, + }, + Object { + "set": Object { + "description": "Set timeWindow.type field", + "field": "slo.timeWindow.type", + "value": "rolling", + }, + }, + Object { + "set": Object { + "description": "Set objective.target field", + "field": "slo.objective.target", + "value": 0.999, + }, + }, + Object { + "set": Object { + "description": "if 'statusCode == 0', set status to NO_DATA", + "field": "status", + "if": "ctx.statusCode == 0", + "value": "NO_DATA", + }, + }, + Object { + "set": Object { + "description": "if 'statusCode == 1', set statusLabel to VIOLATED", + "field": "status", + "if": "ctx.statusCode == 1", + "value": "VIOLATED", + }, + }, + Object { + "set": Object { + "description": "if 'statusCode == 2', set status to DEGRADING", + "field": "status", + "if": "ctx.statusCode == 2", + "value": "DEGRADING", + }, + }, + Object { + "set": Object { + "description": "if 'statusCode == 4', set status to HEALTHY", + "field": "status", + "if": "ctx.statusCode == 4", + "value": "HEALTHY", + }, + }, + Object { + "set": Object { + "field": "summaryUpdatedAt", + "value": "{{{_ingest.timestamp}}}", + }, + }, + Object { + "set": Object { + "field": "spaceId", + "value": "some-space", + }, + }, + ], + }, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": Promise {}, + }, + ], +} +`; + +exports[`ResetSLO resets all associated resources 9`] = ` +[MockFunction] { + "calls": Array [ + Array [ + Object { + "budgetingMethod": "occurrences", + "createdAt": 2023-01-01T00:00:00.000Z, + "description": "irrelevant", + "enabled": true, + "groupBy": "*", + "id": "irrelevant", + "indicator": Object { + "params": Object { + "environment": "irrelevant", + "index": "metrics-apm*", + "service": "irrelevant", + "threshold": 500, + "transactionName": "irrelevant", + "transactionType": "irrelevant", + }, + "type": "sli.apm.transactionDuration", + }, + "name": "irrelevant", + "objective": Object { + "target": 0.999, + }, + "revision": 1, + "settings": Object { + "frequency": Duration { + "unit": "m", + "value": 1, + }, + "syncDelay": Duration { + "unit": "m", + "value": 1, + }, + }, + "tags": Array [ + "critical", + "k8s", + ], + "timeWindow": Object { + "duration": Duration { + "unit": "d", + "value": 7, + }, + "type": "rolling", + }, + "updatedAt": 2023-01-01T00:00:00.000Z, + "version": 1, + }, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], +} +`; + +exports[`ResetSLO resets all associated resources 10`] = ` +[MockFunction] { + "calls": Array [ + Array [ + "slo-irrelevant-1", + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], +} +`; + +exports[`ResetSLO resets all associated resources 11`] = ` +[MockFunction] { + "calls": Array [ + Array [ + Object { + "document": Object { + "errorBudgetConsumed": 0, + "errorBudgetEstimated": false, + "errorBudgetInitial": 0.0010000000000000009, + "errorBudgetRemaining": 1, + "goodEvents": 0, + "isTempDoc": true, + "service": Object { + "environment": null, + "name": null, + }, + "sliValue": -1, + "slo": Object { + "budgetingMethod": "occurrences", + "description": "irrelevant", + "groupBy": "*", + "id": "irrelevant", + "indicator": Object { + "type": "sli.apm.transactionDuration", + }, + "instanceId": "*", + "name": "irrelevant", + "objective": Object { + "target": 0.999, + "timesliceTarget": null, + "timesliceWindow": null, + }, + "revision": 1, + "tags": Array [ + "critical", + "k8s", + ], + "timeWindow": Object { + "duration": "7d", + "type": "rolling", + }, + }, + "spaceId": "some-space", + "status": "NO_DATA", + "statusCode": 0, + "totalEvents": 0, + "transaction": Object { + "name": null, + "type": null, + }, + }, + "id": "slo-irrelevant", + "index": ".slo-observability.summary-v3.temp", + "refresh": true, + }, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": Promise {}, + }, + ], +} +`; diff --git a/x-pack/plugins/observability/server/services/slo/__snapshots__/summary_search_client.test.ts.snap b/x-pack/plugins/observability/server/services/slo/__snapshots__/summary_search_client.test.ts.snap index ea94e840aac0e..daf5e47a0a66c 100644 --- a/x-pack/plugins/observability/server/services/slo/__snapshots__/summary_search_client.test.ts.snap +++ b/x-pack/plugins/observability/server/services/slo/__snapshots__/summary_search_client.test.ts.snap @@ -3,7 +3,7 @@ exports[`Summary Search Client returns the summary documents without duplicate temporary summary documents 1`] = ` Array [ Object { - "index": ".slo-observability.summary-v2*", + "index": ".slo-observability.summary-v3*", "query": Object { "bool": Object { "filter": Array [ diff --git a/x-pack/plugins/observability/server/services/slo/__snapshots__/update_slo.test.ts.snap b/x-pack/plugins/observability/server/services/slo/__snapshots__/update_slo.test.ts.snap index ae7a966951f7c..c1f0099d24343 100644 --- a/x-pack/plugins/observability/server/services/slo/__snapshots__/update_slo.test.ts.snap +++ b/x-pack/plugins/observability/server/services/slo/__snapshots__/update_slo.test.ts.snap @@ -1,51 +1,88 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`UpdateSLO index a temporary summary document 1`] = ` -Array [ - Object { - "document": Object { - "errorBudgetConsumed": 0, - "errorBudgetEstimated": false, - "errorBudgetInitial": 0.0010000000000000009, - "errorBudgetRemaining": 1, - "goodEvents": 0, - "isTempDoc": true, - "service": Object { - "environment": null, - "name": null, +exports[`UpdateSLO when error happens during the update restores the previous SLO definition in the repository 1`] = ` +[MockFunction] { + "calls": Array [ + Array [ + "slo-summary-original-id-2", + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], +} +`; + +exports[`UpdateSLO when error happens during the update restores the previous SLO definition in the repository 2`] = ` +[MockFunction] { + "calls": Array [ + Array [ + "slo-summary-original-id-2", + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], +} +`; + +exports[`UpdateSLO when error happens during the update restores the previous SLO definition in the repository 3`] = ` +[MockFunction] { + "calls": Array [ + Array [ + "slo-original-id-2", + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], +} +`; + +exports[`UpdateSLO when error happens during the update restores the previous SLO definition in the repository 4`] = ` +[MockFunction] { + "calls": Array [ + Array [ + "slo-original-id-2", + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], +} +`; + +exports[`UpdateSLO when error happens during the update restores the previous SLO definition in the repository 5`] = ` +[MockFunction] { + "calls": Array [ + Array [ + Object { + "id": ".slo-observability.summary.pipeline-original-id-2", }, - "sliValue": -1, - "slo": Object { - "budgetingMethod": "occurrences", - "description": "irrelevant", - "groupBy": "*", - "id": "unique-id", - "indicator": Object { - "type": "sli.apm.transactionErrorRate", - }, - "instanceId": "*", - "name": "irrelevant", - "revision": 2, - "tags": Array [ - "critical", - "k8s", + Object { + "ignore": Array [ + 404, ], - "timeWindow": Object { - "duration": "7d", - "type": "rolling", - }, - }, - "status": "NO_DATA", - "statusCode": 0, - "totalEvents": 0, - "transaction": Object { - "name": null, - "type": null, }, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": Promise {}, }, - "id": "slo-unique-id", - "index": ".slo-observability.summary-v2.temp", - "refresh": true, - }, -] + ], +} `; diff --git a/x-pack/plugins/observability/server/services/slo/create_slo.test.ts b/x-pack/plugins/observability/server/services/slo/create_slo.test.ts index bd34d652e5fa4..fe8de00589db6 100644 --- a/x-pack/plugins/observability/server/services/slo/create_slo.test.ts +++ b/x-pack/plugins/observability/server/services/slo/create_slo.test.ts @@ -5,25 +5,45 @@ * 2.0. */ -import { ElasticsearchClientMock, elasticsearchServiceMock } from '@kbn/core/server/mocks'; +import { + ElasticsearchClientMock, + elasticsearchServiceMock, + loggingSystemMock, +} from '@kbn/core/server/mocks'; +import { MockedLogger } from '@kbn/logging-mocks'; import { CreateSLO } from './create_slo'; import { fiveMinute, oneMinute } from './fixtures/duration'; import { createAPMTransactionErrorRateIndicator, createSLOParams } from './fixtures/slo'; -import { createSLORepositoryMock, createTransformManagerMock } from './mocks'; +import { + createSLORepositoryMock, + createSummaryTransformManagerMock, + createTransformManagerMock, +} from './mocks'; import { SLORepository } from './slo_repository'; import { TransformManager } from './transform_manager'; describe('CreateSLO', () => { let esClientMock: ElasticsearchClientMock; + let loggerMock: jest.Mocked; let mockRepository: jest.Mocked; let mockTransformManager: jest.Mocked; + let mockSummaryTransformManager: jest.Mocked; let createSLO: CreateSLO; beforeEach(() => { esClientMock = elasticsearchServiceMock.createElasticsearchClient(); + loggerMock = loggingSystemMock.createLogger(); mockRepository = createSLORepositoryMock(); mockTransformManager = createTransformManagerMock(); - createSLO = new CreateSLO(esClientMock, mockRepository, mockTransformManager); + mockSummaryTransformManager = createSummaryTransformManagerMock(); + createSLO = new CreateSLO( + esClientMock, + mockRepository, + mockTransformManager, + mockSummaryTransformManager, + loggerMock, + 'some-space' + ); }); describe('happy path', () => { @@ -32,7 +52,8 @@ describe('CreateSLO', () => { id: 'unique-id', indicator: createAPMTransactionErrorRateIndicator(), }); - mockTransformManager.install.mockResolvedValue('slo-transform-id'); + mockTransformManager.install.mockResolvedValue('slo-id-revision'); + mockSummaryTransformManager.install.mockResolvedValue('slo-summary-id-revision'); const response = await createSLO.execute(sloParams); @@ -47,18 +68,21 @@ describe('CreateSLO', () => { revision: 1, tags: [], enabled: true, + version: 2, createdAt: expect.any(Date), updatedAt: expect.any(Date), }), { throwOnConflict: true } ); - expect(mockTransformManager.install).toHaveBeenCalledWith( - expect.objectContaining({ ...sloParams, id: 'unique-id' }) - ); - expect(mockTransformManager.preview).toHaveBeenCalledWith('slo-transform-id'); - expect(mockTransformManager.start).toHaveBeenCalledWith('slo-transform-id'); - expect(response).toEqual(expect.objectContaining({ id: 'unique-id' })); + + expect(mockTransformManager.install).toHaveBeenCalled(); + expect(mockTransformManager.start).toHaveBeenCalled(); + expect(esClientMock.ingest.putPipeline.mock.calls[0]).toMatchSnapshot(); + expect(mockSummaryTransformManager.install).toHaveBeenCalled(); + expect(mockSummaryTransformManager.start).toHaveBeenCalled(); expect(esClientMock.index.mock.calls[0]).toMatchSnapshot(); + + expect(response).toEqual(expect.objectContaining({ id: 'unique-id' })); }); it('overrides the default values when provided', async () => { @@ -93,32 +117,20 @@ describe('CreateSLO', () => { }); describe('unhappy path', () => { - it('deletes the SLO when transform installation fails', async () => { - mockTransformManager.install.mockRejectedValue(new Error('Transform install error')); - const sloParams = createSLOParams({ indicator: createAPMTransactionErrorRateIndicator() }); - - await expect(createSLO.execute(sloParams)).rejects.toThrowError('Transform install error'); - expect(mockRepository.deleteById).toBeCalled(); - }); - - it('removes the transform and deletes the SLO when transform preview fails', async () => { - mockTransformManager.install.mockResolvedValue('slo-transform-id'); - mockTransformManager.preview.mockRejectedValue(new Error('Transform preview error')); + it('rollbacks new resources on failure', async () => { + mockTransformManager.install.mockRejectedValue(new Error('Rollup transform install error')); const sloParams = createSLOParams({ indicator: createAPMTransactionErrorRateIndicator() }); - await expect(createSLO.execute(sloParams)).rejects.toThrowError('Transform preview error'); - expect(mockTransformManager.uninstall).toBeCalledWith('slo-transform-id'); - expect(mockRepository.deleteById).toBeCalled(); - }); - - it('removes the transform and deletes the SLO when transform start fails', async () => { - mockTransformManager.install.mockResolvedValue('slo-transform-id'); - mockTransformManager.start.mockRejectedValue(new Error('Transform start error')); - const sloParams = createSLOParams({ indicator: createAPMTransactionErrorRateIndicator() }); + await expect(createSLO.execute(sloParams)).rejects.toThrowError( + 'Rollup transform install error' + ); - await expect(createSLO.execute(sloParams)).rejects.toThrowError('Transform start error'); - expect(mockTransformManager.uninstall).toBeCalledWith('slo-transform-id'); - expect(mockRepository.deleteById).toBeCalled(); + expect(mockSummaryTransformManager.stop).toHaveBeenCalled(); + expect(mockSummaryTransformManager.uninstall).toHaveBeenCalled(); + expect(mockTransformManager.stop).toHaveBeenCalled(); + expect(mockTransformManager.uninstall).toHaveBeenCalled(); + expect(esClientMock.ingest.deletePipeline).toHaveBeenCalled(); + expect(mockRepository.deleteById).toHaveBeenCalled(); }); }); }); diff --git a/x-pack/plugins/observability/server/services/slo/create_slo.ts b/x-pack/plugins/observability/server/services/slo/create_slo.ts index cebe8188a7cbd..d7e116d983584 100644 --- a/x-pack/plugins/observability/server/services/slo/create_slo.ts +++ b/x-pack/plugins/observability/server/services/slo/create_slo.ts @@ -5,21 +5,32 @@ * 2.0. */ -import { ElasticsearchClient } from '@kbn/core/server'; +import { ElasticsearchClient, Logger } from '@kbn/core/server'; import { ALL_VALUE, CreateSLOParams, CreateSLOResponse } from '@kbn/slo-schema'; import { v4 as uuidv4 } from 'uuid'; -import { SLO_SUMMARY_TEMP_INDEX_NAME } from '../../../common/slo/constants'; +import { + getSLOSummaryPipelineId, + getSLOSummaryTransformId, + getSLOTransformId, + SLO_MODEL_VERSION, + SLO_SUMMARY_TEMP_INDEX_NAME, +} from '../../../common/slo/constants'; +import { getSLOSummaryPipelineTemplate } from '../../assets/ingest_templates/slo_summary_pipeline_template'; import { Duration, DurationUnit, SLO } from '../../domain/models'; import { validateSLO } from '../../domain/services'; +import { retryTransientEsErrors } from '../../utils/retry'; import { SLORepository } from './slo_repository'; -import { createTempSummaryDocument } from './summary_transform/helpers/create_temp_summary'; +import { createTempSummaryDocument } from './summary_transform_generator/helpers/create_temp_summary'; import { TransformManager } from './transform_manager'; export class CreateSLO { constructor( private esClient: ElasticsearchClient, private repository: SLORepository, - private transformManager: TransformManager + private transformManager: TransformManager, + private summaryTransformManager: TransformManager, + private logger: Logger, + private spaceId: string ) {} public async execute(params: CreateSLOParams): Promise { @@ -27,33 +38,48 @@ export class CreateSLO { validateSLO(slo); await this.repository.save(slo, { throwOnConflict: true }); - let sloTransformId; - try { - sloTransformId = await this.transformManager.install(slo); - } catch (err) { - await this.repository.deleteById(slo.id); - throw err; - } + const rollupTransformId = getSLOTransformId(slo.id, slo.revision); + const summaryTransformId = getSLOSummaryTransformId(slo.id, slo.revision); try { - await this.transformManager.preview(sloTransformId); - await this.transformManager.start(sloTransformId); + await this.transformManager.install(slo); + await this.transformManager.start(rollupTransformId); + await retryTransientEsErrors( + () => this.esClient.ingest.putPipeline(getSLOSummaryPipelineTemplate(slo, this.spaceId)), + { logger: this.logger } + ); + + await this.summaryTransformManager.install(slo); + await this.summaryTransformManager.start(summaryTransformId); + + await retryTransientEsErrors( + () => + this.esClient.index({ + index: SLO_SUMMARY_TEMP_INDEX_NAME, + id: `slo-${slo.id}`, + document: createTempSummaryDocument(slo, this.spaceId), + refresh: true, + }), + { logger: this.logger } + ); } catch (err) { - await Promise.all([ - this.transformManager.uninstall(sloTransformId), - this.repository.deleteById(slo.id), - ]); + this.logger.error( + `Cannot install the SLO [id: ${slo.id}, revision: ${slo.revision}]. Rolling back.` + ); + + await this.summaryTransformManager.stop(summaryTransformId); + await this.summaryTransformManager.uninstall(summaryTransformId); + await this.transformManager.stop(rollupTransformId); + await this.transformManager.uninstall(rollupTransformId); + await this.esClient.ingest.deletePipeline( + { id: getSLOSummaryPipelineId(slo.id, slo.revision) }, + { ignore: [404] } + ); + await this.repository.deleteById(slo.id); throw err; } - await this.esClient.index({ - index: SLO_SUMMARY_TEMP_INDEX_NAME, - id: `slo-${slo.id}`, - document: createTempSummaryDocument(slo), - refresh: true, - }); - return this.toResponse(slo); } @@ -72,6 +98,7 @@ export class CreateSLO { createdAt: now, updatedAt: now, groupBy: !!params.groupBy ? params.groupBy : ALL_VALUE, + version: SLO_MODEL_VERSION, }; } diff --git a/x-pack/plugins/observability/server/services/slo/delete_slo.test.ts b/x-pack/plugins/observability/server/services/slo/delete_slo.test.ts index 8a19890e2ebc2..506151da864d3 100644 --- a/x-pack/plugins/observability/server/services/slo/delete_slo.test.ts +++ b/x-pack/plugins/observability/server/services/slo/delete_slo.test.ts @@ -9,20 +9,20 @@ import { rulesClientMock } from '@kbn/alerting-plugin/server/rules_client.mock'; import { RulesClientApi } from '@kbn/alerting-plugin/server/types'; import { ElasticsearchClient } from '@kbn/core/server'; import { elasticsearchServiceMock } from '@kbn/core/server/mocks'; -import { - getSLOTransformId, - SLO_DESTINATION_INDEX_PATTERN, - SLO_SUMMARY_DESTINATION_INDEX_PATTERN, -} from '../../../common/slo/constants'; import { DeleteSLO } from './delete_slo'; import { createAPMTransactionErrorRateIndicator, createSLO } from './fixtures/slo'; -import { createSLORepositoryMock, createTransformManagerMock } from './mocks'; +import { + createSLORepositoryMock, + createSummaryTransformManagerMock, + createTransformManagerMock, +} from './mocks'; import { SLORepository } from './slo_repository'; import { TransformManager } from './transform_manager'; describe('DeleteSLO', () => { let mockRepository: jest.Mocked; let mockTransformManager: jest.Mocked; + let mockSummaryTransformManager: jest.Mocked; let mockEsClient: jest.Mocked; let mockRulesClient: jest.Mocked; let deleteSLO: DeleteSLO; @@ -30,52 +30,37 @@ describe('DeleteSLO', () => { beforeEach(() => { mockRepository = createSLORepositoryMock(); mockTransformManager = createTransformManagerMock(); + mockSummaryTransformManager = createSummaryTransformManagerMock(); mockEsClient = elasticsearchServiceMock.createElasticsearchClient(); mockRulesClient = rulesClientMock.create(); - deleteSLO = new DeleteSLO(mockRepository, mockTransformManager, mockEsClient, mockRulesClient); + deleteSLO = new DeleteSLO( + mockRepository, + mockTransformManager, + mockSummaryTransformManager, + mockEsClient, + mockRulesClient + ); }); describe('happy path', () => { - it('removes the transform, the roll up data, the associated rules and the SLO from the repository', async () => { - const slo = createSLO({ indicator: createAPMTransactionErrorRateIndicator() }); + it('removes all resources associatde to the slo', async () => { + const slo = createSLO({ + id: 'irrelevant', + indicator: createAPMTransactionErrorRateIndicator(), + }); mockRepository.findById.mockResolvedValueOnce(slo); await deleteSLO.execute(slo.id); - expect(mockRepository.findById).toHaveBeenCalledWith(slo.id); - expect(mockTransformManager.stop).toHaveBeenCalledWith( - getSLOTransformId(slo.id, slo.revision) - ); - expect(mockTransformManager.uninstall).toHaveBeenCalledWith( - getSLOTransformId(slo.id, slo.revision) - ); - expect(mockEsClient.deleteByQuery).toHaveBeenCalledTimes(2); - expect(mockEsClient.deleteByQuery).toHaveBeenNthCalledWith( - 1, - expect.objectContaining({ - index: SLO_DESTINATION_INDEX_PATTERN, - query: { - match: { - 'slo.id': slo.id, - }, - }, - }) - ); - expect(mockEsClient.deleteByQuery).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - index: SLO_SUMMARY_DESTINATION_INDEX_PATTERN, - query: { - match: { - 'slo.id': slo.id, - }, - }, - }) - ); - expect(mockRulesClient.bulkDeleteRules).toHaveBeenCalledWith({ - filter: `alert.attributes.params.sloId:${slo.id}`, - }); - expect(mockRepository.deleteById).toHaveBeenCalledWith(slo.id); + expect(mockRepository.findById).toMatchSnapshot(); + expect(mockSummaryTransformManager.stop).toMatchSnapshot(); + expect(mockSummaryTransformManager.uninstall).toMatchSnapshot(); + expect(mockTransformManager.stop).toMatchSnapshot(); + expect(mockTransformManager.uninstall).toMatchSnapshot(); + expect(mockEsClient.ingest.deletePipeline).toMatchSnapshot(); + expect(mockEsClient.deleteByQuery).toMatchSnapshot(); + expect(mockRulesClient.bulkDeleteRules).toMatchSnapshot(); + expect(mockRepository.deleteById).toMatchSnapshot(); }); }); }); diff --git a/x-pack/plugins/observability/server/services/slo/delete_slo.ts b/x-pack/plugins/observability/server/services/slo/delete_slo.ts index 78c3bffd05417..e3d6663860222 100644 --- a/x-pack/plugins/observability/server/services/slo/delete_slo.ts +++ b/x-pack/plugins/observability/server/services/slo/delete_slo.ts @@ -8,10 +8,13 @@ import { RulesClientApi } from '@kbn/alerting-plugin/server/types'; import { ElasticsearchClient } from '@kbn/core/server'; import { + getSLOSummaryPipelineId, + getSLOSummaryTransformId, getSLOTransformId, SLO_DESTINATION_INDEX_PATTERN, SLO_SUMMARY_DESTINATION_INDEX_PATTERN, } from '../../../common/slo/constants'; +import { retryTransientEsErrors } from '../../utils/retry'; import { SLORepository } from './slo_repository'; import { TransformManager } from './transform_manager'; @@ -19,6 +22,7 @@ export class DeleteSLO { constructor( private repository: SLORepository, private transformManager: TransformManager, + private summaryTransformManager: TransformManager, private esClient: ElasticsearchClient, private rulesClient: RulesClientApi ) {} @@ -26,9 +30,20 @@ export class DeleteSLO { public async execute(sloId: string): Promise { const slo = await this.repository.findById(sloId); - const sloTransformId = getSLOTransformId(slo.id, slo.revision); - await this.transformManager.stop(sloTransformId); - await this.transformManager.uninstall(sloTransformId); + const summaryTransformId = getSLOSummaryTransformId(slo.id, slo.revision); + await this.summaryTransformManager.stop(summaryTransformId); + await this.summaryTransformManager.uninstall(summaryTransformId); + + const rollupTransformId = getSLOTransformId(slo.id, slo.revision); + await this.transformManager.stop(rollupTransformId); + await this.transformManager.uninstall(rollupTransformId); + + await retryTransientEsErrors(() => + this.esClient.ingest.deletePipeline( + { id: getSLOSummaryPipelineId(slo.id, slo.revision) }, + { ignore: [404] } + ) + ); await this.deleteRollupData(slo.id); await this.deleteSummaryData(slo.id); diff --git a/x-pack/plugins/observability/server/services/slo/delete_slo_instances.test.ts b/x-pack/plugins/observability/server/services/slo/delete_slo_instances.test.ts index 8a9c64a6b441c..ca4eac790bd04 100644 --- a/x-pack/plugins/observability/server/services/slo/delete_slo_instances.test.ts +++ b/x-pack/plugins/observability/server/services/slo/delete_slo_instances.test.ts @@ -43,7 +43,7 @@ describe('DeleteSLOInstances', () => { expect(mockEsClient.deleteByQuery).toHaveBeenCalledTimes(2); expect(mockEsClient.deleteByQuery.mock.calls[0][0]).toMatchInlineSnapshot(` Object { - "index": ".slo-observability.sli-v2*", + "index": ".slo-observability.sli-v3*", "query": Object { "bool": Object { "should": Array [ @@ -103,7 +103,7 @@ describe('DeleteSLOInstances', () => { `); expect(mockEsClient.deleteByQuery.mock.calls[1][0]).toMatchInlineSnapshot(` Object { - "index": ".slo-observability.summary-v2*", + "index": ".slo-observability.summary-v3*", "query": Object { "bool": Object { "should": Array [ diff --git a/x-pack/plugins/observability/server/services/slo/find_slo.test.ts b/x-pack/plugins/observability/server/services/slo/find_slo.test.ts index 10436bc0fad54..e8d80ef8e74f5 100644 --- a/x-pack/plugins/observability/server/services/slo/find_slo.test.ts +++ b/x-pack/plugins/observability/server/services/slo/find_slo.test.ts @@ -5,13 +5,14 @@ * 2.0. */ -import { ALL_VALUE } from '@kbn/slo-schema'; +import { ALL_VALUE, Paginated } from '@kbn/slo-schema'; +import { SLO_MODEL_VERSION } from '../../../common/slo/constants'; import { SLO } from '../../domain/models'; import { FindSLO } from './find_slo'; import { createSLO } from './fixtures/slo'; import { createSLORepositoryMock, createSummarySearchClientMock } from './mocks'; import { SLORepository } from './slo_repository'; -import { Paginated, SLOSummary, SummarySearchClient } from './summary_search_client'; +import { SLOSummary, SummarySearchClient } from './summary_search_client'; describe('FindSLO', () => { let mockRepository: jest.Mocked; @@ -95,6 +96,7 @@ describe('FindSLO', () => { revision: slo.revision, groupBy: slo.groupBy, instanceId: ALL_VALUE, + version: SLO_MODEL_VERSION, }, ], }); @@ -147,7 +149,7 @@ describe('FindSLO', () => { await expect(findSLO.execute({ perPage: '5000' })).resolves.not.toThrow(); await expect(findSLO.execute({ perPage: '5001' })).rejects.toThrowError( - 'perPage limit to 5000' + 'perPage limit set to 5000' ); }); }); diff --git a/x-pack/plugins/observability/server/services/slo/find_slo.ts b/x-pack/plugins/observability/server/services/slo/find_slo.ts index cf8150db3e627..fb90ec86d04d5 100644 --- a/x-pack/plugins/observability/server/services/slo/find_slo.ts +++ b/x-pack/plugins/observability/server/services/slo/find_slo.ts @@ -5,11 +5,11 @@ * 2.0. */ -import { FindSLOParams, FindSLOResponse, findSLOResponseSchema } from '@kbn/slo-schema'; +import { FindSLOParams, FindSLOResponse, findSLOResponseSchema, Pagination } from '@kbn/slo-schema'; import { SLO, SLOWithSummary } from '../../domain/models'; import { IllegalArgumentError } from '../../errors'; import { SLORepository } from './slo_repository'; -import { Pagination, SLOSummary, Sort, SummarySearchClient } from './summary_search_client'; +import { SLOSummary, Sort, SummarySearchClient } from './summary_search_client'; const DEFAULT_PAGE = 1; const DEFAULT_PER_PAGE = 25; @@ -55,7 +55,7 @@ function toPagination(params: FindSLOParams): Pagination { const perPage = Number(params.perPage); if (!isNaN(perPage) && perPage > MAX_PER_PAGE) { - throw new IllegalArgumentError('perPage limit to 5000'); + throw new IllegalArgumentError(`perPage limit set to ${MAX_PER_PAGE}`); } return { diff --git a/x-pack/plugins/observability/server/services/slo/find_slo_definitions.ts b/x-pack/plugins/observability/server/services/slo/find_slo_definitions.ts index 157e5b4be5696..38076d67202d5 100644 --- a/x-pack/plugins/observability/server/services/slo/find_slo_definitions.ts +++ b/x-pack/plugins/observability/server/services/slo/find_slo_definitions.ts @@ -5,14 +5,40 @@ * 2.0. */ -import { FindSloDefinitionsResponse, findSloDefinitionsResponseSchema } from '@kbn/slo-schema'; +import { + FindSLODefinitionsParams, + FindSLODefinitionsResponse, + findSloDefinitionsResponseSchema, + Pagination, +} from '@kbn/slo-schema'; +import { IllegalArgumentError } from '../../errors'; import { SLORepository } from './slo_repository'; +const MAX_PER_PAGE = 1000; +const DEFAULT_PER_PAGE = 100; +const DEFAULT_PAGE = 1; + export class FindSLODefinitions { constructor(private repository: SLORepository) {} - public async execute(search: string): Promise { - const sloList = await this.repository.search(search); - return findSloDefinitionsResponseSchema.encode(sloList); + public async execute(params: FindSLODefinitionsParams): Promise { + const result = await this.repository.search(params.search ?? '', toPagination(params), { + includeOutdatedOnly: params.includeOutdatedOnly === true ? true : false, + }); + return findSloDefinitionsResponseSchema.encode(result); + } +} + +function toPagination(params: FindSLODefinitionsParams): Pagination { + const page = Number(params.page); + const perPage = Number(params.perPage); + + if (!isNaN(perPage) && perPage > MAX_PER_PAGE) { + throw new IllegalArgumentError(`perPage limit set to ${MAX_PER_PAGE}`); } + + return { + page: !isNaN(page) && page >= 1 ? page : DEFAULT_PAGE, + perPage: !isNaN(perPage) && perPage >= 1 ? perPage : DEFAULT_PER_PAGE, + }; } diff --git a/x-pack/plugins/observability/server/services/slo/fixtures/slo.ts b/x-pack/plugins/observability/server/services/slo/fixtures/slo.ts index 2bd320cbb8d65..0f75c83775489 100644 --- a/x-pack/plugins/observability/server/services/slo/fixtures/slo.ts +++ b/x-pack/plugins/observability/server/services/slo/fixtures/slo.ts @@ -15,6 +15,7 @@ import { } from '@kbn/slo-schema'; import { cloneDeep } from 'lodash'; import { v4 as uuidv4 } from 'uuid'; +import { SLO_MODEL_VERSION } from '../../../../common/slo/constants'; import { APMTransactionDurationIndicator, APMTransactionErrorRateIndicator, @@ -139,7 +140,7 @@ export const createHistogramIndicator = ( }, }); -const defaultSLO: Omit = { +const defaultSLO: Omit = { name: 'irrelevant', description: 'irrelevant', timeWindow: sevenDaysRolling(), @@ -190,6 +191,7 @@ export const createSLO = (params: Partial = {}): SLO => { revision: 1, createdAt: now, updatedAt: now, + version: SLO_MODEL_VERSION, ...params, }); }; diff --git a/x-pack/plugins/observability/server/services/slo/get_slo.test.ts b/x-pack/plugins/observability/server/services/slo/get_slo.test.ts index 1a5efccc9eb2a..18fe85cdfb0b1 100644 --- a/x-pack/plugins/observability/server/services/slo/get_slo.test.ts +++ b/x-pack/plugins/observability/server/services/slo/get_slo.test.ts @@ -6,6 +6,7 @@ */ import { ALL_VALUE } from '@kbn/slo-schema'; +import { SLO_MODEL_VERSION } from '../../../common/slo/constants'; import { createAPMTransactionErrorRateIndicator, createSLO } from './fixtures/slo'; import { GetSLO } from './get_slo'; import { createSummaryClientMock, createSLORepositoryMock } from './mocks'; @@ -84,6 +85,7 @@ describe('GetSLO', () => { revision: slo.revision, groupBy: slo.groupBy, instanceId: ALL_VALUE, + version: SLO_MODEL_VERSION, }); }); }); diff --git a/x-pack/plugins/observability/server/services/slo/index.ts b/x-pack/plugins/observability/server/services/slo/index.ts index 7c99c289ae90b..2a939c56fde4b 100644 --- a/x-pack/plugins/observability/server/services/slo/index.ts +++ b/x-pack/plugins/observability/server/services/slo/index.ts @@ -14,10 +14,10 @@ export * from './get_slo'; export * from './historical_summary_client'; export * from './resource_installer'; export * from './slo_installer'; -export * from './summary_transform/summary_transform_installer'; export * from './sli_client'; export * from './slo_repository'; export * from './transform_manager'; +export * from './summay_transform_manager'; export * from './update_slo'; export * from './summary_client'; export * from './get_slo_instances'; diff --git a/x-pack/plugins/observability/server/services/slo/manage_slo.test.ts b/x-pack/plugins/observability/server/services/slo/manage_slo.test.ts index 78396fa74cbee..bace47b69ff4b 100644 --- a/x-pack/plugins/observability/server/services/slo/manage_slo.test.ts +++ b/x-pack/plugins/observability/server/services/slo/manage_slo.test.ts @@ -7,19 +7,26 @@ import { createSLO } from './fixtures/slo'; import { ManageSLO } from './manage_slo'; -import { createSLORepositoryMock, createTransformManagerMock } from './mocks'; +import { + createSLORepositoryMock, + createSummaryTransformManagerMock, + createTransformManagerMock, +} from './mocks'; import { SLORepository } from './slo_repository'; import { TransformManager } from './transform_manager'; describe('ManageSLO', () => { let mockRepository: jest.Mocked; let mockTransformManager: jest.Mocked; + let mockSummaryTransformManager: jest.Mocked; let manageSLO: ManageSLO; beforeEach(() => { mockRepository = createSLORepositoryMock(); mockTransformManager = createTransformManagerMock(); - manageSLO = new ManageSLO(mockRepository, mockTransformManager); + mockSummaryTransformManager = createSummaryTransformManagerMock(); + + manageSLO = new ManageSLO(mockRepository, mockTransformManager, mockSummaryTransformManager); }); describe('Enable', () => { @@ -30,16 +37,18 @@ describe('ManageSLO', () => { await manageSLO.enable(slo.id); expect(mockTransformManager.start).not.toHaveBeenCalled(); + expect(mockSummaryTransformManager.start).not.toHaveBeenCalled(); expect(mockRepository.save).not.toHaveBeenCalled(); }); it('enables the slo when disabled', async () => { - const slo = createSLO({ enabled: false }); + const slo = createSLO({ id: 'irrelevant', enabled: false }); mockRepository.findById.mockResolvedValue(slo); await manageSLO.enable(slo.id); - expect(mockTransformManager.start).toHaveBeenCalled(); + expect(mockTransformManager.start).toMatchSnapshot(); + expect(mockSummaryTransformManager.start).toMatchSnapshot(); expect(mockRepository.save).toHaveBeenCalledWith(expect.objectContaining({ enabled: true })); }); }); @@ -52,16 +61,18 @@ describe('ManageSLO', () => { await manageSLO.disable(slo.id); expect(mockTransformManager.stop).not.toHaveBeenCalled(); + expect(mockSummaryTransformManager.stop).not.toHaveBeenCalled(); expect(mockRepository.save).not.toHaveBeenCalled(); }); it('disables the slo when enabled', async () => { - const slo = createSLO({ enabled: true }); + const slo = createSLO({ id: 'irrelevant', enabled: true }); mockRepository.findById.mockResolvedValue(slo); await manageSLO.disable(slo.id); - expect(mockTransformManager.stop).toHaveBeenCalled(); + expect(mockTransformManager.stop).toMatchSnapshot(); + expect(mockSummaryTransformManager.stop).toMatchSnapshot(); expect(mockRepository.save).toHaveBeenCalledWith(expect.objectContaining({ enabled: false })); }); }); diff --git a/x-pack/plugins/observability/server/services/slo/manage_slo.ts b/x-pack/plugins/observability/server/services/slo/manage_slo.ts index b7220f084a64d..9d9fd6eb1705c 100644 --- a/x-pack/plugins/observability/server/services/slo/manage_slo.ts +++ b/x-pack/plugins/observability/server/services/slo/manage_slo.ts @@ -5,12 +5,16 @@ * 2.0. */ -import { getSLOTransformId } from '../../../common/slo/constants'; +import { getSLOSummaryTransformId, getSLOTransformId } from '../../../common/slo/constants'; import { SLORepository } from './slo_repository'; import { TransformManager } from './transform_manager'; export class ManageSLO { - constructor(private repository: SLORepository, private transformManager: TransformManager) {} + constructor( + private repository: SLORepository, + private transformManager: TransformManager, + private summaryTransformManager: TransformManager + ) {} async enable(sloId: string) { const slo = await this.repository.findById(sloId); @@ -18,6 +22,7 @@ export class ManageSLO { return; } + await this.summaryTransformManager.start(getSLOSummaryTransformId(slo.id, slo.revision)); await this.transformManager.start(getSLOTransformId(slo.id, slo.revision)); slo.enabled = true; slo.updatedAt = new Date(); @@ -30,6 +35,7 @@ export class ManageSLO { return; } + await this.summaryTransformManager.stop(getSLOSummaryTransformId(slo.id, slo.revision)); await this.transformManager.stop(getSLOTransformId(slo.id, slo.revision)); slo.enabled = false; slo.updatedAt = new Date(); diff --git a/x-pack/plugins/observability/server/services/slo/mocks/index.ts b/x-pack/plugins/observability/server/services/slo/mocks/index.ts index 979c3057d1e8e..eb8db093a7174 100644 --- a/x-pack/plugins/observability/server/services/slo/mocks/index.ts +++ b/x-pack/plugins/observability/server/services/slo/mocks/index.ts @@ -10,7 +10,6 @@ import { SLIClient } from '../sli_client'; import { SLORepository } from '../slo_repository'; import { SummaryClient } from '../summary_client'; import { SummarySearchClient } from '../summary_search_client'; -import { SummaryTransformInstaller } from '../summary_transform/summary_transform_installer'; import { TransformManager } from '../transform_manager'; const createResourceInstallerMock = (): jest.Mocked => { @@ -19,13 +18,17 @@ const createResourceInstallerMock = (): jest.Mocked => { }; }; -const createSummaryTransformInstallerMock = (): jest.Mocked => { +const createTransformManagerMock = (): jest.Mocked => { return { - installAndStart: jest.fn(), + install: jest.fn(), + preview: jest.fn(), + uninstall: jest.fn(), + start: jest.fn(), + stop: jest.fn(), }; }; -const createTransformManagerMock = (): jest.Mocked => { +const createSummaryTransformManagerMock = (): jest.Mocked => { return { install: jest.fn(), preview: jest.fn(), @@ -65,8 +68,8 @@ const createSLIClientMock = (): jest.Mocked => { export { createResourceInstallerMock, - createSummaryTransformInstallerMock, createTransformManagerMock, + createSummaryTransformManagerMock, createSLORepositoryMock, createSummaryClientMock, createSummarySearchClientMock, diff --git a/x-pack/plugins/observability/server/services/slo/reset_slo.test.ts b/x-pack/plugins/observability/server/services/slo/reset_slo.test.ts new file mode 100644 index 0000000000000..feae6695fbd33 --- /dev/null +++ b/x-pack/plugins/observability/server/services/slo/reset_slo.test.ts @@ -0,0 +1,87 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ElasticsearchClient } from '@kbn/core/server'; +import { elasticsearchServiceMock, loggingSystemMock } from '@kbn/core/server/mocks'; +import { MockedLogger } from '@kbn/logging-mocks'; + +import { SLO_MODEL_VERSION } from '../../../common/slo/constants'; +import { createSLO } from './fixtures/slo'; +import { + createSLORepositoryMock, + createSummaryTransformManagerMock, + createTransformManagerMock, +} from './mocks'; +import { ResetSLO } from './reset_slo'; +import { SLORepository } from './slo_repository'; +import { TransformManager } from './transform_manager'; + +const TEST_DATE = new Date('2023-01-01T00:00:00.000Z'); + +describe('ResetSLO', () => { + let mockRepository: jest.Mocked; + let mockTransformManager: jest.Mocked; + let mockSummaryTransformManager: jest.Mocked; + let mockEsClient: jest.Mocked; + let loggerMock: jest.Mocked; + let resetSLO: ResetSLO; + + beforeEach(() => { + loggerMock = loggingSystemMock.createLogger(); + mockRepository = createSLORepositoryMock(); + mockTransformManager = createTransformManagerMock(); + mockEsClient = elasticsearchServiceMock.createElasticsearchClient(); + mockSummaryTransformManager = createSummaryTransformManagerMock(); + resetSLO = new ResetSLO( + mockEsClient, + mockRepository, + mockTransformManager, + mockSummaryTransformManager, + loggerMock, + 'some-space' + ); + jest.useFakeTimers().setSystemTime(TEST_DATE); + }); + + afterAll(() => { + jest.useRealTimers(); + }); + + it('resets all associated resources', async () => { + const slo = createSLO({ id: 'irrelevant', version: 1 }); + mockRepository.findById.mockResolvedValueOnce(slo); + mockRepository.save.mockImplementation((v) => Promise.resolve(v)); + + await resetSLO.execute(slo.id); + + // delete existing resources and data + expect(mockSummaryTransformManager.stop).toMatchSnapshot(); + expect(mockSummaryTransformManager.uninstall).toMatchSnapshot(); + + expect(mockTransformManager.stop).toMatchSnapshot(); + expect(mockTransformManager.uninstall).toMatchSnapshot(); + + expect(mockEsClient.deleteByQuery).toMatchSnapshot(); + + // install resources + expect(mockSummaryTransformManager.install).toMatchSnapshot(); + expect(mockSummaryTransformManager.start).toMatchSnapshot(); + + expect(mockEsClient.ingest.putPipeline).toMatchSnapshot(); + + expect(mockTransformManager.install).toMatchSnapshot(); + expect(mockTransformManager.start).toMatchSnapshot(); + + expect(mockEsClient.index).toMatchSnapshot(); + + expect(mockRepository.save).toHaveBeenCalledWith({ + ...slo, + version: SLO_MODEL_VERSION, + updatedAt: expect.anything(), + }); + }); +}); diff --git a/x-pack/plugins/observability/server/services/slo/reset_slo.ts b/x-pack/plugins/observability/server/services/slo/reset_slo.ts new file mode 100644 index 0000000000000..8c4a374761979 --- /dev/null +++ b/x-pack/plugins/observability/server/services/slo/reset_slo.ts @@ -0,0 +1,130 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ElasticsearchClient, Logger } from '@kbn/core/server'; +import { resetSLOResponseSchema } from '@kbn/slo-schema'; +import { + getSLOSummaryPipelineId, + getSLOSummaryTransformId, + getSLOTransformId, + SLO_DESTINATION_INDEX_PATTERN, + SLO_MODEL_VERSION, + SLO_SUMMARY_DESTINATION_INDEX_PATTERN, + SLO_SUMMARY_TEMP_INDEX_NAME, +} from '../../../common/slo/constants'; +import { getSLOSummaryPipelineTemplate } from '../../assets/ingest_templates/slo_summary_pipeline_template'; +import { retryTransientEsErrors } from '../../utils/retry'; +import { SLORepository } from './slo_repository'; +import { createTempSummaryDocument } from './summary_transform_generator/helpers/create_temp_summary'; +import { TransformManager } from './transform_manager'; + +export class ResetSLO { + constructor( + private esClient: ElasticsearchClient, + private repository: SLORepository, + private transformManager: TransformManager, + private summaryTransformManager: TransformManager, + private logger: Logger, + private spaceId: string + ) {} + + public async execute(sloId: string) { + const slo = await this.repository.findById(sloId); + + const summaryTransformId = getSLOSummaryTransformId(slo.id, slo.revision); + await this.summaryTransformManager.stop(summaryTransformId); + await this.summaryTransformManager.uninstall(summaryTransformId); + + const rollupTransformId = getSLOTransformId(slo.id, slo.revision); + await this.transformManager.stop(rollupTransformId); + await this.transformManager.uninstall(rollupTransformId); + + await Promise.all([this.deleteRollupData(slo.id), this.deleteSummaryData(slo.id)]); + + try { + await this.transformManager.install(slo); + await this.transformManager.start(rollupTransformId); + await retryTransientEsErrors( + () => this.esClient.ingest.putPipeline(getSLOSummaryPipelineTemplate(slo, this.spaceId)), + { logger: this.logger } + ); + + await this.summaryTransformManager.install(slo); + await this.summaryTransformManager.start(summaryTransformId); + + await retryTransientEsErrors( + () => + this.esClient.index({ + index: SLO_SUMMARY_TEMP_INDEX_NAME, + id: `slo-${slo.id}`, + document: createTempSummaryDocument(slo, this.spaceId), + refresh: true, + }), + { logger: this.logger } + ); + } catch (err) { + this.logger.error( + `Cannot reset the SLO [id: ${slo.id}, revision: ${slo.revision}]. Rolling back.` + ); + + await this.summaryTransformManager.stop(summaryTransformId); + await this.summaryTransformManager.uninstall(summaryTransformId); + await this.transformManager.stop(rollupTransformId); + await this.transformManager.uninstall(rollupTransformId); + await this.esClient.ingest.deletePipeline( + { id: getSLOSummaryPipelineId(slo.id, slo.revision) }, + { ignore: [404] } + ); + + throw err; + } + + const updatedSlo = await this.repository.save({ + ...slo, + version: SLO_MODEL_VERSION, + updatedAt: new Date(), + }); + + return resetSLOResponseSchema.encode(updatedSlo); + } + + /** + * Deleting all SLI rollup data matching the sloId. All revision will be deleted in case of + * residual documents. + * + * @param sloId + */ + private async deleteRollupData(sloId: string): Promise { + await this.esClient.deleteByQuery({ + index: SLO_DESTINATION_INDEX_PATTERN, + refresh: true, + query: { + bool: { + filter: [{ term: { 'slo.id': sloId } }], + }, + }, + }); + } + + /** + * Deleting the summary documents matching the sloId. All revision will be deleted in case of + * residual documents. + * + * @param sloId + */ + private async deleteSummaryData(sloId: string): Promise { + await this.esClient.deleteByQuery({ + index: SLO_SUMMARY_DESTINATION_INDEX_PATTERN, + refresh: true, + query: { + bool: { + filter: [{ term: { 'slo.id': sloId } }], + }, + }, + }); + } +} diff --git a/x-pack/plugins/observability/server/services/slo/resource_installer.test.ts b/x-pack/plugins/observability/server/services/slo/resource_installer.test.ts index 6634b3bc052af..6fe98ebf8f6b2 100644 --- a/x-pack/plugins/observability/server/services/slo/resource_installer.test.ts +++ b/x-pack/plugins/observability/server/services/slo/resource_installer.test.ts @@ -15,7 +15,6 @@ import { SLO_SUMMARY_COMPONENT_TEMPLATE_MAPPINGS_NAME, SLO_SUMMARY_COMPONENT_TEMPLATE_SETTINGS_NAME, SLO_SUMMARY_INDEX_TEMPLATE_NAME, - SLO_SUMMARY_INGEST_PIPELINE_NAME, } from '../../../common/slo/constants'; import { DefaultResourceInstaller } from './resource_installer'; @@ -54,14 +53,10 @@ describe('resourceInstaller', () => { expect.objectContaining({ name: SLO_SUMMARY_INDEX_TEMPLATE_NAME }) ); - expect(mockClusterClient.ingest.putPipeline).toHaveBeenCalledTimes(2); + expect(mockClusterClient.ingest.putPipeline).toHaveBeenCalledTimes(1); expect(mockClusterClient.ingest.putPipeline).toHaveBeenNthCalledWith( 1, expect.objectContaining({ id: SLO_INGEST_PIPELINE_NAME }) ); - expect(mockClusterClient.ingest.putPipeline).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ id: SLO_SUMMARY_INGEST_PIPELINE_NAME }) - ); }); }); diff --git a/x-pack/plugins/observability/server/services/slo/resource_installer.ts b/x-pack/plugins/observability/server/services/slo/resource_installer.ts index 2975e07333599..c639f4cc6c3d7 100644 --- a/x-pack/plugins/observability/server/services/slo/resource_installer.ts +++ b/x-pack/plugins/observability/server/services/slo/resource_installer.ts @@ -28,13 +28,11 @@ import { SLO_SUMMARY_DESTINATION_INDEX_NAME, SLO_SUMMARY_INDEX_TEMPLATE_NAME, SLO_SUMMARY_INDEX_TEMPLATE_PATTERN, - SLO_SUMMARY_INGEST_PIPELINE_NAME, SLO_SUMMARY_TEMP_INDEX_NAME, } from '../../../common/slo/constants'; import { getSLOIndexTemplate } from '../../assets/index_templates/slo_index_templates'; import { getSLOSummaryIndexTemplate } from '../../assets/index_templates/slo_summary_index_templates'; import { getSLOPipelineTemplate } from '../../assets/ingest_templates/slo_pipeline_template'; -import { getSLOSummaryPipelineTemplate } from '../../assets/ingest_templates/slo_summary_pipeline_template'; import { retryTransientEsErrors } from '../../utils/retry'; export interface ResourceInstaller { @@ -87,10 +85,6 @@ export class DefaultResourceInstaller implements ResourceInstaller { await this.createOrUpdateIngestPipelineTemplate( getSLOPipelineTemplate(SLO_INGEST_PIPELINE_NAME, SLO_INGEST_PIPELINE_INDEX_NAME_PREFIX) ); - - await this.createOrUpdateIngestPipelineTemplate( - getSLOSummaryPipelineTemplate(SLO_SUMMARY_INGEST_PIPELINE_NAME) - ); } catch (err) { this.logger.error(`Error installing resources shared for SLO: ${err.message}`); throw err; diff --git a/x-pack/plugins/observability/server/services/slo/slo_installer.test.ts b/x-pack/plugins/observability/server/services/slo/slo_installer.test.ts index 6bd9f798c6234..92d0865ec5c9f 100644 --- a/x-pack/plugins/observability/server/services/slo/slo_installer.test.ts +++ b/x-pack/plugins/observability/server/services/slo/slo_installer.test.ts @@ -7,7 +7,7 @@ import { loggingSystemMock } from '@kbn/core/server/mocks'; import { MockedLogger } from '@kbn/logging-mocks'; -import { createResourceInstallerMock, createSummaryTransformInstallerMock } from './mocks'; +import { createResourceInstallerMock } from './mocks'; import { DefaultSLOInstaller } from './slo_installer'; describe('SLO Installer', () => { @@ -19,16 +19,10 @@ describe('SLO Installer', () => { it.skip('handles concurrent installation', async () => { const resourceInstaller = createResourceInstallerMock(); - const summaryTransformInstaller = createSummaryTransformInstallerMock(); - const service = new DefaultSLOInstaller( - resourceInstaller, - summaryTransformInstaller, - loggerMock - ); + const service = new DefaultSLOInstaller(resourceInstaller, loggerMock); await Promise.all([service.install(), service.install()]); expect(resourceInstaller.ensureCommonResourcesInstalled).toHaveBeenCalledTimes(1); - expect(summaryTransformInstaller.installAndStart).toHaveBeenCalledTimes(1); }); }); diff --git a/x-pack/plugins/observability/server/services/slo/slo_installer.ts b/x-pack/plugins/observability/server/services/slo/slo_installer.ts index bdee31f62912d..9484ecd907d9f 100644 --- a/x-pack/plugins/observability/server/services/slo/slo_installer.ts +++ b/x-pack/plugins/observability/server/services/slo/slo_installer.ts @@ -6,7 +6,7 @@ */ import { Logger } from '@kbn/core/server'; -import { ResourceInstaller, SummaryTransformInstaller } from '.'; +import { ResourceInstaller } from '.'; export interface SLOInstaller { install(): Promise; @@ -15,11 +15,7 @@ export interface SLOInstaller { export class DefaultSLOInstaller implements SLOInstaller { private isInstalling: boolean = false; - constructor( - private sloResourceInstaller: ResourceInstaller, - private sloSummaryInstaller: SummaryTransformInstaller, - private logger: Logger - ) {} + constructor(private sloResourceInstaller: ResourceInstaller, private logger: Logger) {} public async install() { if (this.isInstalling) { @@ -32,9 +28,8 @@ export class DefaultSLOInstaller implements SLOInstaller { installTimeout = setTimeout(() => (this.isInstalling = false), 60000); await this.sloResourceInstaller.ensureCommonResourcesInstalled(); - await this.sloSummaryInstaller.installAndStart(); } catch (error) { - this.logger.error('Failed to install SLO common resources and summary transforms'); + this.logger.error('Failed to install SLO common resources'); } finally { this.isInstalling = false; clearTimeout(installTimeout); diff --git a/x-pack/plugins/observability/server/services/slo/slo_repository.test.ts b/x-pack/plugins/observability/server/services/slo/slo_repository.test.ts index 4b61bff04ea6f..65248d487a392 100644 --- a/x-pack/plugins/observability/server/services/slo/slo_repository.test.ts +++ b/x-pack/plugins/observability/server/services/slo/slo_repository.test.ts @@ -8,6 +8,7 @@ import { SavedObjectsClientContract, SavedObjectsFindResponse } from '@kbn/core/server'; import { savedObjectsClientMock } from '@kbn/core/server/mocks'; import { sloSchema } from '@kbn/slo-schema'; +import { SLO_MODEL_VERSION } from '../../../common/slo/constants'; import { SLO, StoredSLO } from '../../domain/models'; import { SLOIdConflict, SLONotFound } from '../../errors'; import { SO_SLO_TYPE } from '../../saved_objects'; @@ -164,19 +165,42 @@ describe('KibanaSavedObjectsSLORepository', () => { expect(soClientMock.delete).toHaveBeenCalledWith(SO_SLO_TYPE, SOME_SLO.id); }); - it('searches by name', async () => { - const repository = new KibanaSavedObjectsSLORepository(soClientMock); - soClientMock.find.mockResolvedValueOnce(soFindResponse([SOME_SLO, ANOTHER_SLO])); + describe('search', () => { + it('searches by name', async () => { + const repository = new KibanaSavedObjectsSLORepository(soClientMock); + soClientMock.find.mockResolvedValueOnce(soFindResponse([SOME_SLO, ANOTHER_SLO])); - const results = await repository.search(SOME_SLO.name); + const results = await repository.search(SOME_SLO.name, { page: 1, perPage: 100 }); - expect(results).toEqual([SOME_SLO, ANOTHER_SLO]); - expect(soClientMock.find).toHaveBeenCalledWith({ - type: SO_SLO_TYPE, - page: 1, - perPage: 25, - search: SOME_SLO.name, - searchFields: ['name'], + expect(results.results).toEqual([SOME_SLO, ANOTHER_SLO]); + expect(soClientMock.find).toHaveBeenCalledWith({ + type: SO_SLO_TYPE, + page: 1, + perPage: 100, + search: SOME_SLO.name, + searchFields: ['name'], + }); + }); + + it('searches only the outdated ones', async () => { + const repository = new KibanaSavedObjectsSLORepository(soClientMock); + soClientMock.find.mockResolvedValueOnce(soFindResponse([SOME_SLO, ANOTHER_SLO])); + + const results = await repository.search( + SOME_SLO.name, + { page: 1, perPage: 100 }, + { includeOutdatedOnly: true } + ); + + expect(results.results).toEqual([SOME_SLO, ANOTHER_SLO]); + expect(soClientMock.find).toHaveBeenCalledWith({ + type: SO_SLO_TYPE, + page: 1, + perPage: 100, + search: SOME_SLO.name, + searchFields: ['name'], + filter: `slo.attributes.version < ${SLO_MODEL_VERSION}`, + }); }); }); }); diff --git a/x-pack/plugins/observability/server/services/slo/slo_repository.ts b/x-pack/plugins/observability/server/services/slo/slo_repository.ts index cc595ed0b0099..1e2adfbe84b9b 100644 --- a/x-pack/plugins/observability/server/services/slo/slo_repository.ts +++ b/x-pack/plugins/observability/server/services/slo/slo_repository.ts @@ -6,11 +6,11 @@ */ import { SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server'; -import { SavedObjectsErrorHelpers } from '@kbn/core-saved-objects-server'; -import { sloSchema } from '@kbn/slo-schema'; +import { Paginated, Pagination, sloSchema } from '@kbn/slo-schema'; import { fold } from 'fp-ts/lib/Either'; import { pipe } from 'fp-ts/lib/pipeable'; import * as t from 'io-ts'; +import { SLO_MODEL_VERSION } from '../../../common/slo/constants'; import { SLO, StoredSLO } from '../../domain/models'; import { SLOIdConflict, SLONotFound } from '../../errors'; import { SO_SLO_TYPE } from '../../saved_objects'; @@ -20,7 +20,11 @@ export interface SLORepository { findAllByIds(ids: string[]): Promise; findById(id: string): Promise; deleteById(id: string): Promise; - search(search: string): Promise; + search( + search: string, + pagination: Pagination, + options?: { includeOutdatedOnly?: boolean } + ): Promise>; } export class KibanaSavedObjectsSLORepository implements SLORepository { @@ -83,35 +87,38 @@ export class KibanaSavedObjectsSLORepository implements SLORepository { async findAllByIds(ids: string[]): Promise { if (ids.length === 0) return []; - try { - const response = await this.soClient.find({ - type: SO_SLO_TYPE, - page: 1, - perPage: ids.length, - filter: `slo.attributes.id:(${ids.join(' or ')})`, - }); - return response.saved_objects.map((slo) => toSLO(slo.attributes)); - } catch (err) { - if (SavedObjectsErrorHelpers.isNotFoundError(err)) { - throw new SLONotFound(`SLOs [${ids.join(',')}] not found`); - } - throw err; - } + const response = await this.soClient.find({ + type: SO_SLO_TYPE, + page: 1, + perPage: ids.length, + filter: `slo.attributes.id:(${ids.join(' or ')})`, + }); + + return response.saved_objects.map((slo) => toSLO(slo.attributes)); } - async search(search: string): Promise { - try { - const response = await this.soClient.find({ - type: SO_SLO_TYPE, - page: 1, - perPage: 25, - search, - searchFields: ['name'], - }); - return response.saved_objects.map((slo) => toSLO(slo.attributes)); - } catch (err) { - throw err; - } + async search( + search: string, + pagination: Pagination, + options: { includeOutdatedOnly?: boolean } = { includeOutdatedOnly: false } + ): Promise> { + const response = await this.soClient.find({ + type: SO_SLO_TYPE, + page: pagination.page, + perPage: pagination.perPage, + search, + searchFields: ['name'], + ...(!!options.includeOutdatedOnly && { + filter: `slo.attributes.version < ${SLO_MODEL_VERSION}`, + }), + }); + + return { + total: response.total, + perPage: response.per_page, + page: response.page, + results: response.saved_objects.map((slo) => toSLO(slo.attributes)), + }; } } @@ -121,7 +128,13 @@ function toStoredSLO(slo: SLO): StoredSLO { function toSLO(storedSLO: StoredSLO): SLO { return pipe( - sloSchema.decode(storedSLO), + sloSchema.decode({ + ...storedSLO, + // version was added in 8.12.0. This is a safeguard against SO migration issue. + // if not present, we considered the version to be 1, e.g. not migrated. + // We would need to call the _reset api on this SLO. + version: storedSLO.version ?? 1, + }), fold(() => { throw new Error('Invalid Stored SLO'); }, t.identity) diff --git a/x-pack/plugins/observability/server/services/slo/summary_search_client.test.ts b/x-pack/plugins/observability/server/services/slo/summary_search_client.test.ts index fc53bf1e7181b..256aa9164ea3b 100644 --- a/x-pack/plugins/observability/server/services/slo/summary_search_client.test.ts +++ b/x-pack/plugins/observability/server/services/slo/summary_search_client.test.ts @@ -7,17 +7,13 @@ import { ElasticsearchClientMock, elasticsearchServiceMock } from '@kbn/core/server/mocks'; import { loggerMock } from '@kbn/logging-mocks'; +import { Pagination } from '@kbn/slo-schema/src/models/pagination'; import { aHitFromSummaryIndex, aHitFromTempSummaryIndex, aSummaryDocument, } from './fixtures/summary_search_document'; -import { - DefaultSummarySearchClient, - Pagination, - Sort, - SummarySearchClient, -} from './summary_search_client'; +import { DefaultSummarySearchClient, Sort, SummarySearchClient } from './summary_search_client'; const defaultSort: Sort = { field: 'sli_value', @@ -34,7 +30,7 @@ describe('Summary Search Client', () => { beforeEach(() => { esClientMock = elasticsearchServiceMock.createElasticsearchClient(); - service = new DefaultSummarySearchClient(esClientMock, loggerMock.create()); + service = new DefaultSummarySearchClient(esClientMock, loggerMock.create(), 'some-space'); }); it('returns an empty response on error', async () => { @@ -74,10 +70,7 @@ describe('Summary Search Client', () => { const SLO_ID3 = 'slo-three'; const SLO_ID4 = 'slo-four'; const SLO_ID5 = 'slo-five'; - esClientMock.count.mockResolvedValue({ - count: 8, - _shards: { failed: 0, successful: 1, total: 1 }, - }); + esClientMock.search.mockResolvedValue({ took: 0, timed_out: false, @@ -89,7 +82,7 @@ describe('Summary Search Client', () => { }, hits: { total: { - value: 6, + value: 8, relation: 'eq', }, max_score: 1, diff --git a/x-pack/plugins/observability/server/services/slo/summary_search_client.ts b/x-pack/plugins/observability/server/services/slo/summary_search_client.ts index 2361e8dd3678e..9715d727f6fc5 100644 --- a/x-pack/plugins/observability/server/services/slo/summary_search_client.ts +++ b/x-pack/plugins/observability/server/services/slo/summary_search_client.ts @@ -6,9 +6,10 @@ */ import { ElasticsearchClient, Logger } from '@kbn/core/server'; -import { ALL_VALUE } from '@kbn/slo-schema'; +import { ALL_VALUE, Paginated, Pagination } from '@kbn/slo-schema'; import { assertNever } from '@kbn/std'; import _ from 'lodash'; +import { SearchTotalHits } from '@elastic/elasticsearch/lib/api/types'; import { SLO_SUMMARY_DESTINATION_INDEX_PATTERN } from '../../../common/slo/constants'; import { SLOId, Status, Summary } from '../../domain/models'; import { toHighPrecision } from '../../utils/number'; @@ -30,13 +31,6 @@ interface EsSummaryDocument { isTempDoc: boolean; } -export interface Paginated { - total: number; - page: number; - perPage: number; - results: T[]; -} - export interface SLOSummary { id: SLOId; instanceId: string; @@ -49,17 +43,16 @@ export interface Sort { direction: 'asc' | 'desc'; } -export interface Pagination { - page: number; - perPage: number; -} - export interface SummarySearchClient { search(kqlQuery: string, sort: Sort, pagination: Pagination): Promise>; } export class DefaultSummarySearchClient implements SummarySearchClient { - constructor(private esClient: ElasticsearchClient, private logger: Logger) {} + constructor( + private esClient: ElasticsearchClient, + private logger: Logger, + private spaceId: string + ) {} async search( kqlQuery: string, @@ -67,18 +60,14 @@ export class DefaultSummarySearchClient implements SummarySearchClient { pagination: Pagination ): Promise> { try { - const { count: total } = await this.esClient.count({ - index: SLO_SUMMARY_DESTINATION_INDEX_PATTERN, - query: getElastichsearchQueryOrThrow(kqlQuery), - }); - - if (total === 0) { - return { total: 0, perPage: pagination.perPage, page: pagination.page, results: [] }; - } - const summarySearch = await this.esClient.search({ index: SLO_SUMMARY_DESTINATION_INDEX_PATTERN, - query: getElastichsearchQueryOrThrow(kqlQuery), + track_total_hits: true, + query: { + bool: { + filter: [{ term: { spaceId: this.spaceId } }, getElastichsearchQueryOrThrow(kqlQuery)], + }, + }, sort: { // non-temp first, then temp documents isTempDoc: { @@ -92,6 +81,11 @@ export class DefaultSummarySearchClient implements SummarySearchClient { size: pagination.perPage * 2, // twice as much as we return, in case they are all duplicate temp/non-temp summary }); + const total = (summarySearch.hits.total as SearchTotalHits).value ?? 0; + if (total === 0) { + return { total: 0, perPage: pagination.perPage, page: pagination.page, results: [] }; + } + const [tempSummaryDocuments, summaryDocuments] = _.partition( summarySearch.hits.hits, (doc) => !!doc._source?.isTempDoc diff --git a/x-pack/plugins/observability/server/services/slo/summary_transform/__snapshots__/summary_transform_installer.test.ts.snap b/x-pack/plugins/observability/server/services/slo/summary_transform/__snapshots__/summary_transform_installer.test.ts.snap deleted file mode 100644 index 5161c5e5cb51b..0000000000000 --- a/x-pack/plugins/observability/server/services/slo/summary_transform/__snapshots__/summary_transform_installer.test.ts.snap +++ /dev/null @@ -1,1192 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Summary Transform Installer installs only the missing summary transforms 1`] = ` -Array [ - Array [ - Object { - "_meta": Object { - "managed": true, - "managed_by": "observability", - "version": 3, - }, - "description": "Summarize every SLO with timeslices budgeting method and a 7 days rolling time window", - "dest": Object { - "index": ".slo-observability.summary-v2", - "pipeline": ".slo-observability.summary.pipeline", - }, - "frequency": "1m", - "pivot": Object { - "aggregations": Object { - "_objectiveTarget": Object { - "max": Object { - "field": "slo.objective.target", - }, - }, - "errorBudgetConsumed": Object { - "bucket_script": Object { - "buckets_path": Object { - "errorBudgetInitial": "errorBudgetInitial", - "sliValue": "sliValue", - }, - "script": "if (params.sliValue == -1) { return 0 } else { return (1 - params.sliValue) / params.errorBudgetInitial }", - }, - }, - "errorBudgetInitial": Object { - "bucket_script": Object { - "buckets_path": Object { - "objectiveTarget": "_objectiveTarget", - }, - "script": "1 - params.objectiveTarget", - }, - }, - "errorBudgetRemaining": Object { - "bucket_script": Object { - "buckets_path": Object { - "errorBudgetConsummed": "errorBudgetConsumed", - }, - "script": "1 - params.errorBudgetConsummed", - }, - }, - "goodEvents": Object { - "sum": Object { - "field": "slo.isGoodSlice", - }, - }, - "sliValue": Object { - "bucket_script": Object { - "buckets_path": Object { - "goodEvents": "goodEvents", - "totalEvents": "totalEvents", - }, - "script": "if (params.totalEvents == 0) { return -1 } else if (params.goodEvents >= params.totalEvents) { return 1 } else { return params.goodEvents / params.totalEvents }", - }, - }, - "statusCode": Object { - "bucket_script": Object { - "buckets_path": Object { - "errorBudgetRemaining": "errorBudgetRemaining", - "objectiveTarget": "_objectiveTarget", - "sliValue": "sliValue", - }, - "script": Object { - "source": "if (params.sliValue == -1) { return 0 } else if (params.sliValue >= params.objectiveTarget) { return 4 } else if (params.errorBudgetRemaining > 0) { return 2 } else { return 1 }", - }, - }, - }, - "totalEvents": Object { - "value_count": Object { - "field": "slo.isGoodSlice", - }, - }, - }, - "group_by": Object { - "errorBudgetEstimated": Object { - "terms": Object { - "field": "errorBudgetEstimated", - }, - }, - "isTempDoc": Object { - "terms": Object { - "field": "isTempDoc", - }, - }, - "service.environment": Object { - "terms": Object { - "field": "service.environment", - "missing_bucket": true, - }, - }, - "service.name": Object { - "terms": Object { - "field": "service.name", - "missing_bucket": true, - }, - }, - "slo.budgetingMethod": Object { - "terms": Object { - "field": "slo.budgetingMethod", - }, - }, - "slo.description": Object { - "terms": Object { - "field": "slo.description", - }, - }, - "slo.groupBy": Object { - "terms": Object { - "field": "slo.groupBy", - }, - }, - "slo.id": Object { - "terms": Object { - "field": "slo.id", - }, - }, - "slo.indicator.type": Object { - "terms": Object { - "field": "slo.indicator.type", - }, - }, - "slo.instanceId": Object { - "terms": Object { - "field": "slo.instanceId", - }, - }, - "slo.name": Object { - "terms": Object { - "field": "slo.name", - }, - }, - "slo.revision": Object { - "terms": Object { - "field": "slo.revision", - }, - }, - "slo.tags": Object { - "terms": Object { - "field": "slo.tags", - }, - }, - "slo.timeWindow.duration": Object { - "terms": Object { - "field": "slo.timeWindow.duration", - }, - }, - "slo.timeWindow.type": Object { - "terms": Object { - "field": "slo.timeWindow.type", - }, - }, - "transaction.name": Object { - "terms": Object { - "field": "transaction.name", - "missing_bucket": true, - }, - }, - "transaction.type": Object { - "terms": Object { - "field": "transaction.type", - "missing_bucket": true, - }, - }, - }, - }, - "settings": Object { - "deduce_mappings": false, - "unattended": true, - }, - "source": Object { - "index": ".slo-observability.sli-v2*", - "query": Object { - "bool": Object { - "filter": Array [ - Object { - "range": Object { - "@timestamp": Object { - "gte": "now-7d/m", - "lte": "now/m", - }, - }, - }, - Object { - "term": Object { - "slo.budgetingMethod": "timeslices", - }, - }, - Object { - "term": Object { - "slo.timeWindow.type": "rolling", - }, - }, - Object { - "term": Object { - "slo.timeWindow.duration": "7d", - }, - }, - ], - }, - }, - "runtime_mappings": Object { - "errorBudgetEstimated": Object { - "script": "emit(false)", - "type": "boolean", - }, - "isTempDoc": Object { - "script": "emit(false)", - "type": "boolean", - }, - }, - }, - "sync": Object { - "time": Object { - "delay": "125s", - "field": "@timestamp", - }, - }, - "transform_id": "slo-summary-timeslices-7d-rolling", - }, - Object { - "ignore": Array [ - 409, - ], - }, - ], - Array [ - Object { - "_meta": Object { - "managed": true, - "managed_by": "observability", - "version": 3, - }, - "description": "Summarize every SLO with timeslices budgeting method and a 30 days rolling time window", - "dest": Object { - "index": ".slo-observability.summary-v2", - "pipeline": ".slo-observability.summary.pipeline", - }, - "frequency": "1m", - "pivot": Object { - "aggregations": Object { - "_objectiveTarget": Object { - "max": Object { - "field": "slo.objective.target", - }, - }, - "errorBudgetConsumed": Object { - "bucket_script": Object { - "buckets_path": Object { - "errorBudgetInitial": "errorBudgetInitial", - "sliValue": "sliValue", - }, - "script": "if (params.sliValue == -1) { return 0 } else { return (1 - params.sliValue) / params.errorBudgetInitial }", - }, - }, - "errorBudgetInitial": Object { - "bucket_script": Object { - "buckets_path": Object { - "objectiveTarget": "_objectiveTarget", - }, - "script": "1 - params.objectiveTarget", - }, - }, - "errorBudgetRemaining": Object { - "bucket_script": Object { - "buckets_path": Object { - "errorBudgetConsummed": "errorBudgetConsumed", - }, - "script": "1 - params.errorBudgetConsummed", - }, - }, - "goodEvents": Object { - "sum": Object { - "field": "slo.isGoodSlice", - }, - }, - "sliValue": Object { - "bucket_script": Object { - "buckets_path": Object { - "goodEvents": "goodEvents", - "totalEvents": "totalEvents", - }, - "script": "if (params.totalEvents == 0) { return -1 } else if (params.goodEvents >= params.totalEvents) { return 1 } else { return params.goodEvents / params.totalEvents }", - }, - }, - "statusCode": Object { - "bucket_script": Object { - "buckets_path": Object { - "errorBudgetRemaining": "errorBudgetRemaining", - "objectiveTarget": "_objectiveTarget", - "sliValue": "sliValue", - }, - "script": Object { - "source": "if (params.sliValue == -1) { return 0 } else if (params.sliValue >= params.objectiveTarget) { return 4 } else if (params.errorBudgetRemaining > 0) { return 2 } else { return 1 }", - }, - }, - }, - "totalEvents": Object { - "value_count": Object { - "field": "slo.isGoodSlice", - }, - }, - }, - "group_by": Object { - "errorBudgetEstimated": Object { - "terms": Object { - "field": "errorBudgetEstimated", - }, - }, - "isTempDoc": Object { - "terms": Object { - "field": "isTempDoc", - }, - }, - "service.environment": Object { - "terms": Object { - "field": "service.environment", - "missing_bucket": true, - }, - }, - "service.name": Object { - "terms": Object { - "field": "service.name", - "missing_bucket": true, - }, - }, - "slo.budgetingMethod": Object { - "terms": Object { - "field": "slo.budgetingMethod", - }, - }, - "slo.description": Object { - "terms": Object { - "field": "slo.description", - }, - }, - "slo.groupBy": Object { - "terms": Object { - "field": "slo.groupBy", - }, - }, - "slo.id": Object { - "terms": Object { - "field": "slo.id", - }, - }, - "slo.indicator.type": Object { - "terms": Object { - "field": "slo.indicator.type", - }, - }, - "slo.instanceId": Object { - "terms": Object { - "field": "slo.instanceId", - }, - }, - "slo.name": Object { - "terms": Object { - "field": "slo.name", - }, - }, - "slo.revision": Object { - "terms": Object { - "field": "slo.revision", - }, - }, - "slo.tags": Object { - "terms": Object { - "field": "slo.tags", - }, - }, - "slo.timeWindow.duration": Object { - "terms": Object { - "field": "slo.timeWindow.duration", - }, - }, - "slo.timeWindow.type": Object { - "terms": Object { - "field": "slo.timeWindow.type", - }, - }, - "transaction.name": Object { - "terms": Object { - "field": "transaction.name", - "missing_bucket": true, - }, - }, - "transaction.type": Object { - "terms": Object { - "field": "transaction.type", - "missing_bucket": true, - }, - }, - }, - }, - "settings": Object { - "deduce_mappings": false, - "unattended": true, - }, - "source": Object { - "index": ".slo-observability.sli-v2*", - "query": Object { - "bool": Object { - "filter": Array [ - Object { - "range": Object { - "@timestamp": Object { - "gte": "now-30d/m", - "lte": "now/m", - }, - }, - }, - Object { - "term": Object { - "slo.budgetingMethod": "timeslices", - }, - }, - Object { - "term": Object { - "slo.timeWindow.type": "rolling", - }, - }, - Object { - "term": Object { - "slo.timeWindow.duration": "30d", - }, - }, - ], - }, - }, - "runtime_mappings": Object { - "errorBudgetEstimated": Object { - "script": "emit(false)", - "type": "boolean", - }, - "isTempDoc": Object { - "script": "emit(false)", - "type": "boolean", - }, - }, - }, - "sync": Object { - "time": Object { - "delay": "125s", - "field": "@timestamp", - }, - }, - "transform_id": "slo-summary-timeslices-30d-rolling", - }, - Object { - "ignore": Array [ - 409, - ], - }, - ], - Array [ - Object { - "_meta": Object { - "managed": true, - "managed_by": "observability", - "version": 3, - }, - "description": "Summarize every SLO with timeslices budgeting method and a 90 days rolling time window", - "dest": Object { - "index": ".slo-observability.summary-v2", - "pipeline": ".slo-observability.summary.pipeline", - }, - "frequency": "1m", - "pivot": Object { - "aggregations": Object { - "_objectiveTarget": Object { - "max": Object { - "field": "slo.objective.target", - }, - }, - "errorBudgetConsumed": Object { - "bucket_script": Object { - "buckets_path": Object { - "errorBudgetInitial": "errorBudgetInitial", - "sliValue": "sliValue", - }, - "script": "if (params.sliValue == -1) { return 0 } else { return (1 - params.sliValue) / params.errorBudgetInitial }", - }, - }, - "errorBudgetInitial": Object { - "bucket_script": Object { - "buckets_path": Object { - "objectiveTarget": "_objectiveTarget", - }, - "script": "1 - params.objectiveTarget", - }, - }, - "errorBudgetRemaining": Object { - "bucket_script": Object { - "buckets_path": Object { - "errorBudgetConsummed": "errorBudgetConsumed", - }, - "script": "1 - params.errorBudgetConsummed", - }, - }, - "goodEvents": Object { - "sum": Object { - "field": "slo.isGoodSlice", - }, - }, - "sliValue": Object { - "bucket_script": Object { - "buckets_path": Object { - "goodEvents": "goodEvents", - "totalEvents": "totalEvents", - }, - "script": "if (params.totalEvents == 0) { return -1 } else if (params.goodEvents >= params.totalEvents) { return 1 } else { return params.goodEvents / params.totalEvents }", - }, - }, - "statusCode": Object { - "bucket_script": Object { - "buckets_path": Object { - "errorBudgetRemaining": "errorBudgetRemaining", - "objectiveTarget": "_objectiveTarget", - "sliValue": "sliValue", - }, - "script": Object { - "source": "if (params.sliValue == -1) { return 0 } else if (params.sliValue >= params.objectiveTarget) { return 4 } else if (params.errorBudgetRemaining > 0) { return 2 } else { return 1 }", - }, - }, - }, - "totalEvents": Object { - "value_count": Object { - "field": "slo.isGoodSlice", - }, - }, - }, - "group_by": Object { - "errorBudgetEstimated": Object { - "terms": Object { - "field": "errorBudgetEstimated", - }, - }, - "isTempDoc": Object { - "terms": Object { - "field": "isTempDoc", - }, - }, - "service.environment": Object { - "terms": Object { - "field": "service.environment", - "missing_bucket": true, - }, - }, - "service.name": Object { - "terms": Object { - "field": "service.name", - "missing_bucket": true, - }, - }, - "slo.budgetingMethod": Object { - "terms": Object { - "field": "slo.budgetingMethod", - }, - }, - "slo.description": Object { - "terms": Object { - "field": "slo.description", - }, - }, - "slo.groupBy": Object { - "terms": Object { - "field": "slo.groupBy", - }, - }, - "slo.id": Object { - "terms": Object { - "field": "slo.id", - }, - }, - "slo.indicator.type": Object { - "terms": Object { - "field": "slo.indicator.type", - }, - }, - "slo.instanceId": Object { - "terms": Object { - "field": "slo.instanceId", - }, - }, - "slo.name": Object { - "terms": Object { - "field": "slo.name", - }, - }, - "slo.revision": Object { - "terms": Object { - "field": "slo.revision", - }, - }, - "slo.tags": Object { - "terms": Object { - "field": "slo.tags", - }, - }, - "slo.timeWindow.duration": Object { - "terms": Object { - "field": "slo.timeWindow.duration", - }, - }, - "slo.timeWindow.type": Object { - "terms": Object { - "field": "slo.timeWindow.type", - }, - }, - "transaction.name": Object { - "terms": Object { - "field": "transaction.name", - "missing_bucket": true, - }, - }, - "transaction.type": Object { - "terms": Object { - "field": "transaction.type", - "missing_bucket": true, - }, - }, - }, - }, - "settings": Object { - "deduce_mappings": false, - "unattended": true, - }, - "source": Object { - "index": ".slo-observability.sli-v2*", - "query": Object { - "bool": Object { - "filter": Array [ - Object { - "range": Object { - "@timestamp": Object { - "gte": "now-90d/m", - "lte": "now/m", - }, - }, - }, - Object { - "term": Object { - "slo.budgetingMethod": "timeslices", - }, - }, - Object { - "term": Object { - "slo.timeWindow.type": "rolling", - }, - }, - Object { - "term": Object { - "slo.timeWindow.duration": "90d", - }, - }, - ], - }, - }, - "runtime_mappings": Object { - "errorBudgetEstimated": Object { - "script": "emit(false)", - "type": "boolean", - }, - "isTempDoc": Object { - "script": "emit(false)", - "type": "boolean", - }, - }, - }, - "sync": Object { - "time": Object { - "delay": "125s", - "field": "@timestamp", - }, - }, - "transform_id": "slo-summary-timeslices-90d-rolling", - }, - Object { - "ignore": Array [ - 409, - ], - }, - ], - Array [ - Object { - "_meta": Object { - "managed": true, - "managed_by": "observability", - "version": 3, - }, - "description": "Summarize every SLO with timeslices budgeting method and a weekly calendar aligned time window", - "dest": Object { - "index": ".slo-observability.summary-v2", - "pipeline": ".slo-observability.summary.pipeline", - }, - "frequency": "1m", - "pivot": Object { - "aggregations": Object { - "_objectiveTarget": Object { - "max": Object { - "field": "slo.objective.target", - }, - }, - "_sliceDurationInSeconds": Object { - "max": Object { - "field": "slo.objective.sliceDurationInSeconds", - }, - }, - "_totalSlicesInPeriod": Object { - "bucket_script": Object { - "buckets_path": Object { - "sliceDurationInSeconds": "_sliceDurationInSeconds", - }, - "script": "Math.ceil(7 * 24 * 60 * 60 / params.sliceDurationInSeconds)", - }, - }, - "errorBudgetConsumed": Object { - "bucket_script": Object { - "buckets_path": Object { - "errorBudgetInitial": "errorBudgetInitial", - "goodEvents": "goodEvents", - "totalEvents": "totalEvents", - "totalSlicesInPeriod": "_totalSlicesInPeriod", - }, - "script": "if (params.totalEvents == 0) { return 0 } else { return (params.totalEvents - params.goodEvents) / (params.totalSlicesInPeriod * params.errorBudgetInitial) }", - }, - }, - "errorBudgetInitial": Object { - "bucket_script": Object { - "buckets_path": Object { - "objective": "_objectiveTarget", - }, - "script": "1 - params.objective", - }, - }, - "errorBudgetRemaining": Object { - "bucket_script": Object { - "buckets_path": Object { - "errorBudgetConsumed": "errorBudgetConsumed", - }, - "script": "1 - params.errorBudgetConsumed", - }, - }, - "goodEvents": Object { - "sum": Object { - "field": "slo.isGoodSlice", - }, - }, - "sliValue": Object { - "bucket_script": Object { - "buckets_path": Object { - "goodEvents": "goodEvents", - "totalEvents": "totalEvents", - }, - "script": "if (params.totalEvents == 0) { return -1 } else if (params.goodEvents >= params.totalEvents) { return 1 } else { return params.goodEvents / params.totalEvents }", - }, - }, - "statusCode": Object { - "bucket_script": Object { - "buckets_path": Object { - "errorBudgetRemaining": "errorBudgetRemaining", - "objective": "_objectiveTarget", - "sliValue": "sliValue", - }, - "script": "if (params.sliValue == -1) { return 0 } else if (params.sliValue >= params.objective) { return 4 } else if (params.errorBudgetRemaining > 0) { return 2 } else { return 1 }", - }, - }, - "totalEvents": Object { - "value_count": Object { - "field": "slo.isGoodSlice", - }, - }, - }, - "group_by": Object { - "errorBudgetEstimated": Object { - "terms": Object { - "field": "errorBudgetEstimated", - }, - }, - "isTempDoc": Object { - "terms": Object { - "field": "isTempDoc", - }, - }, - "service.environment": Object { - "terms": Object { - "field": "service.environment", - "missing_bucket": true, - }, - }, - "service.name": Object { - "terms": Object { - "field": "service.name", - "missing_bucket": true, - }, - }, - "slo.budgetingMethod": Object { - "terms": Object { - "field": "slo.budgetingMethod", - }, - }, - "slo.description": Object { - "terms": Object { - "field": "slo.description", - }, - }, - "slo.groupBy": Object { - "terms": Object { - "field": "slo.groupBy", - }, - }, - "slo.id": Object { - "terms": Object { - "field": "slo.id", - }, - }, - "slo.indicator.type": Object { - "terms": Object { - "field": "slo.indicator.type", - }, - }, - "slo.instanceId": Object { - "terms": Object { - "field": "slo.instanceId", - }, - }, - "slo.name": Object { - "terms": Object { - "field": "slo.name", - }, - }, - "slo.revision": Object { - "terms": Object { - "field": "slo.revision", - }, - }, - "slo.tags": Object { - "terms": Object { - "field": "slo.tags", - }, - }, - "slo.timeWindow.duration": Object { - "terms": Object { - "field": "slo.timeWindow.duration", - }, - }, - "slo.timeWindow.type": Object { - "terms": Object { - "field": "slo.timeWindow.type", - }, - }, - "transaction.name": Object { - "terms": Object { - "field": "transaction.name", - "missing_bucket": true, - }, - }, - "transaction.type": Object { - "terms": Object { - "field": "transaction.type", - "missing_bucket": true, - }, - }, - }, - }, - "settings": Object { - "deduce_mappings": false, - "unattended": true, - }, - "source": Object { - "index": ".slo-observability.sli-v2*", - "query": Object { - "bool": Object { - "filter": Array [ - Object { - "range": Object { - "@timestamp": Object { - "gte": "now/w", - "lte": "now/m", - }, - }, - }, - Object { - "term": Object { - "slo.budgetingMethod": "timeslices", - }, - }, - Object { - "term": Object { - "slo.timeWindow.type": "calendarAligned", - }, - }, - Object { - "term": Object { - "slo.timeWindow.duration": "1w", - }, - }, - ], - }, - }, - "runtime_mappings": Object { - "errorBudgetEstimated": Object { - "script": "emit(false)", - "type": "boolean", - }, - "isTempDoc": Object { - "script": "emit(false)", - "type": "boolean", - }, - }, - }, - "sync": Object { - "time": Object { - "delay": "125s", - "field": "@timestamp", - }, - }, - "transform_id": "slo-summary-timeslices-weekly-aligned", - }, - Object { - "ignore": Array [ - 409, - ], - }, - ], - Array [ - Object { - "_meta": Object { - "managed": true, - "managed_by": "observability", - "version": 3, - }, - "description": "Summarize every SLO with timeslices budgeting method and a monthly calendar aligned time window", - "dest": Object { - "index": ".slo-observability.summary-v2", - "pipeline": ".slo-observability.summary.pipeline", - }, - "frequency": "1m", - "pivot": Object { - "aggregations": Object { - "_objectiveTarget": Object { - "max": Object { - "field": "slo.objective.target", - }, - }, - "_sliceDurationInSeconds": Object { - "max": Object { - "field": "slo.objective.sliceDurationInSeconds", - }, - }, - "_totalSlicesInPeriod": Object { - "bucket_script": Object { - "buckets_path": Object { - "sliceDurationInSeconds": "_sliceDurationInSeconds", - }, - "script": Object { - "source": " - Date d = new Date(); - Instant instant = Instant.ofEpochMilli(d.getTime()); - LocalDateTime now = LocalDateTime.ofInstant(instant, ZoneOffset.UTC); - LocalDateTime startOfMonth = now - .withDayOfMonth(1) - .withHour(0) - .withMinute(0) - .withSecond(0); - LocalDateTime startOfNextMonth = startOfMonth.plusMonths(1); - double sliceDurationInMinutes = params.sliceDurationInSeconds / 60; - - return Math.ceil(Duration.between(startOfMonth, startOfNextMonth).toMinutes() / sliceDurationInMinutes); - ", - }, - }, - }, - "errorBudgetConsumed": Object { - "bucket_script": Object { - "buckets_path": Object { - "errorBudgetInitial": "errorBudgetInitial", - "goodEvents": "goodEvents", - "totalEvents": "totalEvents", - "totalSlicesInPeriod": "_totalSlicesInPeriod", - }, - "script": "if (params.totalEvents == 0) { return 0 } else { return (params.totalEvents - params.goodEvents) / (params.totalSlicesInPeriod * params.errorBudgetInitial) }", - }, - }, - "errorBudgetInitial": Object { - "bucket_script": Object { - "buckets_path": Object { - "objective": "_objectiveTarget", - }, - "script": "1 - params.objective", - }, - }, - "errorBudgetRemaining": Object { - "bucket_script": Object { - "buckets_path": Object { - "errorBudgetConsumed": "errorBudgetConsumed", - }, - "script": "1 - params.errorBudgetConsumed", - }, - }, - "goodEvents": Object { - "sum": Object { - "field": "slo.isGoodSlice", - }, - }, - "sliValue": Object { - "bucket_script": Object { - "buckets_path": Object { - "goodEvents": "goodEvents", - "totalEvents": "totalEvents", - }, - "script": "if (params.totalEvents == 0) { return -1 } else if (params.goodEvents >= params.totalEvents) { return 1 } else { return params.goodEvents / params.totalEvents }", - }, - }, - "statusCode": Object { - "bucket_script": Object { - "buckets_path": Object { - "errorBudgetRemaining": "errorBudgetRemaining", - "objective": "_objectiveTarget", - "sliValue": "sliValue", - }, - "script": "if (params.sliValue == -1) { return 0 } else if (params.sliValue >= params.objective) { return 4 } else if (params.errorBudgetRemaining > 0) { return 2 } else { return 1 }", - }, - }, - "totalEvents": Object { - "value_count": Object { - "field": "slo.isGoodSlice", - }, - }, - }, - "group_by": Object { - "errorBudgetEstimated": Object { - "terms": Object { - "field": "errorBudgetEstimated", - }, - }, - "isTempDoc": Object { - "terms": Object { - "field": "isTempDoc", - }, - }, - "service.environment": Object { - "terms": Object { - "field": "service.environment", - "missing_bucket": true, - }, - }, - "service.name": Object { - "terms": Object { - "field": "service.name", - "missing_bucket": true, - }, - }, - "slo.budgetingMethod": Object { - "terms": Object { - "field": "slo.budgetingMethod", - }, - }, - "slo.description": Object { - "terms": Object { - "field": "slo.description", - }, - }, - "slo.groupBy": Object { - "terms": Object { - "field": "slo.groupBy", - }, - }, - "slo.id": Object { - "terms": Object { - "field": "slo.id", - }, - }, - "slo.indicator.type": Object { - "terms": Object { - "field": "slo.indicator.type", - }, - }, - "slo.instanceId": Object { - "terms": Object { - "field": "slo.instanceId", - }, - }, - "slo.name": Object { - "terms": Object { - "field": "slo.name", - }, - }, - "slo.revision": Object { - "terms": Object { - "field": "slo.revision", - }, - }, - "slo.tags": Object { - "terms": Object { - "field": "slo.tags", - }, - }, - "slo.timeWindow.duration": Object { - "terms": Object { - "field": "slo.timeWindow.duration", - }, - }, - "slo.timeWindow.type": Object { - "terms": Object { - "field": "slo.timeWindow.type", - }, - }, - "transaction.name": Object { - "terms": Object { - "field": "transaction.name", - "missing_bucket": true, - }, - }, - "transaction.type": Object { - "terms": Object { - "field": "transaction.type", - "missing_bucket": true, - }, - }, - }, - }, - "settings": Object { - "deduce_mappings": false, - "unattended": true, - }, - "source": Object { - "index": ".slo-observability.sli-v2*", - "query": Object { - "bool": Object { - "filter": Array [ - Object { - "range": Object { - "@timestamp": Object { - "gte": "now/M", - "lte": "now/m", - }, - }, - }, - Object { - "term": Object { - "slo.budgetingMethod": "timeslices", - }, - }, - Object { - "term": Object { - "slo.timeWindow.type": "calendarAligned", - }, - }, - Object { - "term": Object { - "slo.timeWindow.duration": "1M", - }, - }, - ], - }, - }, - "runtime_mappings": Object { - "errorBudgetEstimated": Object { - "script": "emit(false)", - "type": "boolean", - }, - "isTempDoc": Object { - "script": "emit(false)", - "type": "boolean", - }, - }, - }, - "sync": Object { - "time": Object { - "delay": "125s", - "field": "@timestamp", - }, - }, - "transform_id": "slo-summary-timeslices-monthly-aligned", - }, - Object { - "ignore": Array [ - 409, - ], - }, - ], -] -`; diff --git a/x-pack/plugins/observability/server/services/slo/summary_transform/summary_transform_installer.test.ts b/x-pack/plugins/observability/server/services/slo/summary_transform/summary_transform_installer.test.ts deleted file mode 100644 index be79f9d796142..0000000000000 --- a/x-pack/plugins/observability/server/services/slo/summary_transform/summary_transform_installer.test.ts +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -import { - ElasticsearchClientMock, - elasticsearchServiceMock, - loggingSystemMock, -} from '@kbn/core/server/mocks'; -import { MockedLogger } from '@kbn/logging-mocks'; -import { DefaultSummaryTransformInstaller } from './summary_transform_installer'; -import { ALL_TRANSFORM_TEMPLATES } from './templates'; - -describe('Summary Transform Installer', () => { - let esClientMock: ElasticsearchClientMock; - let loggerMock: jest.Mocked; - - beforeEach(() => { - esClientMock = elasticsearchServiceMock.createElasticsearchClient(); - loggerMock = loggingSystemMock.createLogger(); - }); - - it('skips the installation when latest version already installed', async () => { - esClientMock.transform.getTransform.mockResolvedValue({ - count: ALL_TRANSFORM_TEMPLATES.length, - // @ts-ignore - transforms: ALL_TRANSFORM_TEMPLATES.map((transform) => ({ - id: transform.transform_id, - _meta: transform._meta, - })), - }); - const installer = new DefaultSummaryTransformInstaller(esClientMock, loggerMock); - - await installer.installAndStart(); - - expect(esClientMock.transform.stopTransform).not.toHaveBeenCalled(); - expect(esClientMock.transform.deleteTransform).not.toHaveBeenCalled(); - expect(esClientMock.transform.putTransform).not.toHaveBeenCalled(); - expect(esClientMock.transform.startTransform).not.toHaveBeenCalled(); - }); - - it('installs every summary transforms when none are already installed', async () => { - esClientMock.transform.getTransform.mockResolvedValue({ count: 0, transforms: [] }); - const installer = new DefaultSummaryTransformInstaller(esClientMock, loggerMock); - - await installer.installAndStart(); - - const nbOfTransforms = ALL_TRANSFORM_TEMPLATES.length; - - expect(esClientMock.transform.stopTransform).not.toHaveBeenCalled(); - expect(esClientMock.transform.deleteTransform).not.toHaveBeenCalled(); - expect(esClientMock.transform.putTransform).toHaveBeenCalledTimes(nbOfTransforms); - expect(esClientMock.transform.startTransform).toHaveBeenCalledTimes(nbOfTransforms); - }); - - it('desinstalls previous summary transforms prior to installing the new ones', async () => { - esClientMock.transform.getTransform.mockResolvedValue({ - count: ALL_TRANSFORM_TEMPLATES.length, - // @ts-ignore - transforms: ALL_TRANSFORM_TEMPLATES.map((transform) => ({ - id: transform.transform_id, - _meta: { ...transform._meta, version: -1 }, - })), - }); - const installer = new DefaultSummaryTransformInstaller(esClientMock, loggerMock); - - await installer.installAndStart(); - - const nbOfTransforms = ALL_TRANSFORM_TEMPLATES.length; - - expect(esClientMock.transform.stopTransform).toHaveBeenCalledTimes(nbOfTransforms); - expect(esClientMock.transform.deleteTransform).toHaveBeenCalledTimes(nbOfTransforms); - expect(esClientMock.transform.putTransform).toHaveBeenCalledTimes(nbOfTransforms); - expect(esClientMock.transform.startTransform).toHaveBeenCalledTimes(nbOfTransforms); - }); - - it('installs only the missing summary transforms', async () => { - const occurrencesSummaryTransforms = ALL_TRANSFORM_TEMPLATES.filter((transform) => - transform.transform_id.includes('-occurrences-') - ); - esClientMock.transform.getTransform.mockResolvedValue({ - count: occurrencesSummaryTransforms.length, - // @ts-ignore - transforms: occurrencesSummaryTransforms.map((transform) => ({ - id: transform.transform_id, - _meta: transform._meta, - })), - }); - const installer = new DefaultSummaryTransformInstaller(esClientMock, loggerMock); - - await installer.installAndStart(); - - const nbOfTransforms = ALL_TRANSFORM_TEMPLATES.length - occurrencesSummaryTransforms.length; - - expect(esClientMock.transform.stopTransform).not.toHaveBeenCalled(); - expect(esClientMock.transform.deleteTransform).not.toHaveBeenCalled(); - expect(esClientMock.transform.putTransform).toHaveBeenCalledTimes(nbOfTransforms); - expect(esClientMock.transform.startTransform).toHaveBeenCalledTimes(nbOfTransforms); - expect(esClientMock.transform.putTransform.mock.calls).toMatchSnapshot(); - }); -}); diff --git a/x-pack/plugins/observability/server/services/slo/summary_transform/summary_transform_installer.ts b/x-pack/plugins/observability/server/services/slo/summary_transform/summary_transform_installer.ts deleted file mode 100644 index 0a51338615b42..0000000000000 --- a/x-pack/plugins/observability/server/services/slo/summary_transform/summary_transform_installer.ts +++ /dev/null @@ -1,105 +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 { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types'; -import type { ElasticsearchClient, Logger } from '@kbn/core/server'; -import { - SLO_SUMMARY_TRANSFORMS_VERSION, - SLO_SUMMARY_TRANSFORM_NAME_PREFIX, -} from '../../../../common/slo/constants'; -import { retryTransientEsErrors } from '../../../utils/retry'; -import { ALL_TRANSFORM_TEMPLATES } from './templates'; - -export interface SummaryTransformInstaller { - installAndStart(): Promise; -} - -export class DefaultSummaryTransformInstaller implements SummaryTransformInstaller { - constructor(private esClient: ElasticsearchClient, private logger: Logger) {} - - public async installAndStart(): Promise { - const allTransformIds = ALL_TRANSFORM_TEMPLATES.map((transform) => transform.transform_id); - const summaryTransforms = await this.execute(() => - this.esClient.transform.getTransform( - { transform_id: `${SLO_SUMMARY_TRANSFORM_NAME_PREFIX}*`, allow_no_match: true }, - { ignore: [404] } - ) - ); - const alreadyInstalled = - summaryTransforms.count === allTransformIds.length && - summaryTransforms.transforms.every( - (transform) => transform._meta?.version === SLO_SUMMARY_TRANSFORMS_VERSION - ) && - summaryTransforms.transforms.every((transform) => allTransformIds.includes(transform.id)); - - if (alreadyInstalled) { - this.logger.info(`SLO summary transforms already installed - skipping`); - return; - } - - for (const transformTemplate of ALL_TRANSFORM_TEMPLATES) { - const transformId = transformTemplate.transform_id; - const transform = summaryTransforms.transforms.find((t) => t.id === transformId); - - const transformAlreadyInstalled = - !!transform && transform._meta?.version === SLO_SUMMARY_TRANSFORMS_VERSION; - const previousTransformAlreadyInstalled = - !!transform && transform._meta?.version !== SLO_SUMMARY_TRANSFORMS_VERSION; - - if (transformAlreadyInstalled) { - this.logger.info(`SLO summary transform [${transformId}] already installed - skipping`); - continue; - } - - if (previousTransformAlreadyInstalled) { - await this.deletePreviousTransformVersion(transformId); - } - - await this.installTransform(transformId, transformTemplate); - await this.startTransform(transformId); - } - - this.logger.info(`SLO summary transforms installed and started`); - } - - private async installTransform( - transformId: string, - transformTemplate: TransformPutTransformRequest - ) { - this.logger.info(`Installing SLO summary transform [${transformId}]`); - await this.execute(() => - this.esClient.transform.putTransform(transformTemplate, { ignore: [409] }) - ); - } - - private async deletePreviousTransformVersion(transformId: string) { - this.logger.info(`Deleting previous SLO summary transform [${transformId}]`); - await this.execute(() => - this.esClient.transform.stopTransform( - { transform_id: transformId, allow_no_match: true, force: true }, - { ignore: [409, 404] } - ) - ); - await this.execute(() => - this.esClient.transform.deleteTransform( - { transform_id: transformId, force: true }, - { ignore: [409, 404] } - ) - ); - } - - private async startTransform(transformId: string) { - this.logger.info(`Starting SLO summary transform [${transformId}]`); - await this.execute(() => - this.esClient.transform.startTransform({ transform_id: transformId }, { ignore: [409] }) - ); - } - - private async execute(esCall: () => Promise): Promise { - return await retryTransientEsErrors(esCall, { logger: this.logger }); - } -} diff --git a/x-pack/plugins/observability/server/services/slo/summary_transform/templates/common.ts b/x-pack/plugins/observability/server/services/slo/summary_transform/templates/common.ts deleted file mode 100644 index c99a6c2be9d3c..0000000000000 --- a/x-pack/plugins/observability/server/services/slo/summary_transform/templates/common.ts +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -export const groupBy = { - 'slo.id': { - terms: { - field: 'slo.id', - }, - }, - 'slo.revision': { - terms: { - field: 'slo.revision', - }, - }, - 'slo.groupBy': { - terms: { - field: 'slo.groupBy', - }, - }, - 'slo.instanceId': { - terms: { - field: 'slo.instanceId', - }, - }, - 'slo.name': { - terms: { - field: 'slo.name', - }, - }, - 'slo.description': { - terms: { - field: 'slo.description', - }, - }, - 'slo.tags': { - terms: { - field: 'slo.tags', - }, - }, - 'slo.indicator.type': { - terms: { - field: 'slo.indicator.type', - }, - }, - 'slo.budgetingMethod': { - terms: { - field: 'slo.budgetingMethod', - }, - }, - 'slo.timeWindow.duration': { - terms: { - field: 'slo.timeWindow.duration', - }, - }, - 'slo.timeWindow.type': { - terms: { - field: 'slo.timeWindow.type', - }, - }, - errorBudgetEstimated: { - terms: { - field: 'errorBudgetEstimated', - }, - }, - // Differentiate the temporary document from the summary one - isTempDoc: { - terms: { - field: 'isTempDoc', - }, - }, - // optional fields: only specified for APM indicators. Must include missing_bucket:true - 'service.name': { - terms: { - field: 'service.name', - missing_bucket: true, - }, - }, - 'service.environment': { - terms: { - field: 'service.environment', - missing_bucket: true, - }, - }, - 'transaction.name': { - terms: { - field: 'transaction.name', - missing_bucket: true, - }, - }, - 'transaction.type': { - terms: { - field: 'transaction.type', - missing_bucket: true, - }, - }, -}; diff --git a/x-pack/plugins/observability/server/services/slo/summary_transform/templates/index.ts b/x-pack/plugins/observability/server/services/slo/summary_transform/templates/index.ts deleted file mode 100644 index 68c42db91e923..0000000000000 --- a/x-pack/plugins/observability/server/services/slo/summary_transform/templates/index.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { SUMMARY_OCCURRENCES_7D_ROLLING } from './summary_occurrences_7d_rolling'; -import { SUMMARY_OCCURRENCES_30D_ROLLING } from './summary_occurrences_30d_rolling'; -import { SUMMARY_OCCURRENCES_90D_ROLLING } from './summary_occurrences_90d_rolling'; -import { SUMMARY_TIMESLICES_7D_ROLLING } from './summary_timeslices_7d_rolling'; -import { SUMMARY_TIMESLICES_30D_ROLLING } from './summary_timeslices_30d_rolling'; -import { SUMMARY_TIMESLICES_90D_ROLLING } from './summary_timeslices_90d_rolling'; -import { SUMMARY_OCCURRENCES_WEEKLY_ALIGNED } from './summary_occurrences_weekly_aligned'; -import { SUMMARY_OCCURRENCES_MONTHLY_ALIGNED } from './summary_occurrences_monthly_aligned'; -import { SUMMARY_TIMESLICES_WEEKLY_ALIGNED } from './summary_timeslices_weekly_aligned'; -import { SUMMARY_TIMESLICES_MONTHLY_ALIGNED } from './summary_timeslices_monthly_aligned'; - -export const ALL_TRANSFORM_TEMPLATES = [ - SUMMARY_OCCURRENCES_7D_ROLLING, - SUMMARY_OCCURRENCES_30D_ROLLING, - SUMMARY_OCCURRENCES_90D_ROLLING, - SUMMARY_OCCURRENCES_WEEKLY_ALIGNED, - SUMMARY_OCCURRENCES_MONTHLY_ALIGNED, - SUMMARY_TIMESLICES_7D_ROLLING, - SUMMARY_TIMESLICES_30D_ROLLING, - SUMMARY_TIMESLICES_90D_ROLLING, - SUMMARY_TIMESLICES_WEEKLY_ALIGNED, - SUMMARY_TIMESLICES_MONTHLY_ALIGNED, -]; diff --git a/x-pack/plugins/observability/server/services/slo/summary_transform/templates/summary_occurrences_30d_rolling.ts b/x-pack/plugins/observability/server/services/slo/summary_transform/templates/summary_occurrences_30d_rolling.ts deleted file mode 100644 index 9fb34a38051f7..0000000000000 --- a/x-pack/plugins/observability/server/services/slo/summary_transform/templates/summary_occurrences_30d_rolling.ts +++ /dev/null @@ -1,153 +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 { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types'; -import { - SLO_DESTINATION_INDEX_PATTERN, - SLO_SUMMARY_DESTINATION_INDEX_NAME, - SLO_SUMMARY_INGEST_PIPELINE_NAME, - SLO_SUMMARY_TRANSFORMS_VERSION, - SLO_SUMMARY_TRANSFORM_NAME_PREFIX, -} from '../../../../../common/slo/constants'; -import { groupBy } from './common'; - -export const SUMMARY_OCCURRENCES_30D_ROLLING: TransformPutTransformRequest = { - transform_id: `${SLO_SUMMARY_TRANSFORM_NAME_PREFIX}occurrences-30d-rolling`, - dest: { - index: SLO_SUMMARY_DESTINATION_INDEX_NAME, - pipeline: SLO_SUMMARY_INGEST_PIPELINE_NAME, - }, - source: { - index: SLO_DESTINATION_INDEX_PATTERN, - runtime_mappings: { - errorBudgetEstimated: { - type: 'boolean', - script: 'emit(false)', - }, - isTempDoc: { - type: 'boolean', - script: 'emit(false)', - }, - }, - query: { - bool: { - filter: [ - { - range: { - '@timestamp': { - gte: 'now-30d/m', - lte: 'now/m', - }, - }, - }, - { - term: { - 'slo.budgetingMethod': 'occurrences', - }, - }, - { - term: { - 'slo.timeWindow.type': 'rolling', - }, - }, - { - term: { - 'slo.timeWindow.duration': '30d', - }, - }, - ], - }, - }, - }, - pivot: { - group_by: groupBy, - aggregations: { - goodEvents: { - sum: { - field: 'slo.numerator', - }, - }, - totalEvents: { - sum: { - field: 'slo.denominator', - }, - }, - _objectiveTarget: { - max: { - field: 'slo.objective.target', - }, - }, - sliValue: { - bucket_script: { - buckets_path: { - goodEvents: 'goodEvents', - totalEvents: 'totalEvents', - }, - script: - 'if (params.totalEvents == 0) { return -1 } else if (params.goodEvents >= params.totalEvents) { return 1 } else { return params.goodEvents / params.totalEvents }', - }, - }, - errorBudgetInitial: { - bucket_script: { - buckets_path: { - objectiveTarget: '_objectiveTarget', - }, - script: '1 - params.objectiveTarget', - }, - }, - errorBudgetConsumed: { - bucket_script: { - buckets_path: { - sliValue: 'sliValue', - errorBudgetInitial: 'errorBudgetInitial', - }, - script: - 'if (params.sliValue == -1) { return 0 } else { return (1 - params.sliValue) / params.errorBudgetInitial }', - }, - }, - errorBudgetRemaining: { - bucket_script: { - buckets_path: { - errorBudgetConsummed: 'errorBudgetConsumed', - }, - script: '1 - params.errorBudgetConsummed', - }, - }, - statusCode: { - bucket_script: { - buckets_path: { - sliValue: 'sliValue', - objectiveTarget: '_objectiveTarget', - errorBudgetRemaining: 'errorBudgetRemaining', - }, - script: { - source: - 'if (params.sliValue == -1) { return 0 } else if (params.sliValue >= params.objectiveTarget) { return 4 } else if (params.errorBudgetRemaining > 0) { return 2 } else { return 1 }', - }, - }, - }, - }, - }, - description: - 'Summarize every SLO with occurrences budgeting method and a 30 days rolling time window', - frequency: '1m', - sync: { - time: { - field: '@timestamp', - delay: '125s', - }, - }, - settings: { - deduce_mappings: false, - unattended: true, - }, - _meta: { - version: SLO_SUMMARY_TRANSFORMS_VERSION, - managed: true, - managed_by: 'observability', - }, -}; diff --git a/x-pack/plugins/observability/server/services/slo/summary_transform/templates/summary_occurrences_7d_rolling.ts b/x-pack/plugins/observability/server/services/slo/summary_transform/templates/summary_occurrences_7d_rolling.ts deleted file mode 100644 index 9ceb6cd4290a0..0000000000000 --- a/x-pack/plugins/observability/server/services/slo/summary_transform/templates/summary_occurrences_7d_rolling.ts +++ /dev/null @@ -1,153 +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 { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types'; -import { - SLO_DESTINATION_INDEX_PATTERN, - SLO_SUMMARY_DESTINATION_INDEX_NAME, - SLO_SUMMARY_INGEST_PIPELINE_NAME, - SLO_SUMMARY_TRANSFORMS_VERSION, - SLO_SUMMARY_TRANSFORM_NAME_PREFIX, -} from '../../../../../common/slo/constants'; -import { groupBy } from './common'; - -export const SUMMARY_OCCURRENCES_7D_ROLLING: TransformPutTransformRequest = { - transform_id: `${SLO_SUMMARY_TRANSFORM_NAME_PREFIX}occurrences-7d-rolling`, - dest: { - pipeline: SLO_SUMMARY_INGEST_PIPELINE_NAME, - index: SLO_SUMMARY_DESTINATION_INDEX_NAME, - }, - source: { - index: SLO_DESTINATION_INDEX_PATTERN, - runtime_mappings: { - errorBudgetEstimated: { - type: 'boolean', - script: 'emit(false)', - }, - isTempDoc: { - type: 'boolean', - script: 'emit(false)', - }, - }, - query: { - bool: { - filter: [ - { - range: { - '@timestamp': { - gte: 'now-7d/m', - lte: 'now/m', - }, - }, - }, - { - term: { - 'slo.budgetingMethod': 'occurrences', - }, - }, - { - term: { - 'slo.timeWindow.type': 'rolling', - }, - }, - { - term: { - 'slo.timeWindow.duration': '7d', - }, - }, - ], - }, - }, - }, - pivot: { - group_by: groupBy, - aggregations: { - goodEvents: { - sum: { - field: 'slo.numerator', - }, - }, - totalEvents: { - sum: { - field: 'slo.denominator', - }, - }, - _objectiveTarget: { - max: { - field: 'slo.objective.target', - }, - }, - sliValue: { - bucket_script: { - buckets_path: { - goodEvents: 'goodEvents', - totalEvents: 'totalEvents', - }, - script: - 'if (params.totalEvents == 0) { return -1 } else if (params.goodEvents >= params.totalEvents) { return 1 } else { return params.goodEvents / params.totalEvents }', - }, - }, - errorBudgetInitial: { - bucket_script: { - buckets_path: { - objectiveTarget: '_objectiveTarget', - }, - script: '1 - params.objectiveTarget', - }, - }, - errorBudgetConsumed: { - bucket_script: { - buckets_path: { - sliValue: 'sliValue', - errorBudgetInitial: 'errorBudgetInitial', - }, - script: - 'if (params.sliValue == -1) { return 0 } else { return (1 - params.sliValue) / params.errorBudgetInitial }', - }, - }, - errorBudgetRemaining: { - bucket_script: { - buckets_path: { - errorBudgetConsummed: 'errorBudgetConsumed', - }, - script: '1 - params.errorBudgetConsummed', - }, - }, - statusCode: { - bucket_script: { - buckets_path: { - sliValue: 'sliValue', - objectiveTarget: '_objectiveTarget', - errorBudgetRemaining: 'errorBudgetRemaining', - }, - script: { - source: - 'if (params.sliValue == -1) { return 0 } else if (params.sliValue >= params.objectiveTarget) { return 4 } else if (params.errorBudgetRemaining > 0) { return 2 } else { return 1 }', - }, - }, - }, - }, - }, - description: - 'Summarize every SLO with occurrences budgeting method and a 7 days rolling time window', - frequency: '1m', - sync: { - time: { - field: '@timestamp', - delay: '125s', - }, - }, - settings: { - deduce_mappings: false, - unattended: true, - }, - _meta: { - version: SLO_SUMMARY_TRANSFORMS_VERSION, - managed: true, - managed_by: 'observability', - }, -}; diff --git a/x-pack/plugins/observability/server/services/slo/summary_transform/templates/summary_occurrences_90d_rolling.ts b/x-pack/plugins/observability/server/services/slo/summary_transform/templates/summary_occurrences_90d_rolling.ts deleted file mode 100644 index d0f1729f77225..0000000000000 --- a/x-pack/plugins/observability/server/services/slo/summary_transform/templates/summary_occurrences_90d_rolling.ts +++ /dev/null @@ -1,153 +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 { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types'; -import { - SLO_DESTINATION_INDEX_PATTERN, - SLO_SUMMARY_DESTINATION_INDEX_NAME, - SLO_SUMMARY_INGEST_PIPELINE_NAME, - SLO_SUMMARY_TRANSFORMS_VERSION, - SLO_SUMMARY_TRANSFORM_NAME_PREFIX, -} from '../../../../../common/slo/constants'; -import { groupBy } from './common'; - -export const SUMMARY_OCCURRENCES_90D_ROLLING: TransformPutTransformRequest = { - transform_id: `${SLO_SUMMARY_TRANSFORM_NAME_PREFIX}occurrences-90d-rolling`, - dest: { - index: SLO_SUMMARY_DESTINATION_INDEX_NAME, - pipeline: SLO_SUMMARY_INGEST_PIPELINE_NAME, - }, - source: { - index: SLO_DESTINATION_INDEX_PATTERN, - runtime_mappings: { - errorBudgetEstimated: { - type: 'boolean', - script: 'emit(false)', - }, - isTempDoc: { - type: 'boolean', - script: 'emit(false)', - }, - }, - query: { - bool: { - filter: [ - { - range: { - '@timestamp': { - gte: 'now-90d/m', - lte: 'now/m', - }, - }, - }, - { - term: { - 'slo.budgetingMethod': 'occurrences', - }, - }, - { - term: { - 'slo.timeWindow.type': 'rolling', - }, - }, - { - term: { - 'slo.timeWindow.duration': '90d', - }, - }, - ], - }, - }, - }, - pivot: { - group_by: groupBy, - aggregations: { - goodEvents: { - sum: { - field: 'slo.numerator', - }, - }, - totalEvents: { - sum: { - field: 'slo.denominator', - }, - }, - _objectiveTarget: { - max: { - field: 'slo.objective.target', - }, - }, - sliValue: { - bucket_script: { - buckets_path: { - goodEvents: 'goodEvents', - totalEvents: 'totalEvents', - }, - script: - 'if (params.totalEvents == 0) { return -1 } else if (params.goodEvents >= params.totalEvents) { return 1 } else { return params.goodEvents / params.totalEvents }', - }, - }, - errorBudgetInitial: { - bucket_script: { - buckets_path: { - objectiveTarget: '_objectiveTarget', - }, - script: '1 - params.objectiveTarget', - }, - }, - errorBudgetConsumed: { - bucket_script: { - buckets_path: { - sliValue: 'sliValue', - errorBudgetInitial: 'errorBudgetInitial', - }, - script: - 'if (params.sliValue == -1) { return 0 } else { return (1 - params.sliValue) / params.errorBudgetInitial }', - }, - }, - errorBudgetRemaining: { - bucket_script: { - buckets_path: { - errorBudgetConsummed: 'errorBudgetConsumed', - }, - script: '1 - params.errorBudgetConsummed', - }, - }, - statusCode: { - bucket_script: { - buckets_path: { - sliValue: 'sliValue', - objectiveTarget: '_objectiveTarget', - errorBudgetRemaining: 'errorBudgetRemaining', - }, - script: { - source: - 'if (params.sliValue == -1) { return 0 } else if (params.sliValue >= params.objectiveTarget) { return 4 } else if (params.errorBudgetRemaining > 0) { return 2 } else { return 1 }', - }, - }, - }, - }, - }, - description: - 'Summarize every SLO with occurrences budgeting method and a 90 days rolling time window', - frequency: '1m', - sync: { - time: { - field: '@timestamp', - delay: '125s', - }, - }, - settings: { - deduce_mappings: false, - unattended: true, - }, - _meta: { - version: SLO_SUMMARY_TRANSFORMS_VERSION, - managed: true, - managed_by: 'observability', - }, -}; diff --git a/x-pack/plugins/observability/server/services/slo/summary_transform/templates/summary_occurrences_monthly_aligned.ts b/x-pack/plugins/observability/server/services/slo/summary_transform/templates/summary_occurrences_monthly_aligned.ts deleted file mode 100644 index a5b2a70932a5e..0000000000000 --- a/x-pack/plugins/observability/server/services/slo/summary_transform/templates/summary_occurrences_monthly_aligned.ts +++ /dev/null @@ -1,151 +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 { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types'; -import { - SLO_DESTINATION_INDEX_PATTERN, - SLO_SUMMARY_DESTINATION_INDEX_NAME, - SLO_SUMMARY_INGEST_PIPELINE_NAME, - SLO_SUMMARY_TRANSFORMS_VERSION, - SLO_SUMMARY_TRANSFORM_NAME_PREFIX, -} from '../../../../../common/slo/constants'; -import { groupBy } from './common'; - -export const SUMMARY_OCCURRENCES_MONTHLY_ALIGNED: TransformPutTransformRequest = { - transform_id: `${SLO_SUMMARY_TRANSFORM_NAME_PREFIX}occurrences-monthly-aligned`, - dest: { - index: SLO_SUMMARY_DESTINATION_INDEX_NAME, - pipeline: SLO_SUMMARY_INGEST_PIPELINE_NAME, - }, - source: { - index: SLO_DESTINATION_INDEX_PATTERN, - runtime_mappings: { - errorBudgetEstimated: { - type: 'boolean', - script: 'emit(true)', - }, - isTempDoc: { - type: 'boolean', - script: 'emit(false)', - }, - }, - query: { - bool: { - filter: [ - { - range: { - '@timestamp': { - gte: 'now/M', - lte: 'now/m', - }, - }, - }, - { - term: { - 'slo.budgetingMethod': 'occurrences', - }, - }, - { - term: { - 'slo.timeWindow.type': 'calendarAligned', - }, - }, - { - term: { - 'slo.timeWindow.duration': '1M', - }, - }, - ], - }, - }, - }, - pivot: { - group_by: groupBy, - aggregations: { - _objectiveTarget: { - max: { - field: 'slo.objective.target', - }, - }, - goodEvents: { - sum: { - field: 'slo.numerator', - }, - }, - totalEvents: { - sum: { - field: 'slo.denominator', - }, - }, - sliValue: { - bucket_script: { - buckets_path: { - goodEvents: 'goodEvents', - totalEvents: 'totalEvents', - }, - script: - 'if (params.totalEvents == 0) { return -1 } else if (params.goodEvents >= params.totalEvents) { return 1 } else { return params.goodEvents / params.totalEvents }', - }, - }, - errorBudgetInitial: { - bucket_script: { - buckets_path: { - objective: '_objectiveTarget', - }, - script: '1 - params.objective', - }, - }, - errorBudgetConsumed: { - bucket_script: { - buckets_path: { - sliValue: 'sliValue', - errorBudgetInitial: 'errorBudgetInitial', - }, - script: - 'if (params.sliValue == -1) { return 0 } else { return (1 - params.sliValue) / params.errorBudgetInitial }', - }, - }, - errorBudgetRemaining: { - bucket_script: { - buckets_path: { - errorBudgetConsumed: 'errorBudgetConsumed', - }, - script: '1 - params.errorBudgetConsumed', - }, - }, - statusCode: { - bucket_script: { - buckets_path: { - sliValue: 'sliValue', - objective: '_objectiveTarget', - errorBudgetRemaining: 'errorBudgetRemaining', - }, - script: - 'if (params.sliValue == -1) { return 0 } else if (params.sliValue >= params.objective) { return 4 } else if (params.errorBudgetRemaining > 0) { return 2 } else { return 1 }', - }, - }, - }, - }, - description: - 'Summarize every SLO with occurrences budgeting method and a monthly calendar aligned time window', - frequency: '1m', - sync: { - time: { - field: '@timestamp', - delay: '125s', - }, - }, - settings: { - deduce_mappings: false, - unattended: true, - }, - _meta: { - version: SLO_SUMMARY_TRANSFORMS_VERSION, - managed: true, - managed_by: 'observability', - }, -}; diff --git a/x-pack/plugins/observability/server/services/slo/summary_transform/templates/summary_occurrences_weekly_aligned.ts b/x-pack/plugins/observability/server/services/slo/summary_transform/templates/summary_occurrences_weekly_aligned.ts deleted file mode 100644 index 43ed92704c119..0000000000000 --- a/x-pack/plugins/observability/server/services/slo/summary_transform/templates/summary_occurrences_weekly_aligned.ts +++ /dev/null @@ -1,151 +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 { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types'; -import { - SLO_DESTINATION_INDEX_PATTERN, - SLO_SUMMARY_DESTINATION_INDEX_NAME, - SLO_SUMMARY_INGEST_PIPELINE_NAME, - SLO_SUMMARY_TRANSFORMS_VERSION, - SLO_SUMMARY_TRANSFORM_NAME_PREFIX, -} from '../../../../../common/slo/constants'; -import { groupBy } from './common'; - -export const SUMMARY_OCCURRENCES_WEEKLY_ALIGNED: TransformPutTransformRequest = { - transform_id: `${SLO_SUMMARY_TRANSFORM_NAME_PREFIX}occurrences-weekly-aligned`, - dest: { - index: SLO_SUMMARY_DESTINATION_INDEX_NAME, - pipeline: SLO_SUMMARY_INGEST_PIPELINE_NAME, - }, - source: { - index: SLO_DESTINATION_INDEX_PATTERN, - runtime_mappings: { - errorBudgetEstimated: { - type: 'boolean', - script: 'emit(true)', - }, - isTempDoc: { - type: 'boolean', - script: 'emit(false)', - }, - }, - query: { - bool: { - filter: [ - { - range: { - '@timestamp': { - gte: 'now/w', - lte: 'now/m', - }, - }, - }, - { - term: { - 'slo.budgetingMethod': 'occurrences', - }, - }, - { - term: { - 'slo.timeWindow.type': 'calendarAligned', - }, - }, - { - term: { - 'slo.timeWindow.duration': '1w', - }, - }, - ], - }, - }, - }, - pivot: { - group_by: groupBy, - aggregations: { - _objectiveTarget: { - max: { - field: 'slo.objective.target', - }, - }, - goodEvents: { - sum: { - field: 'slo.numerator', - }, - }, - totalEvents: { - sum: { - field: 'slo.denominator', - }, - }, - sliValue: { - bucket_script: { - buckets_path: { - goodEvents: 'goodEvents', - totalEvents: 'totalEvents', - }, - script: - 'if (params.totalEvents == 0) { return -1 } else if (params.goodEvents >= params.totalEvents) { return 1 } else { return params.goodEvents / params.totalEvents }', - }, - }, - errorBudgetInitial: { - bucket_script: { - buckets_path: { - objective: '_objectiveTarget', - }, - script: '1 - params.objective', - }, - }, - errorBudgetConsumed: { - bucket_script: { - buckets_path: { - sliValue: 'sliValue', - errorBudgetInitial: 'errorBudgetInitial', - }, - script: - 'if (params.sliValue == -1) { return 0 } else { return (1 - params.sliValue) / params.errorBudgetInitial }', - }, - }, - errorBudgetRemaining: { - bucket_script: { - buckets_path: { - errorBudgetConsumed: 'errorBudgetConsumed', - }, - script: '1 - params.errorBudgetConsumed', - }, - }, - statusCode: { - bucket_script: { - buckets_path: { - sliValue: 'sliValue', - objective: '_objectiveTarget', - errorBudgetRemaining: 'errorBudgetRemaining', - }, - script: - 'if (params.sliValue == -1) { return 0 } else if (params.sliValue >= params.objective) { return 4 } else if (params.errorBudgetRemaining > 0) { return 2 } else { return 1 }', - }, - }, - }, - }, - description: - 'Summarize every SLO with occurrences budgeting method and a weekly calendar aligned time window', - frequency: '1m', - sync: { - time: { - field: '@timestamp', - delay: '125s', - }, - }, - settings: { - deduce_mappings: false, - unattended: true, - }, - _meta: { - version: SLO_SUMMARY_TRANSFORMS_VERSION, - managed: true, - managed_by: 'observability', - }, -}; diff --git a/x-pack/plugins/observability/server/services/slo/summary_transform/templates/summary_timeslices_30d_rolling.ts b/x-pack/plugins/observability/server/services/slo/summary_transform/templates/summary_timeslices_30d_rolling.ts deleted file mode 100644 index 5d1c6c48f8f1f..0000000000000 --- a/x-pack/plugins/observability/server/services/slo/summary_transform/templates/summary_timeslices_30d_rolling.ts +++ /dev/null @@ -1,153 +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 { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types'; -import { - SLO_DESTINATION_INDEX_PATTERN, - SLO_SUMMARY_DESTINATION_INDEX_NAME, - SLO_SUMMARY_INGEST_PIPELINE_NAME, - SLO_SUMMARY_TRANSFORMS_VERSION, - SLO_SUMMARY_TRANSFORM_NAME_PREFIX, -} from '../../../../../common/slo/constants'; -import { groupBy } from './common'; - -export const SUMMARY_TIMESLICES_30D_ROLLING: TransformPutTransformRequest = { - transform_id: `${SLO_SUMMARY_TRANSFORM_NAME_PREFIX}timeslices-30d-rolling`, - dest: { - index: SLO_SUMMARY_DESTINATION_INDEX_NAME, - pipeline: SLO_SUMMARY_INGEST_PIPELINE_NAME, - }, - source: { - index: SLO_DESTINATION_INDEX_PATTERN, - runtime_mappings: { - errorBudgetEstimated: { - type: 'boolean', - script: 'emit(false)', - }, - isTempDoc: { - type: 'boolean', - script: 'emit(false)', - }, - }, - query: { - bool: { - filter: [ - { - range: { - '@timestamp': { - gte: 'now-30d/m', - lte: 'now/m', - }, - }, - }, - { - term: { - 'slo.budgetingMethod': 'timeslices', - }, - }, - { - term: { - 'slo.timeWindow.type': 'rolling', - }, - }, - { - term: { - 'slo.timeWindow.duration': '30d', - }, - }, - ], - }, - }, - }, - pivot: { - group_by: groupBy, - aggregations: { - goodEvents: { - sum: { - field: 'slo.isGoodSlice', - }, - }, - totalEvents: { - value_count: { - field: 'slo.isGoodSlice', - }, - }, - _objectiveTarget: { - max: { - field: 'slo.objective.target', - }, - }, - sliValue: { - bucket_script: { - buckets_path: { - goodEvents: 'goodEvents', - totalEvents: 'totalEvents', - }, - script: - 'if (params.totalEvents == 0) { return -1 } else if (params.goodEvents >= params.totalEvents) { return 1 } else { return params.goodEvents / params.totalEvents }', - }, - }, - errorBudgetInitial: { - bucket_script: { - buckets_path: { - objectiveTarget: '_objectiveTarget', - }, - script: '1 - params.objectiveTarget', - }, - }, - errorBudgetConsumed: { - bucket_script: { - buckets_path: { - sliValue: 'sliValue', - errorBudgetInitial: 'errorBudgetInitial', - }, - script: - 'if (params.sliValue == -1) { return 0 } else { return (1 - params.sliValue) / params.errorBudgetInitial }', - }, - }, - errorBudgetRemaining: { - bucket_script: { - buckets_path: { - errorBudgetConsummed: 'errorBudgetConsumed', - }, - script: '1 - params.errorBudgetConsummed', - }, - }, - statusCode: { - bucket_script: { - buckets_path: { - sliValue: 'sliValue', - objectiveTarget: '_objectiveTarget', - errorBudgetRemaining: 'errorBudgetRemaining', - }, - script: { - source: - 'if (params.sliValue == -1) { return 0 } else if (params.sliValue >= params.objectiveTarget) { return 4 } else if (params.errorBudgetRemaining > 0) { return 2 } else { return 1 }', - }, - }, - }, - }, - }, - description: - 'Summarize every SLO with timeslices budgeting method and a 30 days rolling time window', - frequency: '1m', - sync: { - time: { - field: '@timestamp', - delay: '125s', - }, - }, - settings: { - deduce_mappings: false, - unattended: true, - }, - _meta: { - version: SLO_SUMMARY_TRANSFORMS_VERSION, - managed: true, - managed_by: 'observability', - }, -}; diff --git a/x-pack/plugins/observability/server/services/slo/summary_transform/templates/summary_timeslices_7d_rolling.ts b/x-pack/plugins/observability/server/services/slo/summary_transform/templates/summary_timeslices_7d_rolling.ts deleted file mode 100644 index a9256955ac08a..0000000000000 --- a/x-pack/plugins/observability/server/services/slo/summary_transform/templates/summary_timeslices_7d_rolling.ts +++ /dev/null @@ -1,153 +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 { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types'; -import { - SLO_DESTINATION_INDEX_PATTERN, - SLO_SUMMARY_DESTINATION_INDEX_NAME, - SLO_SUMMARY_INGEST_PIPELINE_NAME, - SLO_SUMMARY_TRANSFORMS_VERSION, - SLO_SUMMARY_TRANSFORM_NAME_PREFIX, -} from '../../../../../common/slo/constants'; -import { groupBy } from './common'; - -export const SUMMARY_TIMESLICES_7D_ROLLING: TransformPutTransformRequest = { - transform_id: `${SLO_SUMMARY_TRANSFORM_NAME_PREFIX}timeslices-7d-rolling`, - dest: { - index: SLO_SUMMARY_DESTINATION_INDEX_NAME, - pipeline: SLO_SUMMARY_INGEST_PIPELINE_NAME, - }, - source: { - index: SLO_DESTINATION_INDEX_PATTERN, - runtime_mappings: { - errorBudgetEstimated: { - type: 'boolean', - script: 'emit(false)', - }, - isTempDoc: { - type: 'boolean', - script: 'emit(false)', - }, - }, - query: { - bool: { - filter: [ - { - range: { - '@timestamp': { - gte: 'now-7d/m', - lte: 'now/m', - }, - }, - }, - { - term: { - 'slo.budgetingMethod': 'timeslices', - }, - }, - { - term: { - 'slo.timeWindow.type': 'rolling', - }, - }, - { - term: { - 'slo.timeWindow.duration': '7d', - }, - }, - ], - }, - }, - }, - pivot: { - group_by: groupBy, - aggregations: { - goodEvents: { - sum: { - field: 'slo.isGoodSlice', - }, - }, - totalEvents: { - value_count: { - field: 'slo.isGoodSlice', - }, - }, - _objectiveTarget: { - max: { - field: 'slo.objective.target', - }, - }, - sliValue: { - bucket_script: { - buckets_path: { - goodEvents: 'goodEvents', - totalEvents: 'totalEvents', - }, - script: - 'if (params.totalEvents == 0) { return -1 } else if (params.goodEvents >= params.totalEvents) { return 1 } else { return params.goodEvents / params.totalEvents }', - }, - }, - errorBudgetInitial: { - bucket_script: { - buckets_path: { - objectiveTarget: '_objectiveTarget', - }, - script: '1 - params.objectiveTarget', - }, - }, - errorBudgetConsumed: { - bucket_script: { - buckets_path: { - sliValue: 'sliValue', - errorBudgetInitial: 'errorBudgetInitial', - }, - script: - 'if (params.sliValue == -1) { return 0 } else { return (1 - params.sliValue) / params.errorBudgetInitial }', - }, - }, - errorBudgetRemaining: { - bucket_script: { - buckets_path: { - errorBudgetConsummed: 'errorBudgetConsumed', - }, - script: '1 - params.errorBudgetConsummed', - }, - }, - statusCode: { - bucket_script: { - buckets_path: { - sliValue: 'sliValue', - objectiveTarget: '_objectiveTarget', - errorBudgetRemaining: 'errorBudgetRemaining', - }, - script: { - source: - 'if (params.sliValue == -1) { return 0 } else if (params.sliValue >= params.objectiveTarget) { return 4 } else if (params.errorBudgetRemaining > 0) { return 2 } else { return 1 }', - }, - }, - }, - }, - }, - description: - 'Summarize every SLO with timeslices budgeting method and a 7 days rolling time window', - frequency: '1m', - sync: { - time: { - field: '@timestamp', - delay: '125s', - }, - }, - settings: { - deduce_mappings: false, - unattended: true, - }, - _meta: { - version: SLO_SUMMARY_TRANSFORMS_VERSION, - managed: true, - managed_by: 'observability', - }, -}; diff --git a/x-pack/plugins/observability/server/services/slo/summary_transform/templates/summary_timeslices_90d_rolling.ts b/x-pack/plugins/observability/server/services/slo/summary_transform/templates/summary_timeslices_90d_rolling.ts deleted file mode 100644 index f922bd210e253..0000000000000 --- a/x-pack/plugins/observability/server/services/slo/summary_transform/templates/summary_timeslices_90d_rolling.ts +++ /dev/null @@ -1,153 +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 { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types'; -import { - SLO_DESTINATION_INDEX_PATTERN, - SLO_SUMMARY_DESTINATION_INDEX_NAME, - SLO_SUMMARY_INGEST_PIPELINE_NAME, - SLO_SUMMARY_TRANSFORMS_VERSION, - SLO_SUMMARY_TRANSFORM_NAME_PREFIX, -} from '../../../../../common/slo/constants'; -import { groupBy } from './common'; - -export const SUMMARY_TIMESLICES_90D_ROLLING: TransformPutTransformRequest = { - transform_id: `${SLO_SUMMARY_TRANSFORM_NAME_PREFIX}timeslices-90d-rolling`, - dest: { - index: SLO_SUMMARY_DESTINATION_INDEX_NAME, - pipeline: SLO_SUMMARY_INGEST_PIPELINE_NAME, - }, - source: { - index: SLO_DESTINATION_INDEX_PATTERN, - runtime_mappings: { - errorBudgetEstimated: { - type: 'boolean', - script: 'emit(false)', - }, - isTempDoc: { - type: 'boolean', - script: 'emit(false)', - }, - }, - query: { - bool: { - filter: [ - { - range: { - '@timestamp': { - gte: 'now-90d/m', - lte: 'now/m', - }, - }, - }, - { - term: { - 'slo.budgetingMethod': 'timeslices', - }, - }, - { - term: { - 'slo.timeWindow.type': 'rolling', - }, - }, - { - term: { - 'slo.timeWindow.duration': '90d', - }, - }, - ], - }, - }, - }, - pivot: { - group_by: groupBy, - aggregations: { - goodEvents: { - sum: { - field: 'slo.isGoodSlice', - }, - }, - totalEvents: { - value_count: { - field: 'slo.isGoodSlice', - }, - }, - _objectiveTarget: { - max: { - field: 'slo.objective.target', - }, - }, - sliValue: { - bucket_script: { - buckets_path: { - goodEvents: 'goodEvents', - totalEvents: 'totalEvents', - }, - script: - 'if (params.totalEvents == 0) { return -1 } else if (params.goodEvents >= params.totalEvents) { return 1 } else { return params.goodEvents / params.totalEvents }', - }, - }, - errorBudgetInitial: { - bucket_script: { - buckets_path: { - objectiveTarget: '_objectiveTarget', - }, - script: '1 - params.objectiveTarget', - }, - }, - errorBudgetConsumed: { - bucket_script: { - buckets_path: { - sliValue: 'sliValue', - errorBudgetInitial: 'errorBudgetInitial', - }, - script: - 'if (params.sliValue == -1) { return 0 } else { return (1 - params.sliValue) / params.errorBudgetInitial }', - }, - }, - errorBudgetRemaining: { - bucket_script: { - buckets_path: { - errorBudgetConsummed: 'errorBudgetConsumed', - }, - script: '1 - params.errorBudgetConsummed', - }, - }, - statusCode: { - bucket_script: { - buckets_path: { - sliValue: 'sliValue', - objectiveTarget: '_objectiveTarget', - errorBudgetRemaining: 'errorBudgetRemaining', - }, - script: { - source: - 'if (params.sliValue == -1) { return 0 } else if (params.sliValue >= params.objectiveTarget) { return 4 } else if (params.errorBudgetRemaining > 0) { return 2 } else { return 1 }', - }, - }, - }, - }, - }, - description: - 'Summarize every SLO with timeslices budgeting method and a 90 days rolling time window', - frequency: '1m', - sync: { - time: { - field: '@timestamp', - delay: '125s', - }, - }, - settings: { - deduce_mappings: false, - unattended: true, - }, - _meta: { - version: SLO_SUMMARY_TRANSFORMS_VERSION, - managed: true, - managed_by: 'observability', - }, -}; diff --git a/x-pack/plugins/observability/server/services/slo/summary_transform/templates/summary_timeslices_monthly_aligned.ts b/x-pack/plugins/observability/server/services/slo/summary_transform/templates/summary_timeslices_monthly_aligned.ts deleted file mode 100644 index 3b39d9acd3372..0000000000000 --- a/x-pack/plugins/observability/server/services/slo/summary_transform/templates/summary_timeslices_monthly_aligned.ts +++ /dev/null @@ -1,181 +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 { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types'; -import { - SLO_DESTINATION_INDEX_PATTERN, - SLO_SUMMARY_DESTINATION_INDEX_NAME, - SLO_SUMMARY_INGEST_PIPELINE_NAME, - SLO_SUMMARY_TRANSFORMS_VERSION, - SLO_SUMMARY_TRANSFORM_NAME_PREFIX, -} from '../../../../../common/slo/constants'; -import { groupBy } from './common'; - -export const SUMMARY_TIMESLICES_MONTHLY_ALIGNED: TransformPutTransformRequest = { - transform_id: `${SLO_SUMMARY_TRANSFORM_NAME_PREFIX}timeslices-monthly-aligned`, - dest: { - index: SLO_SUMMARY_DESTINATION_INDEX_NAME, - pipeline: SLO_SUMMARY_INGEST_PIPELINE_NAME, - }, - source: { - index: SLO_DESTINATION_INDEX_PATTERN, - runtime_mappings: { - errorBudgetEstimated: { - type: 'boolean', - script: 'emit(false)', - }, - isTempDoc: { - type: 'boolean', - script: 'emit(false)', - }, - }, - query: { - bool: { - filter: [ - { - range: { - '@timestamp': { - gte: 'now/M', - lte: 'now/m', - }, - }, - }, - { - term: { - 'slo.budgetingMethod': 'timeslices', - }, - }, - { - term: { - 'slo.timeWindow.type': 'calendarAligned', - }, - }, - { - term: { - 'slo.timeWindow.duration': '1M', - }, - }, - ], - }, - }, - }, - pivot: { - group_by: groupBy, - aggregations: { - _sliceDurationInSeconds: { - max: { - field: 'slo.objective.sliceDurationInSeconds', - }, - }, - _totalSlicesInPeriod: { - bucket_script: { - buckets_path: { - sliceDurationInSeconds: '_sliceDurationInSeconds', - }, - script: { - source: ` - Date d = new Date(); - Instant instant = Instant.ofEpochMilli(d.getTime()); - LocalDateTime now = LocalDateTime.ofInstant(instant, ZoneOffset.UTC); - LocalDateTime startOfMonth = now - .withDayOfMonth(1) - .withHour(0) - .withMinute(0) - .withSecond(0); - LocalDateTime startOfNextMonth = startOfMonth.plusMonths(1); - double sliceDurationInMinutes = params.sliceDurationInSeconds / 60; - - return Math.ceil(Duration.between(startOfMonth, startOfNextMonth).toMinutes() / sliceDurationInMinutes); - `, - }, - }, - }, - _objectiveTarget: { - max: { - field: 'slo.objective.target', - }, - }, - goodEvents: { - sum: { - field: 'slo.isGoodSlice', - }, - }, - totalEvents: { - value_count: { - field: 'slo.isGoodSlice', - }, - }, - sliValue: { - bucket_script: { - buckets_path: { - goodEvents: 'goodEvents', - totalEvents: 'totalEvents', - }, - script: - 'if (params.totalEvents == 0) { return -1 } else if (params.goodEvents >= params.totalEvents) { return 1 } else { return params.goodEvents / params.totalEvents }', - }, - }, - errorBudgetInitial: { - bucket_script: { - buckets_path: { - objective: '_objectiveTarget', - }, - script: '1 - params.objective', - }, - }, - errorBudgetConsumed: { - bucket_script: { - buckets_path: { - goodEvents: 'goodEvents', - totalEvents: 'totalEvents', - totalSlicesInPeriod: '_totalSlicesInPeriod', - errorBudgetInitial: 'errorBudgetInitial', - }, - script: - 'if (params.totalEvents == 0) { return 0 } else { return (params.totalEvents - params.goodEvents) / (params.totalSlicesInPeriod * params.errorBudgetInitial) }', - }, - }, - errorBudgetRemaining: { - bucket_script: { - buckets_path: { - errorBudgetConsumed: 'errorBudgetConsumed', - }, - script: '1 - params.errorBudgetConsumed', - }, - }, - statusCode: { - bucket_script: { - buckets_path: { - sliValue: 'sliValue', - objective: '_objectiveTarget', - errorBudgetRemaining: 'errorBudgetRemaining', - }, - script: - 'if (params.sliValue == -1) { return 0 } else if (params.sliValue >= params.objective) { return 4 } else if (params.errorBudgetRemaining > 0) { return 2 } else { return 1 }', - }, - }, - }, - }, - description: - 'Summarize every SLO with timeslices budgeting method and a monthly calendar aligned time window', - frequency: '1m', - sync: { - time: { - field: '@timestamp', - delay: '125s', - }, - }, - settings: { - deduce_mappings: false, - unattended: true, - }, - _meta: { - version: SLO_SUMMARY_TRANSFORMS_VERSION, - managed: true, - managed_by: 'observability', - }, -}; diff --git a/x-pack/plugins/observability/server/services/slo/summary_transform/templates/summary_timeslices_weekly_aligned.ts b/x-pack/plugins/observability/server/services/slo/summary_transform/templates/summary_timeslices_weekly_aligned.ts deleted file mode 100644 index 3cae5f9bcd9b1..0000000000000 --- a/x-pack/plugins/observability/server/services/slo/summary_transform/templates/summary_timeslices_weekly_aligned.ts +++ /dev/null @@ -1,166 +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 { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types'; -import { - SLO_DESTINATION_INDEX_PATTERN, - SLO_SUMMARY_DESTINATION_INDEX_NAME, - SLO_SUMMARY_INGEST_PIPELINE_NAME, - SLO_SUMMARY_TRANSFORMS_VERSION, - SLO_SUMMARY_TRANSFORM_NAME_PREFIX, -} from '../../../../../common/slo/constants'; -import { groupBy } from './common'; - -export const SUMMARY_TIMESLICES_WEEKLY_ALIGNED: TransformPutTransformRequest = { - transform_id: `${SLO_SUMMARY_TRANSFORM_NAME_PREFIX}timeslices-weekly-aligned`, - dest: { - index: SLO_SUMMARY_DESTINATION_INDEX_NAME, - pipeline: SLO_SUMMARY_INGEST_PIPELINE_NAME, - }, - source: { - index: SLO_DESTINATION_INDEX_PATTERN, - runtime_mappings: { - errorBudgetEstimated: { - type: 'boolean', - script: 'emit(false)', - }, - isTempDoc: { - type: 'boolean', - script: 'emit(false)', - }, - }, - query: { - bool: { - filter: [ - { - range: { - '@timestamp': { - gte: 'now/w', - lte: 'now/m', - }, - }, - }, - { - term: { - 'slo.budgetingMethod': 'timeslices', - }, - }, - { - term: { - 'slo.timeWindow.type': 'calendarAligned', - }, - }, - { - term: { - 'slo.timeWindow.duration': '1w', - }, - }, - ], - }, - }, - }, - pivot: { - group_by: groupBy, - aggregations: { - _sliceDurationInSeconds: { - max: { - field: 'slo.objective.sliceDurationInSeconds', - }, - }, - _totalSlicesInPeriod: { - bucket_script: { - buckets_path: { - sliceDurationInSeconds: '_sliceDurationInSeconds', - }, - script: 'Math.ceil(7 * 24 * 60 * 60 / params.sliceDurationInSeconds)', - }, - }, - _objectiveTarget: { - max: { - field: 'slo.objective.target', - }, - }, - goodEvents: { - sum: { - field: 'slo.isGoodSlice', - }, - }, - totalEvents: { - value_count: { - field: 'slo.isGoodSlice', - }, - }, - sliValue: { - bucket_script: { - buckets_path: { - goodEvents: 'goodEvents', - totalEvents: 'totalEvents', - }, - script: - 'if (params.totalEvents == 0) { return -1 } else if (params.goodEvents >= params.totalEvents) { return 1 } else { return params.goodEvents / params.totalEvents }', - }, - }, - errorBudgetInitial: { - bucket_script: { - buckets_path: { - objective: '_objectiveTarget', - }, - script: '1 - params.objective', - }, - }, - errorBudgetConsumed: { - bucket_script: { - buckets_path: { - goodEvents: 'goodEvents', - totalEvents: 'totalEvents', - totalSlicesInPeriod: '_totalSlicesInPeriod', - errorBudgetInitial: 'errorBudgetInitial', - }, - script: - 'if (params.totalEvents == 0) { return 0 } else { return (params.totalEvents - params.goodEvents) / (params.totalSlicesInPeriod * params.errorBudgetInitial) }', - }, - }, - errorBudgetRemaining: { - bucket_script: { - buckets_path: { - errorBudgetConsumed: 'errorBudgetConsumed', - }, - script: '1 - params.errorBudgetConsumed', - }, - }, - statusCode: { - bucket_script: { - buckets_path: { - sliValue: 'sliValue', - objective: '_objectiveTarget', - errorBudgetRemaining: 'errorBudgetRemaining', - }, - script: - 'if (params.sliValue == -1) { return 0 } else if (params.sliValue >= params.objective) { return 4 } else if (params.errorBudgetRemaining > 0) { return 2 } else { return 1 }', - }, - }, - }, - }, - description: - 'Summarize every SLO with timeslices budgeting method and a weekly calendar aligned time window', - frequency: '1m', - sync: { - time: { - field: '@timestamp', - delay: '125s', - }, - }, - settings: { - deduce_mappings: false, - unattended: true, - }, - _meta: { - version: SLO_SUMMARY_TRANSFORMS_VERSION, - managed: true, - managed_by: 'observability', - }, -}; diff --git a/x-pack/plugins/observability/server/services/slo/summary_transform_generator/generators/common.ts b/x-pack/plugins/observability/server/services/slo/summary_transform_generator/generators/common.ts new file mode 100644 index 0000000000000..743ba333c8f98 --- /dev/null +++ b/x-pack/plugins/observability/server/services/slo/summary_transform_generator/generators/common.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 { ALL_VALUE } from '@kbn/slo-schema'; +import { SLO } from '../../../../domain/models/slo'; + +export const getGroupBy = (slo: SLO) => { + const groupings = + slo.groupBy !== '' && slo.groupBy !== ALL_VALUE + ? [slo.groupBy].flat().reduce((acc, field) => { + return { + ...acc, + [`slo.groupings.${field}`]: { + terms: { + field: `slo.groupings.${field}`, + }, + }, + }; + }, {}) + : {}; + + return { + 'slo.id': { + terms: { + field: 'slo.id', + }, + }, + 'slo.revision': { + terms: { + field: 'slo.revision', + }, + }, + 'slo.instanceId': { + terms: { + field: 'slo.instanceId', + }, + }, + ...groupings, + // optional fields: only specified for APM indicators. Must include missing_bucket:true + 'service.name': { + terms: { + field: 'service.name', + missing_bucket: true, + }, + }, + 'service.environment': { + terms: { + field: 'service.environment', + missing_bucket: true, + }, + }, + 'transaction.name': { + terms: { + field: 'transaction.name', + missing_bucket: true, + }, + }, + 'transaction.type': { + terms: { + field: 'transaction.type', + missing_bucket: true, + }, + }, + }; +}; diff --git a/x-pack/plugins/observability/server/services/slo/summary_transform_generator/generators/occurrences.ts b/x-pack/plugins/observability/server/services/slo/summary_transform_generator/generators/occurrences.ts new file mode 100644 index 0000000000000..2647e296f5544 --- /dev/null +++ b/x-pack/plugins/observability/server/services/slo/summary_transform_generator/generators/occurrences.ts @@ -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 { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types'; +import { SLO } from '../../../../domain/models'; +import { + getSLOSummaryPipelineId, + getSLOSummaryTransformId, + SLO_DESTINATION_INDEX_PATTERN, + SLO_RESOURCES_VERSION, + SLO_SUMMARY_DESTINATION_INDEX_NAME, +} from '../../../../../common/slo/constants'; +import { getGroupBy } from './common'; + +export function generateSummaryTransformForOccurrences(slo: SLO): TransformPutTransformRequest { + return { + transform_id: getSLOSummaryTransformId(slo.id, slo.revision), + dest: { + pipeline: getSLOSummaryPipelineId(slo.id, slo.revision), + index: SLO_SUMMARY_DESTINATION_INDEX_NAME, + }, + source: { + index: SLO_DESTINATION_INDEX_PATTERN, + query: { + bool: { + filter: [ + { + range: { + '@timestamp': { + gte: `now-${slo.timeWindow.duration.format()}/m`, + lte: 'now/m', + }, + }, + }, + { + term: { + 'slo.id': slo.id, + }, + }, + { + term: { + 'slo.revision': slo.revision, + }, + }, + ], + }, + }, + }, + pivot: { + group_by: getGroupBy(slo), + aggregations: { + goodEvents: { + sum: { + field: 'slo.numerator', + }, + }, + totalEvents: { + sum: { + field: 'slo.denominator', + }, + }, + sliValue: { + bucket_script: { + buckets_path: { + goodEvents: 'goodEvents', + totalEvents: 'totalEvents', + }, + script: + 'if (params.totalEvents == 0) { return -1 } else if (params.goodEvents >= params.totalEvents) { return 1 } else { return params.goodEvents / params.totalEvents }', + }, + }, + errorBudgetInitial: { + bucket_script: { + buckets_path: {}, + script: `1 - ${slo.objective.target}`, + }, + }, + errorBudgetConsumed: { + bucket_script: { + buckets_path: { + sliValue: 'sliValue', + errorBudgetInitial: 'errorBudgetInitial', + }, + script: + 'if (params.sliValue == -1) { return 0 } else { return (1 - params.sliValue) / params.errorBudgetInitial }', + }, + }, + errorBudgetRemaining: { + bucket_script: { + buckets_path: { + errorBudgetConsumed: 'errorBudgetConsumed', + }, + script: '1 - params.errorBudgetConsumed', + }, + }, + statusCode: { + bucket_script: { + buckets_path: { + sliValue: 'sliValue', + errorBudgetRemaining: 'errorBudgetRemaining', + }, + script: { + source: `if (params.sliValue == -1) { return 0 } else if (params.sliValue >= ${slo.objective.target}) { return 4 } else if (params.errorBudgetRemaining > 0) { return 2 } else { return 1 }`, + }, + }, + }, + latestSliTimestamp: { + max: { + field: '@timestamp', + }, + }, + }, + }, + description: `Summarise the rollup data of SLO: ${slo.name} [id: ${slo.id}, revision: ${slo.revision}].`, + frequency: '1m', + sync: { + time: { + field: 'event.ingested', + delay: '65s', + }, + }, + settings: { + deduce_mappings: false, + unattended: true, + }, + _meta: { + version: SLO_RESOURCES_VERSION, + managed: true, + managed_by: 'observability', + }, + }; +} diff --git a/x-pack/plugins/observability/server/services/slo/summary_transform_generator/generators/timeslices_calendar_aligned.ts b/x-pack/plugins/observability/server/services/slo/summary_transform_generator/generators/timeslices_calendar_aligned.ts new file mode 100644 index 0000000000000..6f5c27e5f869a --- /dev/null +++ b/x-pack/plugins/observability/server/services/slo/summary_transform_generator/generators/timeslices_calendar_aligned.ts @@ -0,0 +1,166 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types'; +import { DurationUnit, SLO } from '../../../../domain/models'; +import { + getSLOSummaryPipelineId, + getSLOSummaryTransformId, + SLO_DESTINATION_INDEX_PATTERN, + SLO_RESOURCES_VERSION, + SLO_SUMMARY_DESTINATION_INDEX_NAME, +} from '../../../../../common/slo/constants'; +import { getGroupBy } from './common'; + +export function generateSummaryTransformForTimeslicesAndCalendarAligned( + slo: SLO +): TransformPutTransformRequest { + const isWeeklyAligned = slo.timeWindow.duration.unit === DurationUnit.Week; + const sliceDurationInSeconds = slo.objective.timesliceWindow!.asSeconds(); + + return { + transform_id: getSLOSummaryTransformId(slo.id, slo.revision), + dest: { + pipeline: getSLOSummaryPipelineId(slo.id, slo.revision), + index: SLO_SUMMARY_DESTINATION_INDEX_NAME, + }, + source: { + index: SLO_DESTINATION_INDEX_PATTERN, + query: { + bool: { + filter: [ + { + range: { + '@timestamp': { + gte: isWeeklyAligned ? `now/w` : `now/M`, + lte: 'now/m', + }, + }, + }, + { + term: { + 'slo.id': slo.id, + }, + }, + { + term: { + 'slo.revision': slo.revision, + }, + }, + ], + }, + }, + }, + pivot: { + group_by: getGroupBy(slo), + aggregations: { + _totalSlicesInPeriod: { + bucket_script: { + buckets_path: {}, + script: { + source: ` + if (${isWeeklyAligned} == true) { + return Math.ceil(7 * 24 * 60 * 60 / ${sliceDurationInSeconds}); + } else { + Date d = new Date(); + Instant instant = Instant.ofEpochMilli(d.getTime()); + LocalDateTime now = LocalDateTime.ofInstant(instant, ZoneOffset.UTC); + LocalDateTime startOfMonth = now + .withDayOfMonth(1) + .withHour(0) + .withMinute(0) + .withSecond(0); + LocalDateTime startOfNextMonth = startOfMonth.plusMonths(1); + double sliceDurationInMinutes = ${sliceDurationInSeconds} / 60; + + return Math.ceil(Duration.between(startOfMonth, startOfNextMonth).toMinutes() / sliceDurationInMinutes); + } + `, + }, + }, + }, + goodEvents: { + sum: { + field: 'slo.isGoodSlice', + }, + }, + totalEvents: { + value_count: { + field: 'slo.isGoodSlice', + }, + }, + sliValue: { + bucket_script: { + buckets_path: { + goodEvents: 'goodEvents', + totalEvents: 'totalEvents', + }, + script: + 'if (params.totalEvents == 0) { return -1 } else if (params.goodEvents >= params.totalEvents) { return 1 } else { return params.goodEvents / params.totalEvents }', + }, + }, + errorBudgetInitial: { + bucket_script: { + buckets_path: {}, + script: `1 - ${slo.objective.target}`, + }, + }, + errorBudgetConsumed: { + bucket_script: { + buckets_path: { + goodEvents: 'goodEvents', + totalEvents: 'totalEvents', + totalSlicesInPeriod: '_totalSlicesInPeriod', + errorBudgetInitial: 'errorBudgetInitial', + }, + script: + 'if (params.totalEvents == 0) { return 0 } else { return (params.totalEvents - params.goodEvents) / (params.totalSlicesInPeriod * params.errorBudgetInitial) }', + }, + }, + errorBudgetRemaining: { + bucket_script: { + buckets_path: { + errorBudgetConsumed: 'errorBudgetConsumed', + }, + script: '1 - params.errorBudgetConsumed', + }, + }, + statusCode: { + bucket_script: { + buckets_path: { + sliValue: 'sliValue', + errorBudgetRemaining: 'errorBudgetRemaining', + }, + script: `if (params.sliValue == -1) { return 0 } else if (params.sliValue >= ${slo.objective.target}) { return 4 } else if (params.errorBudgetRemaining > 0) { return 2 } else { return 1 }`, + }, + }, + latestSliTimestamp: { + max: { + field: '@timestamp', + }, + }, + }, + }, + description: `Summarise the rollup data of SLO: ${slo.name} [id: ${slo.id}, revision: ${slo.revision}].`, + frequency: '1m', + sync: { + time: { + field: 'event.ingested', + delay: '65s', + }, + }, + settings: { + deduce_mappings: false, + unattended: true, + }, + _meta: { + version: SLO_RESOURCES_VERSION, + managed: true, + managed_by: 'observability', + }, + }; +} diff --git a/x-pack/plugins/observability/server/services/slo/summary_transform_generator/generators/timeslices_rolling.ts b/x-pack/plugins/observability/server/services/slo/summary_transform_generator/generators/timeslices_rolling.ts new file mode 100644 index 0000000000000..a6212f671997b --- /dev/null +++ b/x-pack/plugins/observability/server/services/slo/summary_transform_generator/generators/timeslices_rolling.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 { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types'; +import { SLO } from '../../../../domain/models'; +import { + getSLOSummaryPipelineId, + getSLOSummaryTransformId, + SLO_DESTINATION_INDEX_PATTERN, + SLO_RESOURCES_VERSION, + SLO_SUMMARY_DESTINATION_INDEX_NAME, +} from '../../../../../common/slo/constants'; +import { getGroupBy } from './common'; + +export function generateSummaryTransformForTimeslicesAndRolling( + slo: SLO +): TransformPutTransformRequest { + return { + transform_id: getSLOSummaryTransformId(slo.id, slo.revision), + dest: { + pipeline: getSLOSummaryPipelineId(slo.id, slo.revision), + index: SLO_SUMMARY_DESTINATION_INDEX_NAME, + }, + source: { + index: SLO_DESTINATION_INDEX_PATTERN, + query: { + bool: { + filter: [ + { + range: { + '@timestamp': { + gte: `now-${slo.timeWindow.duration.format()}/m`, + lte: 'now/m', + }, + }, + }, + { + term: { + 'slo.id': slo.id, + }, + }, + { + term: { + 'slo.revision': slo.revision, + }, + }, + ], + }, + }, + }, + pivot: { + group_by: getGroupBy(slo), + aggregations: { + goodEvents: { + sum: { + field: 'slo.isGoodSlice', + }, + }, + totalEvents: { + value_count: { + field: 'slo.isGoodSlice', + }, + }, + sliValue: { + bucket_script: { + buckets_path: { + goodEvents: 'goodEvents', + totalEvents: 'totalEvents', + }, + script: + 'if (params.totalEvents == 0) { return -1 } else if (params.goodEvents >= params.totalEvents) { return 1 } else { return params.goodEvents / params.totalEvents }', + }, + }, + errorBudgetInitial: { + bucket_script: { + buckets_path: {}, + script: `1 - ${slo.objective.target}`, + }, + }, + errorBudgetConsumed: { + bucket_script: { + buckets_path: { + sliValue: 'sliValue', + errorBudgetInitial: 'errorBudgetInitial', + }, + script: + 'if (params.sliValue == -1) { return 0 } else { return (1 - params.sliValue) / params.errorBudgetInitial }', + }, + }, + errorBudgetRemaining: { + bucket_script: { + buckets_path: { + errorBudgetConsumed: 'errorBudgetConsumed', + }, + script: '1 - params.errorBudgetConsumed', + }, + }, + statusCode: { + bucket_script: { + buckets_path: { + sliValue: 'sliValue', + errorBudgetRemaining: 'errorBudgetRemaining', + }, + script: { + source: `if (params.sliValue == -1) { return 0 } else if (params.sliValue >= ${slo.objective.target}) { return 4 } else if (params.errorBudgetRemaining > 0) { return 2 } else { return 1 }`, + }, + }, + }, + latestSliTimestamp: { + max: { + field: '@timestamp', + }, + }, + }, + }, + description: `Summarise the rollup data of SLO: ${slo.name} [id: ${slo.id}, revision: ${slo.revision}].`, + frequency: '1m', + sync: { + time: { + field: 'event.ingested', + delay: '65s', + }, + }, + settings: { + deduce_mappings: false, + unattended: true, + }, + _meta: { + version: SLO_RESOURCES_VERSION, + managed: true, + managed_by: 'observability', + }, + }; +} diff --git a/x-pack/plugins/observability/server/services/slo/summary_transform/helpers/create_temp_summary.ts b/x-pack/plugins/observability/server/services/slo/summary_transform_generator/helpers/create_temp_summary.ts similarity index 80% rename from x-pack/plugins/observability/server/services/slo/summary_transform/helpers/create_temp_summary.ts rename to x-pack/plugins/observability/server/services/slo/summary_transform_generator/helpers/create_temp_summary.ts index 9b4a15f2bf51f..166ca0198dbb8 100644 --- a/x-pack/plugins/observability/server/services/slo/summary_transform/helpers/create_temp_summary.ts +++ b/x-pack/plugins/observability/server/services/slo/summary_transform_generator/helpers/create_temp_summary.ts @@ -8,7 +8,7 @@ import { ALL_VALUE } from '@kbn/slo-schema'; import { SLO } from '../../../../domain/models'; -export function createTempSummaryDocument(slo: SLO) { +export function createTempSummaryDocument(slo: SLO, spaceId: string) { return { service: { environment: null, @@ -33,6 +33,11 @@ export function createTempSummaryDocument(slo: SLO) { id: slo.id, budgetingMethod: slo.budgetingMethod, revision: slo.revision, + objective: { + target: slo.objective.target, + timesliceTarget: slo.objective.timesliceTarget ?? null, + timesliceWindow: slo.objective.timesliceWindow?.format() ?? null, + }, tags: slo.tags, }, goodEvents: 0, @@ -45,5 +50,6 @@ export function createTempSummaryDocument(slo: SLO) { statusCode: 0, status: 'NO_DATA', isTempDoc: true, + spaceId, }; } diff --git a/x-pack/plugins/observability/server/services/slo/summary_transform_generator/summary_transform_generator.ts b/x-pack/plugins/observability/server/services/slo/summary_transform_generator/summary_transform_generator.ts new file mode 100644 index 0000000000000..7710515f6538a --- /dev/null +++ b/x-pack/plugins/observability/server/services/slo/summary_transform_generator/summary_transform_generator.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { TransformPutTransformRequest } from '@elastic/elasticsearch/lib/api/types'; +import { SLO } from '../../../domain/models'; +import { generateSummaryTransformForOccurrences } from './generators/occurrences'; +import { generateSummaryTransformForTimeslicesAndRolling } from './generators/timeslices_rolling'; +import { generateSummaryTransformForTimeslicesAndCalendarAligned } from './generators/timeslices_calendar_aligned'; + +export interface SummaryTransformGenerator { + generate(slo: SLO): TransformPutTransformRequest; +} + +export class DefaultSummaryTransformGenerator implements SummaryTransformGenerator { + public generate(slo: SLO): TransformPutTransformRequest { + if (slo.budgetingMethod === 'occurrences') { + return generateSummaryTransformForOccurrences(slo); + } else if (slo.budgetingMethod === 'timeslices' && slo.timeWindow.type === 'rolling') { + return generateSummaryTransformForTimeslicesAndRolling(slo); + } else if (slo.budgetingMethod === 'timeslices' && slo.timeWindow.type === 'calendarAligned') { + return generateSummaryTransformForTimeslicesAndCalendarAligned(slo); + } + + throw new Error('Not supported SLO'); + } +} diff --git a/x-pack/plugins/observability/server/services/slo/summay_transform_manager.ts b/x-pack/plugins/observability/server/services/slo/summay_transform_manager.ts new file mode 100644 index 0000000000000..bc22f801c9fcc --- /dev/null +++ b/x-pack/plugins/observability/server/services/slo/summay_transform_manager.ts @@ -0,0 +1,99 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ElasticsearchClient, Logger } from '@kbn/core/server'; + +import { SLO } from '../../domain/models'; +import { SecurityException } from '../../errors'; +import { retryTransientEsErrors } from '../../utils/retry'; +import { SummaryTransformGenerator } from './summary_transform_generator/summary_transform_generator'; +import { TransformManager } from './transform_manager'; + +type TransformId = string; + +export class DefaultSummaryTransformManager implements TransformManager { + constructor( + private generator: SummaryTransformGenerator, + private esClient: ElasticsearchClient, + private logger: Logger + ) {} + + async install(slo: SLO): Promise { + const transformParams = this.generator.generate(slo); + try { + await retryTransientEsErrors(() => this.esClient.transform.putTransform(transformParams), { + logger: this.logger, + }); + } catch (err) { + this.logger.error(`Cannot create summary transform for SLO [${slo.id}]`); + if (err.meta?.body?.error?.type === 'security_exception') { + throw new SecurityException(err.meta.body.error.reason); + } + + throw err; + } + + return transformParams.transform_id; + } + + async preview(transformId: string): Promise { + try { + await retryTransientEsErrors( + () => this.esClient.transform.previewTransform({ transform_id: transformId }), + { logger: this.logger } + ); + } catch (err) { + this.logger.error(`Cannot preview SLO summary transform [${transformId}]`); + throw err; + } + } + + async start(transformId: TransformId): Promise { + try { + await retryTransientEsErrors( + () => + this.esClient.transform.startTransform({ transform_id: transformId }, { ignore: [409] }), + { logger: this.logger } + ); + } catch (err) { + this.logger.error(`Cannot start SLO summary transform [${transformId}]`); + throw err; + } + } + + async stop(transformId: TransformId): Promise { + try { + await retryTransientEsErrors( + () => + this.esClient.transform.stopTransform( + { transform_id: transformId, wait_for_completion: true, force: true }, + { ignore: [404] } + ), + { logger: this.logger } + ); + } catch (err) { + this.logger.error(`Cannot stop SLO summary transform [${transformId}]`); + throw err; + } + } + + async uninstall(transformId: TransformId): Promise { + try { + await retryTransientEsErrors( + () => + this.esClient.transform.deleteTransform( + { transform_id: transformId, force: true }, + { ignore: [404] } + ), + { logger: this.logger } + ); + } catch (err) { + this.logger.error(`Cannot delete SLO summary transform [${transformId}]`); + throw err; + } + } +} diff --git a/x-pack/plugins/observability/server/services/slo/tasks/orphan_summary_cleanup_task.test.ts b/x-pack/plugins/observability/server/services/slo/tasks/orphan_summary_cleanup_task.test.ts new file mode 100644 index 0000000000000..6638ab84c2c0a --- /dev/null +++ b/x-pack/plugins/observability/server/services/slo/tasks/orphan_summary_cleanup_task.test.ts @@ -0,0 +1,320 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getDeleteQueryFilter, SloOrphanSummaryCleanupTask } from './orphan_summary_cleanup_task'; +import { taskManagerMock } from '@kbn/task-manager-plugin/server/mocks'; + +import { loggerMock } from '@kbn/logging-mocks'; +import { elasticsearchClientMock } from '@kbn/core-elasticsearch-client-server-mocks'; +import { times } from 'lodash'; +import { savedObjectsClientMock } from '@kbn/core-saved-objects-api-server-mocks'; + +const taskManagerSetup = taskManagerMock.createSetup(); +const taskManagerStart = taskManagerMock.createStart(); +const logger = loggerMock.create(); +const esClient = elasticsearchClientMock.createClusterClient().asInternalUser; +const soClient = savedObjectsClientMock.create(); + +describe('SloSummaryCleanupTask', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should run for empty', async function () { + const task = new SloOrphanSummaryCleanupTask(taskManagerSetup, logger, {} as any); + await task.start(taskManagerStart, soClient, esClient); + + task.fetchSloSummariesIds = jest.fn().mockReturnValue({ sloSummaryIds: [] }); + + task.findSloDefinitions = jest.fn(); + await task.runTask(); + }); + + it('should run some slos', async function () { + const task = new SloOrphanSummaryCleanupTask(taskManagerSetup, logger, {} as any); + task.findSloDefinitions = jest.fn().mockResolvedValue([ + { id: '1', revision: 1 }, + { id: '2', revision: 1 }, + { id: '3', revision: 1 }, + ]); + + await task.start(taskManagerStart, soClient, esClient); + + task.fetchSloSummariesIds = jest.fn().mockReturnValue({ + sloSummaryIds: [ + { id: '1', revision: 1 }, + { id: '2', revision: 1 }, + { id: '3', revision: 2 }, + { id: '4', revision: NaN }, + { id: '3', revision: 1 }, + { id: '3', revision: 0 }, + ], + }); + + await task.runTask(); + + expect(task.fetchSloSummariesIds).toHaveBeenCalled(); + expect(esClient.deleteByQuery).toHaveBeenCalledWith({ + index: '.slo-observability.summary-v3*', + query: { + bool: { + should: [ + { bool: { must: [{ term: { 'slo.id': '3' } }, { term: { 'slo.revision': 2 } }] } }, + { bool: { must: [{ term: { 'slo.id': '4' } }, { term: { 'slo.revision': NaN } }] } }, + { bool: { must: [{ term: { 'slo.id': '3' } }, { term: { 'slo.revision': 0 } }] } }, + ], + }, + }, + wait_for_completion: false, + }); + }); + + it('should run lots of slos', async function () { + const task = new SloOrphanSummaryCleanupTask(taskManagerSetup, logger, {} as any); + task.findSloDefinitions = jest.fn().mockResolvedValue( + times(10000, (i) => ({ + id: `${i}`, + revision: 1, + })) + ); + task.fetchSloSummariesIds = jest.fn().mockReturnValue({ + sloSummaryIds: [ + { id: '01', revision: 1 }, + { id: '02', revision: 1 }, + { id: '03', revision: 1 }, + { id: '04', revision: 1 }, + { id: '1', revision: 1 }, + { id: '2', revision: 1 }, + { id: '3', revision: 1 }, + { id: '4', revision: 1 }, + ], + }); + await task.start(taskManagerStart, soClient, esClient); + await task.runTask(); + + expect(task.fetchSloSummariesIds).toHaveBeenCalledTimes(1); + expect(esClient.deleteByQuery).toHaveBeenCalledTimes(1); + expect(esClient.deleteByQuery).toHaveBeenNthCalledWith(1, { + wait_for_completion: false, + index: '.slo-observability.summary-v3*', + query: { + bool: { + should: getDeleteQueryFilter([ + { id: '01', revision: 1 }, + { id: '02', revision: 1 }, + { id: '03', revision: 1 }, + { id: '04', revision: 1 }, + ]), + }, + }, + }); + }); + + it('should run when lots of slo defs are there', async function () { + const task = new SloOrphanSummaryCleanupTask(taskManagerSetup, logger, {} as any); + task.findSloDefinitions = jest.fn().mockResolvedValue( + times(10000, (i) => ({ + id: `${i}`, + revision: 2, + })) + ); + task.fetchSloSummariesIds = jest.fn().mockImplementation(async (searchKey) => { + if (!searchKey) { + return { + sloSummaryIds: [ + { id: '1', revision: 2 }, + { id: '2', revision: 2 }, + { id: '3', revision: 2 }, + { id: '4', revision: 2 }, + { id: '01', revision: 1 }, + { id: '02', revision: 1 }, + { id: '03', revision: 1 }, + { id: '04', revision: 1 }, + ], + searchAfter: '1', + }; + } + return { + sloSummaryIds: [ + { id: '5', revision: 2 }, + { id: '6', revision: 2 }, + { id: '7', revision: 2 }, + { id: '8', revision: 2 }, + { id: '05', revision: 1 }, + { id: '06', revision: 1 }, + { id: '07', revision: 1 }, + { id: '08', revision: 1 }, + ], + }; + }); + await task.start(taskManagerStart, soClient, esClient); + await task.runTask(); + + expect(task.fetchSloSummariesIds).toHaveBeenCalledTimes(2); + expect(esClient.deleteByQuery).toHaveBeenCalledTimes(2); + + expect(esClient.deleteByQuery).toHaveBeenNthCalledWith(1, { + index: '.slo-observability.summary-v3*', + query: { + bool: { + should: getDeleteQueryFilter([ + { id: '01', revision: 1 }, + { id: '02', revision: 1 }, + { id: '03', revision: 1 }, + { id: '04', revision: 1 }, + ]), + }, + }, + wait_for_completion: false, + }); + + expect(esClient.deleteByQuery).toHaveBeenLastCalledWith({ + wait_for_completion: false, + index: '.slo-observability.summary-v3*', + query: { + bool: { + should: getDeleteQueryFilter([ + { id: '05', revision: 1 }, + { id: '06', revision: 1 }, + { id: '07', revision: 1 }, + { id: '08', revision: 1 }, + ]), + }, + }, + }); + }); + + it('should run when summaries are way more then defs', async function () { + const task = new SloOrphanSummaryCleanupTask(taskManagerSetup, logger, {} as any); + task.findSloDefinitions = jest.fn().mockResolvedValue( + times(100, (i) => ({ + id: `${i}`, + revision: 2, + })) + ); + task.fetchSloSummariesIds = jest.fn().mockImplementation(async (searchKey) => { + if (!searchKey) { + return { + sloSummaryIds: [ + { id: '1', revision: 2 }, + { id: '2', revision: 2 }, + { id: '3', revision: 2 }, + { id: '4', revision: 2 }, + { id: '01', revision: 1 }, + { id: '02', revision: 1 }, + { id: '03', revision: 1 }, + { id: '04', revision: 1 }, + ], + searchAfter: '1', + }; + } + return { + sloSummaryIds: [ + { id: '5', revision: 2 }, + { id: '6', revision: 2 }, + { id: '7', revision: 2 }, + { id: '8', revision: 2 }, + { id: '05', revision: 1 }, + { id: '06', revision: 1 }, + { id: '07', revision: 1 }, + { id: '08', revision: 1 }, + ], + }; + }); + await task.start(taskManagerStart, soClient, esClient); + await task.runTask(); + + expect(task.fetchSloSummariesIds).toHaveBeenCalledTimes(2); + expect(esClient.deleteByQuery).toHaveBeenCalledTimes(2); + expect(esClient.deleteByQuery).toHaveBeenNthCalledWith(1, { + wait_for_completion: false, + index: '.slo-observability.summary-v3*', + query: { + bool: { + should: getDeleteQueryFilter([ + { id: '01', revision: 1 }, + { id: '02', revision: 1 }, + { id: '03', revision: 1 }, + { id: '04', revision: 1 }, + ]), + }, + }, + }); + expect(esClient.deleteByQuery).toHaveBeenLastCalledWith({ + wait_for_completion: false, + index: '.slo-observability.summary-v3*', + query: { + bool: { + should: getDeleteQueryFilter([ + { id: '05', revision: 1 }, + { id: '06', revision: 1 }, + { id: '07', revision: 1 }, + { id: '08', revision: 1 }, + ]), + }, + }, + }); + }); + + it('should run when there are no Slo defs', async function () { + const task = new SloOrphanSummaryCleanupTask(taskManagerSetup, logger, {} as any); + task.findSloDefinitions = jest.fn().mockResolvedValue([]); + task.fetchSloSummariesIds = jest.fn().mockImplementation(async (searchKey) => { + if (!searchKey) { + return { + sloSummaryIds: [ + { id: '1', revision: 2 }, + { id: '2', revision: 2 }, + { id: '3', revision: 2 }, + { id: '4', revision: 2 }, + { id: '01', revision: 1 }, + { id: '02', revision: 1 }, + { id: '03', revision: 1 }, + { id: '04', revision: 1 }, + ], + searchAfter: '1', + }; + } + return { + sloSummaryIds: [ + { id: '5', revision: 2 }, + { id: '6', revision: 2 }, + { id: '7', revision: 2 }, + { id: '8', revision: 2 }, + { id: '05', revision: 1 }, + { id: '06', revision: 1 }, + { id: '07', revision: 1 }, + { id: '08', revision: 1 }, + ], + }; + }); + await task.start(taskManagerStart, soClient, esClient); + await task.runTask(); + + expect(task.fetchSloSummariesIds).toHaveBeenCalledTimes(2); + expect(esClient.deleteByQuery).toHaveBeenCalledTimes(2); + + expect(esClient.deleteByQuery).toHaveBeenNthCalledWith(1, { + wait_for_completion: false, + index: '.slo-observability.summary-v3*', + query: { + bool: { + should: getDeleteQueryFilter([ + { id: '1', revision: 2 }, + { id: '2', revision: 2 }, + { id: '3', revision: 2 }, + { id: '4', revision: 2 }, + { id: '01', revision: 1 }, + { id: '02', revision: 1 }, + { id: '03', revision: 1 }, + { id: '04', revision: 1 }, + ]), + }, + }, + }); + }); +}); diff --git a/x-pack/plugins/observability/server/services/slo/tasks/orphan_summary_cleanup_task.ts b/x-pack/plugins/observability/server/services/slo/tasks/orphan_summary_cleanup_task.ts new file mode 100644 index 0000000000000..c863dadfe859c --- /dev/null +++ b/x-pack/plugins/observability/server/services/slo/tasks/orphan_summary_cleanup_task.ts @@ -0,0 +1,249 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ElasticsearchClient, Logger, SavedObjectsClientContract } from '@kbn/core/server'; +import { + ConcreteTaskInstance, + TaskManagerSetupContract, + TaskManagerStartContract, +} from '@kbn/task-manager-plugin/server'; +import { AggregationsCompositeAggregateKey } from '@elastic/elasticsearch/lib/api/types'; +import { ALL_SPACES_ID } from '@kbn/spaces-plugin/common/constants'; +import { StoredSLO } from '../../../domain/models'; +import { SO_SLO_TYPE } from '../../../saved_objects'; +import { ObservabilityConfig } from '../../..'; +import { SLO_SUMMARY_DESTINATION_INDEX_PATTERN } from '../../../../common/slo/constants'; + +export const TASK_TYPE = 'SLO:ORPHAN_SUMMARIES-CLEANUP-TASK'; + +export const getDeleteQueryFilter = ( + sloSummaryIdsToDelete: Array<{ id: string; revision: number }> +) => { + return sloSummaryIdsToDelete.map(({ id, revision }) => { + return { + bool: { + must: [ + { + term: { + 'slo.id': id, + }, + }, + { + term: { + 'slo.revision': Number(revision), + }, + }, + ], + }, + }; + }); +}; + +export class SloOrphanSummaryCleanupTask { + private abortController = new AbortController(); + private logger: Logger; + private taskManager?: TaskManagerStartContract; + private soClient?: SavedObjectsClientContract; + private esClient?: ElasticsearchClient; + private config: ObservabilityConfig; + + constructor(taskManager: TaskManagerSetupContract, logger: Logger, config: ObservabilityConfig) { + this.logger = logger; + this.config = config; + + taskManager.registerTaskDefinitions({ + [TASK_TYPE]: { + title: 'SLO Definitions Cleanup Task', + timeout: '3m', + maxAttempts: 1, + createTaskRunner: ({ taskInstance }: { taskInstance: ConcreteTaskInstance }) => { + return { + run: async () => { + return this.runTask(); + }, + + cancel: async () => { + this.abortController.abort('[SLO] Definitions clean up Task timed out'); + }, + }; + }, + }, + }); + } + + runTask = async () => { + if (this.soClient && this.esClient) { + let searchAfterKey: AggregationsCompositeAggregateKey | undefined; + + do { + const { sloSummaryIds, searchAfter } = await this.fetchSloSummariesIds(searchAfterKey); + + if (sloSummaryIds.length === 0) { + return; + } + + searchAfterKey = searchAfter; + + const ids = sloSummaryIds.map(({ id }) => id); + + const sloDefinitions = await this.findSloDefinitions(ids); + + const sloSummaryIdsToDelete = sloSummaryIds.filter( + ({ id, revision }) => + !sloDefinitions.find( + (attributes) => attributes.id === id && attributes.revision === revision + ) + ); + + if (sloSummaryIdsToDelete.length > 0) { + this.logger.info( + `[SLO] Deleting ${sloSummaryIdsToDelete.length} SLO Summaries from the summary index` + ); + + await this.esClient.deleteByQuery({ + wait_for_completion: false, + index: SLO_SUMMARY_DESTINATION_INDEX_PATTERN, + query: { + bool: { + should: getDeleteQueryFilter(sloSummaryIdsToDelete.sort()), + }, + }, + }); + } + } while (searchAfterKey); + } + }; + + fetchSloSummariesIds = async ( + searchAfter?: AggregationsCompositeAggregateKey + ): Promise<{ + searchAfter?: AggregationsCompositeAggregateKey; + sloSummaryIds: Array<{ id: string; revision: number }>; + }> => { + this.logger.debug(`[SLO] Fetching SLO Summaries ids after ${searchAfter}`); + if (!this.esClient) { + return { + searchAfter: undefined, + sloSummaryIds: [], + }; + } + + const size = 1000; + + const result = await this.esClient.search< + unknown, + { + slos: { + after_key: AggregationsCompositeAggregateKey; + buckets: Array<{ + key: { + id: string; + revision: number; + }; + }>; + }; + } + >({ + size: 0, + index: SLO_SUMMARY_DESTINATION_INDEX_PATTERN, + aggs: { + slos: { + composite: { + size, + sources: [ + { + id: { + terms: { + field: 'slo.id', + }, + }, + }, + { + revision: { + terms: { + field: 'slo.revision', + }, + }, + }, + ], + after: searchAfter, + }, + }, + }, + }); + + const aggBuckets = result.aggregations?.slos.buckets ?? []; + if (aggBuckets.length === 0) { + return { + searchAfter: undefined, + sloSummaryIds: [], + }; + } + + const newSearchAfter = + aggBuckets.length < size ? undefined : result.aggregations?.slos.after_key; + + const sloSummaryIds = aggBuckets.map(({ key }) => { + return { + id: String(key.id), + revision: Number(key.revision), + }; + }); + + return { + searchAfter: newSearchAfter, + sloSummaryIds, + }; + }; + + findSloDefinitions = async (ids: string[]) => { + const sloDefinitions = await this.soClient?.find>({ + type: SO_SLO_TYPE, + page: 1, + perPage: ids.length, + filter: `slo.attributes.id:(${ids.join(' or ')})`, + namespaces: [ALL_SPACES_ID], + fields: ['id', 'revision'], + }); + + return sloDefinitions?.saved_objects.map(({ attributes }) => attributes) ?? []; + }; + + private get taskId() { + return `${TASK_TYPE}:1.0.0`; + } + + public async start( + taskManager: TaskManagerStartContract, + soClient: SavedObjectsClientContract, + esClient: ElasticsearchClient + ) { + this.taskManager = taskManager; + this.soClient = soClient; + this.esClient = esClient; + + if (!taskManager) { + this.logger.info('[SLO] Missing required service during startup, skipping task.'); + return; + } + + if (this.config.sloOrphanSummaryCleanUpTaskEnabled) { + this.taskManager.ensureScheduled({ + id: this.taskId, + taskType: TASK_TYPE, + schedule: { + interval: '1h', + }, + scope: ['observability', 'slo'], + state: {}, + params: {}, + }); + } else { + this.taskManager.removeIfExists(this.taskId); + } + } +} diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/apm_transaction_duration.test.ts.snap b/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/apm_transaction_duration.test.ts.snap index d5ac57c80e40d..4fa12cae3e12e 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/apm_transaction_duration.test.ts.snap +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/apm_transaction_duration.test.ts.snap @@ -190,66 +190,21 @@ Object { "field": "service.environment", }, }, - "slo.budgetingMethod": Object { - "terms": Object { - "field": "slo.budgetingMethod", - }, - }, - "slo.description": Object { - "terms": Object { - "field": "slo.description", - }, - }, - "slo.groupBy": Object { - "terms": Object { - "field": "slo.groupBy", - }, - }, "slo.id": Object { "terms": Object { "field": "slo.id", }, }, - "slo.indicator.type": Object { - "terms": Object { - "field": "slo.indicator.type", - }, - }, "slo.instanceId": Object { "terms": Object { "field": "slo.instanceId", }, }, - "slo.name": Object { - "terms": Object { - "field": "slo.name", - }, - }, - "slo.objective.target": Object { - "terms": Object { - "field": "slo.objective.target", - }, - }, "slo.revision": Object { "terms": Object { "field": "slo.revision", }, }, - "slo.tags": Object { - "terms": Object { - "field": "slo.tags", - }, - }, - "slo.timeWindow.duration": Object { - "terms": Object { - "field": "slo.timeWindow.duration", - }, - }, - "slo.timeWindow.type": Object { - "terms": Object { - "field": "slo.timeWindow.type", - }, - }, } `; @@ -304,66 +259,21 @@ Object { "field": "service.name", }, }, - "slo.budgetingMethod": Object { - "terms": Object { - "field": "slo.budgetingMethod", - }, - }, - "slo.description": Object { - "terms": Object { - "field": "slo.description", - }, - }, - "slo.groupBy": Object { - "terms": Object { - "field": "slo.groupBy", - }, - }, "slo.id": Object { "terms": Object { "field": "slo.id", }, }, - "slo.indicator.type": Object { - "terms": Object { - "field": "slo.indicator.type", - }, - }, "slo.instanceId": Object { "terms": Object { "field": "slo.instanceId", }, }, - "slo.name": Object { - "terms": Object { - "field": "slo.name", - }, - }, - "slo.objective.target": Object { - "terms": Object { - "field": "slo.objective.target", - }, - }, "slo.revision": Object { "terms": Object { "field": "slo.revision", }, }, - "slo.tags": Object { - "terms": Object { - "field": "slo.tags", - }, - }, - "slo.timeWindow.duration": Object { - "terms": Object { - "field": "slo.timeWindow.duration", - }, - }, - "slo.timeWindow.type": Object { - "terms": Object { - "field": "slo.timeWindow.type", - }, - }, } `; @@ -413,66 +323,21 @@ Object { "fixed_interval": "1m", }, }, - "slo.budgetingMethod": Object { - "terms": Object { - "field": "slo.budgetingMethod", - }, - }, - "slo.description": Object { - "terms": Object { - "field": "slo.description", - }, - }, - "slo.groupBy": Object { - "terms": Object { - "field": "slo.groupBy", - }, - }, "slo.id": Object { "terms": Object { "field": "slo.id", }, }, - "slo.indicator.type": Object { - "terms": Object { - "field": "slo.indicator.type", - }, - }, "slo.instanceId": Object { "terms": Object { "field": "slo.instanceId", }, }, - "slo.name": Object { - "terms": Object { - "field": "slo.name", - }, - }, - "slo.objective.target": Object { - "terms": Object { - "field": "slo.objective.target", - }, - }, "slo.revision": Object { "terms": Object { "field": "slo.revision", }, }, - "slo.tags": Object { - "terms": Object { - "field": "slo.tags", - }, - }, - "slo.timeWindow.duration": Object { - "terms": Object { - "field": "slo.timeWindow.duration", - }, - }, - "slo.timeWindow.type": Object { - "terms": Object { - "field": "slo.timeWindow.type", - }, - }, "transaction.name": Object { "terms": Object { "field": "transaction.name", @@ -527,66 +392,21 @@ Object { "fixed_interval": "1m", }, }, - "slo.budgetingMethod": Object { - "terms": Object { - "field": "slo.budgetingMethod", - }, - }, - "slo.description": Object { - "terms": Object { - "field": "slo.description", - }, - }, - "slo.groupBy": Object { - "terms": Object { - "field": "slo.groupBy", - }, - }, "slo.id": Object { "terms": Object { "field": "slo.id", }, }, - "slo.indicator.type": Object { - "terms": Object { - "field": "slo.indicator.type", - }, - }, "slo.instanceId": Object { "terms": Object { "field": "slo.instanceId", }, }, - "slo.name": Object { - "terms": Object { - "field": "slo.name", - }, - }, - "slo.objective.target": Object { - "terms": Object { - "field": "slo.objective.target", - }, - }, "slo.revision": Object { "terms": Object { "field": "slo.revision", }, }, - "slo.tags": Object { - "terms": Object { - "field": "slo.tags", - }, - }, - "slo.timeWindow.duration": Object { - "terms": Object { - "field": "slo.timeWindow.duration", - }, - }, - "slo.timeWindow.type": Object { - "terms": Object { - "field": "slo.timeWindow.type", - }, - }, "transaction.type": Object { "terms": Object { "field": "transaction.type", @@ -600,12 +420,12 @@ Object { "_meta": Object { "managed": true, "managed_by": "observability", - "version": 2, + "version": 3, }, - "description": "Rolled-up SLI data for SLO: irrelevant", + "description": "Rolled-up SLI data for SLO: irrelevant [id: irrelevant, revision: 1]", "dest": Object { - "index": ".slo-observability.sli-v2", - "pipeline": ".slo-observability.sli.pipeline", + "index": ".slo-observability.sli-v3", + "pipeline": ".slo-observability.sli.pipeline-v3", }, "frequency": "1m", "pivot": Object { @@ -660,71 +480,21 @@ Object { "field": "service.name", }, }, - "slo.budgetingMethod": Object { - "terms": Object { - "field": "slo.budgetingMethod", - }, - }, - "slo.description": Object { - "terms": Object { - "field": "slo.description", - }, - }, - "slo.groupBy": Object { - "terms": Object { - "field": "slo.groupBy", - }, - }, "slo.id": Object { "terms": Object { "field": "slo.id", }, }, - "slo.indicator.type": Object { - "terms": Object { - "field": "slo.indicator.type", - }, - }, "slo.instanceId": Object { "terms": Object { "field": "slo.instanceId", }, }, - "slo.name": Object { - "terms": Object { - "field": "slo.name", - }, - }, - "slo.objective.sliceDurationInSeconds": Object { - "terms": Object { - "field": "slo.objective.sliceDurationInSeconds", - }, - }, - "slo.objective.target": Object { - "terms": Object { - "field": "slo.objective.target", - }, - }, "slo.revision": Object { "terms": Object { "field": "slo.revision", }, }, - "slo.tags": Object { - "terms": Object { - "field": "slo.tags", - }, - }, - "slo.timeWindow.duration": Object { - "terms": Object { - "field": "slo.timeWindow.duration", - }, - }, - "slo.timeWindow.type": Object { - "terms": Object { - "field": "slo.timeWindow.type", - }, - }, "transaction.name": Object { "terms": Object { "field": "transaction.name", @@ -794,33 +564,9 @@ Object { }, }, "runtime_mappings": Object { - "slo.budgetingMethod": Object { - "script": Object { - "source": "emit('timeslices')", - }, - "type": "keyword", - }, - "slo.description": Object { - "script": Object { - "source": "emit('irrelevant')", - }, - "type": "keyword", - }, - "slo.groupBy": Object { - "script": Object { - "source": "emit('*')", - }, - "type": "keyword", - }, "slo.id": Object { "script": Object { - "source": Any, - }, - "type": "keyword", - }, - "slo.indicator.type": Object { - "script": Object { - "source": "emit('sli.apm.transactionDuration')", + "source": "emit('irrelevant')", }, "type": "keyword", }, @@ -830,48 +576,12 @@ Object { }, "type": "keyword", }, - "slo.name": Object { - "script": Object { - "source": "emit('irrelevant')", - }, - "type": "keyword", - }, - "slo.objective.sliceDurationInSeconds": Object { - "script": Object { - "source": "emit(120)", - }, - "type": "long", - }, - "slo.objective.target": Object { - "script": Object { - "source": "emit(0.98)", - }, - "type": "double", - }, "slo.revision": Object { "script": Object { "source": "emit(1)", }, "type": "long", }, - "slo.tags": Object { - "script": Object { - "source": "emit('critical,k8s')", - }, - "type": "keyword", - }, - "slo.timeWindow.duration": Object { - "script": Object { - "source": "emit('7d')", - }, - "type": "keyword", - }, - "slo.timeWindow.type": Object { - "script": Object { - "source": "emit('rolling')", - }, - "type": "keyword", - }, }, }, "sync": Object { @@ -880,7 +590,7 @@ Object { "field": "@timestamp", }, }, - "transform_id": Any, + "transform_id": "slo-irrelevant-1", } `; @@ -889,12 +599,12 @@ Object { "_meta": Object { "managed": true, "managed_by": "observability", - "version": 2, + "version": 3, }, - "description": "Rolled-up SLI data for SLO: irrelevant", + "description": "Rolled-up SLI data for SLO: irrelevant [id: irrelevant, revision: 1]", "dest": Object { - "index": ".slo-observability.sli-v2", - "pipeline": ".slo-observability.sli.pipeline", + "index": ".slo-observability.sli-v3", + "pipeline": ".slo-observability.sli.pipeline-v3", }, "frequency": "1m", "pivot": Object { @@ -940,66 +650,21 @@ Object { "field": "service.name", }, }, - "slo.budgetingMethod": Object { - "terms": Object { - "field": "slo.budgetingMethod", - }, - }, - "slo.description": Object { - "terms": Object { - "field": "slo.description", - }, - }, - "slo.groupBy": Object { - "terms": Object { - "field": "slo.groupBy", - }, - }, "slo.id": Object { "terms": Object { "field": "slo.id", }, }, - "slo.indicator.type": Object { - "terms": Object { - "field": "slo.indicator.type", - }, - }, "slo.instanceId": Object { "terms": Object { "field": "slo.instanceId", }, }, - "slo.name": Object { - "terms": Object { - "field": "slo.name", - }, - }, - "slo.objective.target": Object { - "terms": Object { - "field": "slo.objective.target", - }, - }, "slo.revision": Object { "terms": Object { "field": "slo.revision", }, }, - "slo.tags": Object { - "terms": Object { - "field": "slo.tags", - }, - }, - "slo.timeWindow.duration": Object { - "terms": Object { - "field": "slo.timeWindow.duration", - }, - }, - "slo.timeWindow.type": Object { - "terms": Object { - "field": "slo.timeWindow.type", - }, - }, "transaction.name": Object { "terms": Object { "field": "transaction.name", @@ -1069,33 +734,9 @@ Object { }, }, "runtime_mappings": Object { - "slo.budgetingMethod": Object { - "script": Object { - "source": "emit('occurrences')", - }, - "type": "keyword", - }, - "slo.description": Object { - "script": Object { - "source": "emit('irrelevant')", - }, - "type": "keyword", - }, - "slo.groupBy": Object { - "script": Object { - "source": "emit('*')", - }, - "type": "keyword", - }, "slo.id": Object { "script": Object { - "source": Any, - }, - "type": "keyword", - }, - "slo.indicator.type": Object { - "script": Object { - "source": "emit('sli.apm.transactionDuration')", + "source": "emit('irrelevant')", }, "type": "keyword", }, @@ -1105,42 +746,12 @@ Object { }, "type": "keyword", }, - "slo.name": Object { - "script": Object { - "source": "emit('irrelevant')", - }, - "type": "keyword", - }, - "slo.objective.target": Object { - "script": Object { - "source": "emit(0.999)", - }, - "type": "double", - }, "slo.revision": Object { "script": Object { "source": "emit(1)", }, "type": "long", }, - "slo.tags": Object { - "script": Object { - "source": "emit('critical,k8s')", - }, - "type": "keyword", - }, - "slo.timeWindow.duration": Object { - "script": Object { - "source": "emit('7d')", - }, - "type": "keyword", - }, - "slo.timeWindow.type": Object { - "script": Object { - "source": "emit('rolling')", - }, - "type": "keyword", - }, }, }, "sync": Object { @@ -1149,6 +760,6 @@ Object { "field": "@timestamp", }, }, - "transform_id": Any, + "transform_id": "slo-irrelevant-1", } `; diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/apm_transaction_error_rate.test.ts.snap b/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/apm_transaction_error_rate.test.ts.snap index c8d687383b513..515980a9dee54 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/apm_transaction_error_rate.test.ts.snap +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/apm_transaction_error_rate.test.ts.snap @@ -178,66 +178,21 @@ Object { "field": "service.environment", }, }, - "slo.budgetingMethod": Object { - "terms": Object { - "field": "slo.budgetingMethod", - }, - }, - "slo.description": Object { - "terms": Object { - "field": "slo.description", - }, - }, - "slo.groupBy": Object { - "terms": Object { - "field": "slo.groupBy", - }, - }, "slo.id": Object { "terms": Object { "field": "slo.id", }, }, - "slo.indicator.type": Object { - "terms": Object { - "field": "slo.indicator.type", - }, - }, "slo.instanceId": Object { "terms": Object { "field": "slo.instanceId", }, }, - "slo.name": Object { - "terms": Object { - "field": "slo.name", - }, - }, - "slo.objective.target": Object { - "terms": Object { - "field": "slo.objective.target", - }, - }, "slo.revision": Object { "terms": Object { "field": "slo.revision", }, }, - "slo.tags": Object { - "terms": Object { - "field": "slo.tags", - }, - }, - "slo.timeWindow.duration": Object { - "terms": Object { - "field": "slo.timeWindow.duration", - }, - }, - "slo.timeWindow.type": Object { - "terms": Object { - "field": "slo.timeWindow.type", - }, - }, } `; @@ -288,66 +243,21 @@ Object { "field": "service.name", }, }, - "slo.budgetingMethod": Object { - "terms": Object { - "field": "slo.budgetingMethod", - }, - }, - "slo.description": Object { - "terms": Object { - "field": "slo.description", - }, - }, - "slo.groupBy": Object { - "terms": Object { - "field": "slo.groupBy", - }, - }, "slo.id": Object { "terms": Object { "field": "slo.id", }, }, - "slo.indicator.type": Object { - "terms": Object { - "field": "slo.indicator.type", - }, - }, "slo.instanceId": Object { "terms": Object { "field": "slo.instanceId", }, }, - "slo.name": Object { - "terms": Object { - "field": "slo.name", - }, - }, - "slo.objective.target": Object { - "terms": Object { - "field": "slo.objective.target", - }, - }, "slo.revision": Object { "terms": Object { "field": "slo.revision", }, }, - "slo.tags": Object { - "terms": Object { - "field": "slo.tags", - }, - }, - "slo.timeWindow.duration": Object { - "terms": Object { - "field": "slo.timeWindow.duration", - }, - }, - "slo.timeWindow.type": Object { - "terms": Object { - "field": "slo.timeWindow.type", - }, - }, } `; @@ -393,66 +303,21 @@ Object { "fixed_interval": "1m", }, }, - "slo.budgetingMethod": Object { - "terms": Object { - "field": "slo.budgetingMethod", - }, - }, - "slo.description": Object { - "terms": Object { - "field": "slo.description", - }, - }, - "slo.groupBy": Object { - "terms": Object { - "field": "slo.groupBy", - }, - }, "slo.id": Object { "terms": Object { "field": "slo.id", }, }, - "slo.indicator.type": Object { - "terms": Object { - "field": "slo.indicator.type", - }, - }, "slo.instanceId": Object { "terms": Object { "field": "slo.instanceId", }, }, - "slo.name": Object { - "terms": Object { - "field": "slo.name", - }, - }, - "slo.objective.target": Object { - "terms": Object { - "field": "slo.objective.target", - }, - }, "slo.revision": Object { "terms": Object { "field": "slo.revision", }, }, - "slo.tags": Object { - "terms": Object { - "field": "slo.tags", - }, - }, - "slo.timeWindow.duration": Object { - "terms": Object { - "field": "slo.timeWindow.duration", - }, - }, - "slo.timeWindow.type": Object { - "terms": Object { - "field": "slo.timeWindow.type", - }, - }, "transaction.name": Object { "terms": Object { "field": "transaction.name", @@ -503,66 +368,21 @@ Object { "fixed_interval": "1m", }, }, - "slo.budgetingMethod": Object { - "terms": Object { - "field": "slo.budgetingMethod", - }, - }, - "slo.description": Object { - "terms": Object { - "field": "slo.description", - }, - }, - "slo.groupBy": Object { - "terms": Object { - "field": "slo.groupBy", - }, - }, "slo.id": Object { "terms": Object { "field": "slo.id", }, }, - "slo.indicator.type": Object { - "terms": Object { - "field": "slo.indicator.type", - }, - }, "slo.instanceId": Object { "terms": Object { "field": "slo.instanceId", }, }, - "slo.name": Object { - "terms": Object { - "field": "slo.name", - }, - }, - "slo.objective.target": Object { - "terms": Object { - "field": "slo.objective.target", - }, - }, "slo.revision": Object { "terms": Object { "field": "slo.revision", }, }, - "slo.tags": Object { - "terms": Object { - "field": "slo.tags", - }, - }, - "slo.timeWindow.duration": Object { - "terms": Object { - "field": "slo.timeWindow.duration", - }, - }, - "slo.timeWindow.type": Object { - "terms": Object { - "field": "slo.timeWindow.type", - }, - }, "transaction.type": Object { "terms": Object { "field": "transaction.type", @@ -576,12 +396,12 @@ Object { "_meta": Object { "managed": true, "managed_by": "observability", - "version": 2, + "version": 3, }, - "description": "Rolled-up SLI data for SLO: irrelevant", + "description": "Rolled-up SLI data for SLO: irrelevant [id: irrelevant, revision: 1]", "dest": Object { - "index": ".slo-observability.sli-v2", - "pipeline": ".slo-observability.sli.pipeline", + "index": ".slo-observability.sli-v3", + "pipeline": ".slo-observability.sli.pipeline-v3", }, "frequency": "1m", "pivot": Object { @@ -629,71 +449,21 @@ Object { "field": "service.name", }, }, - "slo.budgetingMethod": Object { - "terms": Object { - "field": "slo.budgetingMethod", - }, - }, - "slo.description": Object { - "terms": Object { - "field": "slo.description", - }, - }, - "slo.groupBy": Object { - "terms": Object { - "field": "slo.groupBy", - }, - }, "slo.id": Object { "terms": Object { "field": "slo.id", }, }, - "slo.indicator.type": Object { - "terms": Object { - "field": "slo.indicator.type", - }, - }, "slo.instanceId": Object { "terms": Object { "field": "slo.instanceId", }, }, - "slo.name": Object { - "terms": Object { - "field": "slo.name", - }, - }, - "slo.objective.sliceDurationInSeconds": Object { - "terms": Object { - "field": "slo.objective.sliceDurationInSeconds", - }, - }, - "slo.objective.target": Object { - "terms": Object { - "field": "slo.objective.target", - }, - }, "slo.revision": Object { "terms": Object { "field": "slo.revision", }, }, - "slo.tags": Object { - "terms": Object { - "field": "slo.tags", - }, - }, - "slo.timeWindow.duration": Object { - "terms": Object { - "field": "slo.timeWindow.duration", - }, - }, - "slo.timeWindow.type": Object { - "terms": Object { - "field": "slo.timeWindow.type", - }, - }, "transaction.name": Object { "terms": Object { "field": "transaction.name", @@ -759,33 +529,9 @@ Object { }, }, "runtime_mappings": Object { - "slo.budgetingMethod": Object { - "script": Object { - "source": "emit('timeslices')", - }, - "type": "keyword", - }, - "slo.description": Object { - "script": Object { - "source": "emit('irrelevant')", - }, - "type": "keyword", - }, - "slo.groupBy": Object { - "script": Object { - "source": "emit('*')", - }, - "type": "keyword", - }, "slo.id": Object { "script": Object { - "source": Any, - }, - "type": "keyword", - }, - "slo.indicator.type": Object { - "script": Object { - "source": "emit('sli.apm.transactionErrorRate')", + "source": "emit('irrelevant')", }, "type": "keyword", }, @@ -795,48 +541,12 @@ Object { }, "type": "keyword", }, - "slo.name": Object { - "script": Object { - "source": "emit('irrelevant')", - }, - "type": "keyword", - }, - "slo.objective.sliceDurationInSeconds": Object { - "script": Object { - "source": "emit(120)", - }, - "type": "long", - }, - "slo.objective.target": Object { - "script": Object { - "source": "emit(0.98)", - }, - "type": "double", - }, "slo.revision": Object { "script": Object { "source": "emit(1)", }, "type": "long", }, - "slo.tags": Object { - "script": Object { - "source": "emit('critical,k8s')", - }, - "type": "keyword", - }, - "slo.timeWindow.duration": Object { - "script": Object { - "source": "emit('7d')", - }, - "type": "keyword", - }, - "slo.timeWindow.type": Object { - "script": Object { - "source": "emit('rolling')", - }, - "type": "keyword", - }, }, }, "sync": Object { @@ -845,7 +555,7 @@ Object { "field": "@timestamp", }, }, - "transform_id": Any, + "transform_id": "slo-irrelevant-1", } `; @@ -854,12 +564,12 @@ Object { "_meta": Object { "managed": true, "managed_by": "observability", - "version": 2, + "version": 3, }, - "description": "Rolled-up SLI data for SLO: irrelevant", + "description": "Rolled-up SLI data for SLO: irrelevant [id: irrelevant, revision: 1]", "dest": Object { - "index": ".slo-observability.sli-v2", - "pipeline": ".slo-observability.sli.pipeline", + "index": ".slo-observability.sli-v3", + "pipeline": ".slo-observability.sli.pipeline-v3", }, "frequency": "1m", "pivot": Object { @@ -898,66 +608,21 @@ Object { "field": "service.name", }, }, - "slo.budgetingMethod": Object { - "terms": Object { - "field": "slo.budgetingMethod", - }, - }, - "slo.description": Object { - "terms": Object { - "field": "slo.description", - }, - }, - "slo.groupBy": Object { - "terms": Object { - "field": "slo.groupBy", - }, - }, "slo.id": Object { "terms": Object { "field": "slo.id", }, }, - "slo.indicator.type": Object { - "terms": Object { - "field": "slo.indicator.type", - }, - }, "slo.instanceId": Object { "terms": Object { "field": "slo.instanceId", }, }, - "slo.name": Object { - "terms": Object { - "field": "slo.name", - }, - }, - "slo.objective.target": Object { - "terms": Object { - "field": "slo.objective.target", - }, - }, "slo.revision": Object { "terms": Object { "field": "slo.revision", }, }, - "slo.tags": Object { - "terms": Object { - "field": "slo.tags", - }, - }, - "slo.timeWindow.duration": Object { - "terms": Object { - "field": "slo.timeWindow.duration", - }, - }, - "slo.timeWindow.type": Object { - "terms": Object { - "field": "slo.timeWindow.type", - }, - }, "transaction.name": Object { "terms": Object { "field": "transaction.name", @@ -1023,33 +688,9 @@ Object { }, }, "runtime_mappings": Object { - "slo.budgetingMethod": Object { - "script": Object { - "source": "emit('occurrences')", - }, - "type": "keyword", - }, - "slo.description": Object { - "script": Object { - "source": "emit('irrelevant')", - }, - "type": "keyword", - }, - "slo.groupBy": Object { - "script": Object { - "source": "emit('*')", - }, - "type": "keyword", - }, "slo.id": Object { "script": Object { - "source": Any, - }, - "type": "keyword", - }, - "slo.indicator.type": Object { - "script": Object { - "source": "emit('sli.apm.transactionErrorRate')", + "source": "emit('irrelevant')", }, "type": "keyword", }, @@ -1059,42 +700,12 @@ Object { }, "type": "keyword", }, - "slo.name": Object { - "script": Object { - "source": "emit('irrelevant')", - }, - "type": "keyword", - }, - "slo.objective.target": Object { - "script": Object { - "source": "emit(0.999)", - }, - "type": "double", - }, "slo.revision": Object { "script": Object { "source": "emit(1)", }, "type": "long", }, - "slo.tags": Object { - "script": Object { - "source": "emit('critical,k8s')", - }, - "type": "keyword", - }, - "slo.timeWindow.duration": Object { - "script": Object { - "source": "emit('7d')", - }, - "type": "keyword", - }, - "slo.timeWindow.type": Object { - "script": Object { - "source": "emit('rolling')", - }, - "type": "keyword", - }, }, }, "sync": Object { @@ -1103,6 +714,6 @@ Object { "field": "@timestamp", }, }, - "transform_id": Any, + "transform_id": "slo-irrelevant-1", } `; diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/histogram.test.ts.snap b/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/histogram.test.ts.snap index cdbdd8c527043..0b50f3ef5c52c 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/histogram.test.ts.snap +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/histogram.test.ts.snap @@ -77,12 +77,12 @@ Object { "_meta": Object { "managed": true, "managed_by": "observability", - "version": 2, + "version": 3, }, - "description": "Rolled-up SLI data for SLO: irrelevant", + "description": "Rolled-up SLI data for SLO: irrelevant [id: irrelevant, revision: 1]", "dest": Object { - "index": ".slo-observability.sli-v2", - "pipeline": ".slo-observability.sli.pipeline", + "index": ".slo-observability.sli-v3", + "pipeline": ".slo-observability.sli.pipeline-v3", }, "frequency": "1m", "pivot": Object { @@ -151,71 +151,21 @@ Object { "fixed_interval": "2m", }, }, - "slo.budgetingMethod": Object { - "terms": Object { - "field": "slo.budgetingMethod", - }, - }, - "slo.description": Object { - "terms": Object { - "field": "slo.description", - }, - }, - "slo.groupBy": Object { - "terms": Object { - "field": "slo.groupBy", - }, - }, "slo.id": Object { "terms": Object { "field": "slo.id", }, }, - "slo.indicator.type": Object { - "terms": Object { - "field": "slo.indicator.type", - }, - }, "slo.instanceId": Object { "terms": Object { "field": "slo.instanceId", }, }, - "slo.name": Object { - "terms": Object { - "field": "slo.name", - }, - }, - "slo.objective.sliceDurationInSeconds": Object { - "terms": Object { - "field": "slo.objective.sliceDurationInSeconds", - }, - }, - "slo.objective.target": Object { - "terms": Object { - "field": "slo.objective.target", - }, - }, "slo.revision": Object { "terms": Object { "field": "slo.revision", }, }, - "slo.tags": Object { - "terms": Object { - "field": "slo.tags", - }, - }, - "slo.timeWindow.duration": Object { - "terms": Object { - "field": "slo.timeWindow.duration", - }, - }, - "slo.timeWindow.type": Object { - "terms": Object { - "field": "slo.timeWindow.type", - }, - }, }, }, "settings": Object { @@ -253,33 +203,9 @@ Object { }, }, "runtime_mappings": Object { - "slo.budgetingMethod": Object { - "script": Object { - "source": "emit('timeslices')", - }, - "type": "keyword", - }, - "slo.description": Object { - "script": Object { - "source": "emit('irrelevant')", - }, - "type": "keyword", - }, - "slo.groupBy": Object { - "script": Object { - "source": "emit('*')", - }, - "type": "keyword", - }, "slo.id": Object { "script": Object { - "source": Any, - }, - "type": "keyword", - }, - "slo.indicator.type": Object { - "script": Object { - "source": "emit('sli.histogram.custom')", + "source": "emit('irrelevant')", }, "type": "keyword", }, @@ -289,48 +215,12 @@ Object { }, "type": "keyword", }, - "slo.name": Object { - "script": Object { - "source": "emit('irrelevant')", - }, - "type": "keyword", - }, - "slo.objective.sliceDurationInSeconds": Object { - "script": Object { - "source": "emit(120)", - }, - "type": "long", - }, - "slo.objective.target": Object { - "script": Object { - "source": "emit(0.98)", - }, - "type": "double", - }, "slo.revision": Object { "script": Object { "source": "emit(1)", }, "type": "long", }, - "slo.tags": Object { - "script": Object { - "source": "emit('critical,k8s')", - }, - "type": "keyword", - }, - "slo.timeWindow.duration": Object { - "script": Object { - "source": "emit('7d')", - }, - "type": "keyword", - }, - "slo.timeWindow.type": Object { - "script": Object { - "source": "emit('rolling')", - }, - "type": "keyword", - }, }, }, "sync": Object { @@ -339,7 +229,7 @@ Object { "field": "log_timestamp", }, }, - "transform_id": Any, + "transform_id": "slo-irrelevant-1", } `; @@ -348,12 +238,12 @@ Object { "_meta": Object { "managed": true, "managed_by": "observability", - "version": 2, + "version": 3, }, - "description": "Rolled-up SLI data for SLO: irrelevant", + "description": "Rolled-up SLI data for SLO: irrelevant [id: irrelevant, revision: 1]", "dest": Object { - "index": ".slo-observability.sli-v2", - "pipeline": ".slo-observability.sli.pipeline", + "index": ".slo-observability.sli-v3", + "pipeline": ".slo-observability.sli.pipeline-v3", }, "frequency": "1m", "pivot": Object { @@ -413,66 +303,21 @@ Object { "fixed_interval": "1m", }, }, - "slo.budgetingMethod": Object { - "terms": Object { - "field": "slo.budgetingMethod", - }, - }, - "slo.description": Object { - "terms": Object { - "field": "slo.description", - }, - }, - "slo.groupBy": Object { - "terms": Object { - "field": "slo.groupBy", - }, - }, "slo.id": Object { "terms": Object { "field": "slo.id", }, }, - "slo.indicator.type": Object { - "terms": Object { - "field": "slo.indicator.type", - }, - }, "slo.instanceId": Object { "terms": Object { "field": "slo.instanceId", }, }, - "slo.name": Object { - "terms": Object { - "field": "slo.name", - }, - }, - "slo.objective.target": Object { - "terms": Object { - "field": "slo.objective.target", - }, - }, "slo.revision": Object { "terms": Object { "field": "slo.revision", }, }, - "slo.tags": Object { - "terms": Object { - "field": "slo.tags", - }, - }, - "slo.timeWindow.duration": Object { - "terms": Object { - "field": "slo.timeWindow.duration", - }, - }, - "slo.timeWindow.type": Object { - "terms": Object { - "field": "slo.timeWindow.type", - }, - }, }, }, "settings": Object { @@ -510,33 +355,9 @@ Object { }, }, "runtime_mappings": Object { - "slo.budgetingMethod": Object { - "script": Object { - "source": "emit('occurrences')", - }, - "type": "keyword", - }, - "slo.description": Object { - "script": Object { - "source": "emit('irrelevant')", - }, - "type": "keyword", - }, - "slo.groupBy": Object { - "script": Object { - "source": "emit('*')", - }, - "type": "keyword", - }, "slo.id": Object { "script": Object { - "source": Any, - }, - "type": "keyword", - }, - "slo.indicator.type": Object { - "script": Object { - "source": "emit('sli.histogram.custom')", + "source": "emit('irrelevant')", }, "type": "keyword", }, @@ -546,42 +367,12 @@ Object { }, "type": "keyword", }, - "slo.name": Object { - "script": Object { - "source": "emit('irrelevant')", - }, - "type": "keyword", - }, - "slo.objective.target": Object { - "script": Object { - "source": "emit(0.999)", - }, - "type": "double", - }, "slo.revision": Object { "script": Object { "source": "emit(1)", }, "type": "long", }, - "slo.tags": Object { - "script": Object { - "source": "emit('critical,k8s')", - }, - "type": "keyword", - }, - "slo.timeWindow.duration": Object { - "script": Object { - "source": "emit('7d')", - }, - "type": "keyword", - }, - "slo.timeWindow.type": Object { - "script": Object { - "source": "emit('rolling')", - }, - "type": "keyword", - }, }, }, "sync": Object { @@ -590,6 +381,6 @@ Object { "field": "log_timestamp", }, }, - "transform_id": Any, + "transform_id": "slo-irrelevant-1", } `; diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/kql_custom.test.ts.snap b/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/kql_custom.test.ts.snap index 27da87629465d..0b5dc06be0a58 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/kql_custom.test.ts.snap +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/kql_custom.test.ts.snap @@ -118,12 +118,12 @@ Object { "_meta": Object { "managed": true, "managed_by": "observability", - "version": 2, + "version": 3, }, - "description": "Rolled-up SLI data for SLO: irrelevant", + "description": "Rolled-up SLI data for SLO: irrelevant [id: irrelevant, revision: 1]", "dest": Object { - "index": ".slo-observability.sli-v2", - "pipeline": ".slo-observability.sli.pipeline", + "index": ".slo-observability.sli-v3", + "pipeline": ".slo-observability.sli.pipeline-v3", }, "frequency": "1m", "pivot": Object { @@ -166,71 +166,21 @@ Object { "fixed_interval": "2m", }, }, - "slo.budgetingMethod": Object { - "terms": Object { - "field": "slo.budgetingMethod", - }, - }, - "slo.description": Object { - "terms": Object { - "field": "slo.description", - }, - }, - "slo.groupBy": Object { - "terms": Object { - "field": "slo.groupBy", - }, - }, "slo.id": Object { "terms": Object { "field": "slo.id", }, }, - "slo.indicator.type": Object { - "terms": Object { - "field": "slo.indicator.type", - }, - }, "slo.instanceId": Object { "terms": Object { "field": "slo.instanceId", }, }, - "slo.name": Object { - "terms": Object { - "field": "slo.name", - }, - }, - "slo.objective.sliceDurationInSeconds": Object { - "terms": Object { - "field": "slo.objective.sliceDurationInSeconds", - }, - }, - "slo.objective.target": Object { - "terms": Object { - "field": "slo.objective.target", - }, - }, "slo.revision": Object { "terms": Object { "field": "slo.revision", }, }, - "slo.tags": Object { - "terms": Object { - "field": "slo.tags", - }, - }, - "slo.timeWindow.duration": Object { - "terms": Object { - "field": "slo.timeWindow.duration", - }, - }, - "slo.timeWindow.type": Object { - "terms": Object { - "field": "slo.timeWindow.type", - }, - }, }, }, "settings": Object { @@ -268,33 +218,9 @@ Object { }, }, "runtime_mappings": Object { - "slo.budgetingMethod": Object { - "script": Object { - "source": "emit('timeslices')", - }, - "type": "keyword", - }, - "slo.description": Object { - "script": Object { - "source": "emit('irrelevant')", - }, - "type": "keyword", - }, - "slo.groupBy": Object { - "script": Object { - "source": "emit('*')", - }, - "type": "keyword", - }, "slo.id": Object { "script": Object { - "source": Any, - }, - "type": "keyword", - }, - "slo.indicator.type": Object { - "script": Object { - "source": "emit('sli.kql.custom')", + "source": "emit('irrelevant')", }, "type": "keyword", }, @@ -304,48 +230,12 @@ Object { }, "type": "keyword", }, - "slo.name": Object { - "script": Object { - "source": "emit('irrelevant')", - }, - "type": "keyword", - }, - "slo.objective.sliceDurationInSeconds": Object { - "script": Object { - "source": "emit(120)", - }, - "type": "long", - }, - "slo.objective.target": Object { - "script": Object { - "source": "emit(0.98)", - }, - "type": "double", - }, "slo.revision": Object { "script": Object { "source": "emit(1)", }, "type": "long", }, - "slo.tags": Object { - "script": Object { - "source": "emit('critical,k8s')", - }, - "type": "keyword", - }, - "slo.timeWindow.duration": Object { - "script": Object { - "source": "emit('7d')", - }, - "type": "keyword", - }, - "slo.timeWindow.type": Object { - "script": Object { - "source": "emit('rolling')", - }, - "type": "keyword", - }, }, }, "sync": Object { @@ -354,7 +244,7 @@ Object { "field": "log_timestamp", }, }, - "transform_id": Any, + "transform_id": "slo-irrelevant-1", } `; @@ -363,12 +253,12 @@ Object { "_meta": Object { "managed": true, "managed_by": "observability", - "version": 2, + "version": 3, }, - "description": "Rolled-up SLI data for SLO: irrelevant", + "description": "Rolled-up SLI data for SLO: irrelevant [id: irrelevant, revision: 1]", "dest": Object { - "index": ".slo-observability.sli-v2", - "pipeline": ".slo-observability.sli.pipeline", + "index": ".slo-observability.sli-v3", + "pipeline": ".slo-observability.sli.pipeline-v3", }, "frequency": "1m", "pivot": Object { @@ -402,66 +292,21 @@ Object { "fixed_interval": "1m", }, }, - "slo.budgetingMethod": Object { - "terms": Object { - "field": "slo.budgetingMethod", - }, - }, - "slo.description": Object { - "terms": Object { - "field": "slo.description", - }, - }, - "slo.groupBy": Object { - "terms": Object { - "field": "slo.groupBy", - }, - }, "slo.id": Object { "terms": Object { "field": "slo.id", }, }, - "slo.indicator.type": Object { - "terms": Object { - "field": "slo.indicator.type", - }, - }, "slo.instanceId": Object { "terms": Object { "field": "slo.instanceId", }, }, - "slo.name": Object { - "terms": Object { - "field": "slo.name", - }, - }, - "slo.objective.target": Object { - "terms": Object { - "field": "slo.objective.target", - }, - }, "slo.revision": Object { "terms": Object { "field": "slo.revision", }, }, - "slo.tags": Object { - "terms": Object { - "field": "slo.tags", - }, - }, - "slo.timeWindow.duration": Object { - "terms": Object { - "field": "slo.timeWindow.duration", - }, - }, - "slo.timeWindow.type": Object { - "terms": Object { - "field": "slo.timeWindow.type", - }, - }, }, }, "settings": Object { @@ -499,33 +344,9 @@ Object { }, }, "runtime_mappings": Object { - "slo.budgetingMethod": Object { - "script": Object { - "source": "emit('occurrences')", - }, - "type": "keyword", - }, - "slo.description": Object { - "script": Object { - "source": "emit('irrelevant')", - }, - "type": "keyword", - }, - "slo.groupBy": Object { - "script": Object { - "source": "emit('*')", - }, - "type": "keyword", - }, "slo.id": Object { "script": Object { - "source": Any, - }, - "type": "keyword", - }, - "slo.indicator.type": Object { - "script": Object { - "source": "emit('sli.kql.custom')", + "source": "emit('irrelevant')", }, "type": "keyword", }, @@ -535,42 +356,12 @@ Object { }, "type": "keyword", }, - "slo.name": Object { - "script": Object { - "source": "emit('irrelevant')", - }, - "type": "keyword", - }, - "slo.objective.target": Object { - "script": Object { - "source": "emit(0.999)", - }, - "type": "double", - }, "slo.revision": Object { "script": Object { "source": "emit(1)", }, "type": "long", }, - "slo.tags": Object { - "script": Object { - "source": "emit('critical,k8s')", - }, - "type": "keyword", - }, - "slo.timeWindow.duration": Object { - "script": Object { - "source": "emit('7d')", - }, - "type": "keyword", - }, - "slo.timeWindow.type": Object { - "script": Object { - "source": "emit('rolling')", - }, - "type": "keyword", - }, }, }, "sync": Object { @@ -579,6 +370,6 @@ Object { "field": "log_timestamp", }, }, - "transform_id": Any, + "transform_id": "slo-irrelevant-1", } `; diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/metric_custom.test.ts.snap b/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/metric_custom.test.ts.snap index 55ed414bd2ec7..ed5ee454a5596 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/metric_custom.test.ts.snap +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/metric_custom.test.ts.snap @@ -117,12 +117,12 @@ Object { "_meta": Object { "managed": true, "managed_by": "observability", - "version": 2, + "version": 3, }, - "description": "Rolled-up SLI data for SLO: irrelevant", + "description": "Rolled-up SLI data for SLO: irrelevant [id: irrelevant, revision: 1]", "dest": Object { - "index": ".slo-observability.sli-v2", - "pipeline": ".slo-observability.sli.pipeline", + "index": ".slo-observability.sli-v3", + "pipeline": ".slo-observability.sli.pipeline-v3", }, "frequency": "1m", "pivot": Object { @@ -203,71 +203,21 @@ Object { "fixed_interval": "2m", }, }, - "slo.budgetingMethod": Object { - "terms": Object { - "field": "slo.budgetingMethod", - }, - }, - "slo.description": Object { - "terms": Object { - "field": "slo.description", - }, - }, - "slo.groupBy": Object { - "terms": Object { - "field": "slo.groupBy", - }, - }, "slo.id": Object { "terms": Object { "field": "slo.id", }, }, - "slo.indicator.type": Object { - "terms": Object { - "field": "slo.indicator.type", - }, - }, "slo.instanceId": Object { "terms": Object { "field": "slo.instanceId", }, }, - "slo.name": Object { - "terms": Object { - "field": "slo.name", - }, - }, - "slo.objective.sliceDurationInSeconds": Object { - "terms": Object { - "field": "slo.objective.sliceDurationInSeconds", - }, - }, - "slo.objective.target": Object { - "terms": Object { - "field": "slo.objective.target", - }, - }, "slo.revision": Object { "terms": Object { "field": "slo.revision", }, }, - "slo.tags": Object { - "terms": Object { - "field": "slo.tags", - }, - }, - "slo.timeWindow.duration": Object { - "terms": Object { - "field": "slo.timeWindow.duration", - }, - }, - "slo.timeWindow.type": Object { - "terms": Object { - "field": "slo.timeWindow.type", - }, - }, }, }, "settings": Object { @@ -305,33 +255,9 @@ Object { }, }, "runtime_mappings": Object { - "slo.budgetingMethod": Object { - "script": Object { - "source": "emit('timeslices')", - }, - "type": "keyword", - }, - "slo.description": Object { - "script": Object { - "source": "emit('irrelevant')", - }, - "type": "keyword", - }, - "slo.groupBy": Object { - "script": Object { - "source": "emit('*')", - }, - "type": "keyword", - }, "slo.id": Object { "script": Object { - "source": Any, - }, - "type": "keyword", - }, - "slo.indicator.type": Object { - "script": Object { - "source": "emit('sli.metric.custom')", + "source": "emit('irrelevant')", }, "type": "keyword", }, @@ -341,48 +267,12 @@ Object { }, "type": "keyword", }, - "slo.name": Object { - "script": Object { - "source": "emit('irrelevant')", - }, - "type": "keyword", - }, - "slo.objective.sliceDurationInSeconds": Object { - "script": Object { - "source": "emit(120)", - }, - "type": "long", - }, - "slo.objective.target": Object { - "script": Object { - "source": "emit(0.98)", - }, - "type": "double", - }, "slo.revision": Object { "script": Object { "source": "emit(1)", }, "type": "long", }, - "slo.tags": Object { - "script": Object { - "source": "emit('critical,k8s')", - }, - "type": "keyword", - }, - "slo.timeWindow.duration": Object { - "script": Object { - "source": "emit('7d')", - }, - "type": "keyword", - }, - "slo.timeWindow.type": Object { - "script": Object { - "source": "emit('rolling')", - }, - "type": "keyword", - }, }, }, "sync": Object { @@ -391,7 +281,7 @@ Object { "field": "log_timestamp", }, }, - "transform_id": Any, + "transform_id": "slo-irrelevant-1", } `; @@ -400,12 +290,12 @@ Object { "_meta": Object { "managed": true, "managed_by": "observability", - "version": 2, + "version": 3, }, - "description": "Rolled-up SLI data for SLO: irrelevant", + "description": "Rolled-up SLI data for SLO: irrelevant [id: irrelevant, revision: 1]", "dest": Object { - "index": ".slo-observability.sli-v2", - "pipeline": ".slo-observability.sli.pipeline", + "index": ".slo-observability.sli-v3", + "pipeline": ".slo-observability.sli.pipeline-v3", }, "frequency": "1m", "pivot": Object { @@ -477,66 +367,21 @@ Object { "fixed_interval": "1m", }, }, - "slo.budgetingMethod": Object { - "terms": Object { - "field": "slo.budgetingMethod", - }, - }, - "slo.description": Object { - "terms": Object { - "field": "slo.description", - }, - }, - "slo.groupBy": Object { - "terms": Object { - "field": "slo.groupBy", - }, - }, "slo.id": Object { "terms": Object { "field": "slo.id", }, }, - "slo.indicator.type": Object { - "terms": Object { - "field": "slo.indicator.type", - }, - }, "slo.instanceId": Object { "terms": Object { "field": "slo.instanceId", }, }, - "slo.name": Object { - "terms": Object { - "field": "slo.name", - }, - }, - "slo.objective.target": Object { - "terms": Object { - "field": "slo.objective.target", - }, - }, "slo.revision": Object { "terms": Object { "field": "slo.revision", }, }, - "slo.tags": Object { - "terms": Object { - "field": "slo.tags", - }, - }, - "slo.timeWindow.duration": Object { - "terms": Object { - "field": "slo.timeWindow.duration", - }, - }, - "slo.timeWindow.type": Object { - "terms": Object { - "field": "slo.timeWindow.type", - }, - }, }, }, "settings": Object { @@ -574,33 +419,9 @@ Object { }, }, "runtime_mappings": Object { - "slo.budgetingMethod": Object { - "script": Object { - "source": "emit('occurrences')", - }, - "type": "keyword", - }, - "slo.description": Object { - "script": Object { - "source": "emit('irrelevant')", - }, - "type": "keyword", - }, - "slo.groupBy": Object { - "script": Object { - "source": "emit('*')", - }, - "type": "keyword", - }, "slo.id": Object { "script": Object { - "source": Any, - }, - "type": "keyword", - }, - "slo.indicator.type": Object { - "script": Object { - "source": "emit('sli.metric.custom')", + "source": "emit('irrelevant')", }, "type": "keyword", }, @@ -610,42 +431,12 @@ Object { }, "type": "keyword", }, - "slo.name": Object { - "script": Object { - "source": "emit('irrelevant')", - }, - "type": "keyword", - }, - "slo.objective.target": Object { - "script": Object { - "source": "emit(0.999)", - }, - "type": "double", - }, "slo.revision": Object { "script": Object { "source": "emit(1)", }, "type": "long", }, - "slo.tags": Object { - "script": Object { - "source": "emit('critical,k8s')", - }, - "type": "keyword", - }, - "slo.timeWindow.duration": Object { - "script": Object { - "source": "emit('7d')", - }, - "type": "keyword", - }, - "slo.timeWindow.type": Object { - "script": Object { - "source": "emit('rolling')", - }, - "type": "keyword", - }, }, }, "sync": Object { @@ -654,7 +445,7 @@ Object { "field": "log_timestamp", }, }, - "transform_id": Any, + "transform_id": "slo-irrelevant-1", } `; diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/timeslice_metric.test.ts.snap b/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/timeslice_metric.test.ts.snap index e2698ba3e1793..48ebb4de1e8d6 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/timeslice_metric.test.ts.snap +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/__snapshots__/timeslice_metric.test.ts.snap @@ -33,12 +33,12 @@ Object { "_meta": Object { "managed": true, "managed_by": "observability", - "version": 2, + "version": 3, }, - "description": "Rolled-up SLI data for SLO: irrelevant", + "description": "Rolled-up SLI data for SLO: irrelevant [id: irrelevant, revision: 1]", "dest": Object { - "index": ".slo-observability.sli-v2", - "pipeline": ".slo-observability.sli.pipeline", + "index": ".slo-observability.sli-v3", + "pipeline": ".slo-observability.sli.pipeline-v3", }, "frequency": "1m", "pivot": Object { @@ -173,71 +173,21 @@ Object { "fixed_interval": "2m", }, }, - "slo.budgetingMethod": Object { - "terms": Object { - "field": "slo.budgetingMethod", - }, - }, - "slo.description": Object { - "terms": Object { - "field": "slo.description", - }, - }, - "slo.groupBy": Object { - "terms": Object { - "field": "slo.groupBy", - }, - }, "slo.id": Object { "terms": Object { "field": "slo.id", }, }, - "slo.indicator.type": Object { - "terms": Object { - "field": "slo.indicator.type", - }, - }, "slo.instanceId": Object { "terms": Object { "field": "slo.instanceId", }, }, - "slo.name": Object { - "terms": Object { - "field": "slo.name", - }, - }, - "slo.objective.sliceDurationInSeconds": Object { - "terms": Object { - "field": "slo.objective.sliceDurationInSeconds", - }, - }, - "slo.objective.target": Object { - "terms": Object { - "field": "slo.objective.target", - }, - }, "slo.revision": Object { "terms": Object { "field": "slo.revision", }, }, - "slo.tags": Object { - "terms": Object { - "field": "slo.tags", - }, - }, - "slo.timeWindow.duration": Object { - "terms": Object { - "field": "slo.timeWindow.duration", - }, - }, - "slo.timeWindow.type": Object { - "terms": Object { - "field": "slo.timeWindow.type", - }, - }, }, }, "settings": Object { @@ -272,33 +222,9 @@ Object { }, }, "runtime_mappings": Object { - "slo.budgetingMethod": Object { - "script": Object { - "source": "emit('timeslices')", - }, - "type": "keyword", - }, - "slo.description": Object { - "script": Object { - "source": "emit('irrelevant')", - }, - "type": "keyword", - }, - "slo.groupBy": Object { - "script": Object { - "source": "emit('*')", - }, - "type": "keyword", - }, "slo.id": Object { "script": Object { - "source": Any, - }, - "type": "keyword", - }, - "slo.indicator.type": Object { - "script": Object { - "source": "emit('sli.metric.timeslice')", + "source": "emit('irrelevant')", }, "type": "keyword", }, @@ -308,48 +234,12 @@ Object { }, "type": "keyword", }, - "slo.name": Object { - "script": Object { - "source": "emit('irrelevant')", - }, - "type": "keyword", - }, - "slo.objective.sliceDurationInSeconds": Object { - "script": Object { - "source": "emit(120)", - }, - "type": "long", - }, - "slo.objective.target": Object { - "script": Object { - "source": "emit(0.98)", - }, - "type": "double", - }, "slo.revision": Object { "script": Object { "source": "emit(1)", }, "type": "long", }, - "slo.tags": Object { - "script": Object { - "source": "emit('critical,k8s')", - }, - "type": "keyword", - }, - "slo.timeWindow.duration": Object { - "script": Object { - "source": "emit('7d')", - }, - "type": "keyword", - }, - "slo.timeWindow.type": Object { - "script": Object { - "source": "emit('rolling')", - }, - "type": "keyword", - }, }, }, "sync": Object { @@ -358,7 +248,7 @@ Object { "field": "@timestamp", }, }, - "transform_id": Any, + "transform_id": "slo-irrelevant-1", } `; @@ -367,12 +257,12 @@ Object { "_meta": Object { "managed": true, "managed_by": "observability", - "version": 2, + "version": 3, }, - "description": "Rolled-up SLI data for SLO: irrelevant", + "description": "Rolled-up SLI data for SLO: irrelevant [id: irrelevant, revision: 1]", "dest": Object { - "index": ".slo-observability.sli-v2", - "pipeline": ".slo-observability.sli.pipeline", + "index": ".slo-observability.sli-v3", + "pipeline": ".slo-observability.sli.pipeline-v3", }, "frequency": "1m", "pivot": Object { @@ -507,71 +397,21 @@ Object { "fixed_interval": "2m", }, }, - "slo.budgetingMethod": Object { - "terms": Object { - "field": "slo.budgetingMethod", - }, - }, - "slo.description": Object { - "terms": Object { - "field": "slo.description", - }, - }, - "slo.groupBy": Object { - "terms": Object { - "field": "slo.groupBy", - }, - }, "slo.id": Object { "terms": Object { "field": "slo.id", }, }, - "slo.indicator.type": Object { - "terms": Object { - "field": "slo.indicator.type", - }, - }, "slo.instanceId": Object { "terms": Object { "field": "slo.instanceId", }, }, - "slo.name": Object { - "terms": Object { - "field": "slo.name", - }, - }, - "slo.objective.sliceDurationInSeconds": Object { - "terms": Object { - "field": "slo.objective.sliceDurationInSeconds", - }, - }, - "slo.objective.target": Object { - "terms": Object { - "field": "slo.objective.target", - }, - }, "slo.revision": Object { "terms": Object { "field": "slo.revision", }, }, - "slo.tags": Object { - "terms": Object { - "field": "slo.tags", - }, - }, - "slo.timeWindow.duration": Object { - "terms": Object { - "field": "slo.timeWindow.duration", - }, - }, - "slo.timeWindow.type": Object { - "terms": Object { - "field": "slo.timeWindow.type", - }, - }, }, }, "settings": Object { @@ -606,33 +446,9 @@ Object { }, }, "runtime_mappings": Object { - "slo.budgetingMethod": Object { - "script": Object { - "source": "emit('timeslices')", - }, - "type": "keyword", - }, - "slo.description": Object { - "script": Object { - "source": "emit('irrelevant')", - }, - "type": "keyword", - }, - "slo.groupBy": Object { - "script": Object { - "source": "emit('*')", - }, - "type": "keyword", - }, "slo.id": Object { "script": Object { - "source": Any, - }, - "type": "keyword", - }, - "slo.indicator.type": Object { - "script": Object { - "source": "emit('sli.metric.timeslice')", + "source": "emit('irrelevant')", }, "type": "keyword", }, @@ -642,48 +458,12 @@ Object { }, "type": "keyword", }, - "slo.name": Object { - "script": Object { - "source": "emit('irrelevant')", - }, - "type": "keyword", - }, - "slo.objective.sliceDurationInSeconds": Object { - "script": Object { - "source": "emit(120)", - }, - "type": "long", - }, - "slo.objective.target": Object { - "script": Object { - "source": "emit(0.98)", - }, - "type": "double", - }, "slo.revision": Object { "script": Object { "source": "emit(1)", }, "type": "long", }, - "slo.tags": Object { - "script": Object { - "source": "emit('critical,k8s')", - }, - "type": "keyword", - }, - "slo.timeWindow.duration": Object { - "script": Object { - "source": "emit('7d')", - }, - "type": "keyword", - }, - "slo.timeWindow.type": Object { - "script": Object { - "source": "emit('rolling')", - }, - "type": "keyword", - }, }, }, "sync": Object { @@ -692,6 +472,6 @@ Object { "field": "@timestamp", }, }, - "transform_id": Any, + "transform_id": "slo-irrelevant-1", } `; diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_duration.test.ts b/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_duration.test.ts index f19ee083a3115..6b5491e487191 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_duration.test.ts +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_duration.test.ts @@ -17,32 +17,20 @@ const generator = new ApmTransactionDurationTransformGenerator(); describe('APM Transaction Duration Transform Generator', () => { it('returns the expected transform params with every specified indicator params', () => { - const slo = createSLO({ indicator: createAPMTransactionDurationIndicator() }); + const slo = createSLO({ id: 'irrelevant', indicator: createAPMTransactionDurationIndicator() }); const transform = generator.getTransformParams(slo); - expect(transform).toMatchSnapshot({ - transform_id: expect.any(String), - source: { runtime_mappings: { 'slo.id': { script: { source: expect.any(String) } } } }, - }); - expect(transform.transform_id).toEqual(`slo-${slo.id}-${slo.revision}`); - expect(transform.source.runtime_mappings!['slo.id']).toMatchObject({ - script: { source: `emit('${slo.id}')` }, - }); - expect(transform.source.runtime_mappings!['slo.revision']).toMatchObject({ - script: { source: `emit(${slo.revision})` }, - }); + expect(transform).toMatchSnapshot(); }); it('returns the expected transform params for timeslices slo', () => { const slo = createSLOWithTimeslicesBudgetingMethod({ + id: 'irrelevant', indicator: createAPMTransactionDurationIndicator(), }); const transform = generator.getTransformParams(slo); - expect(transform).toMatchSnapshot({ - transform_id: expect.any(String), - source: { runtime_mappings: { 'slo.id': { script: { source: expect.any(String) } } } }, - }); + expect(transform).toMatchSnapshot(); }); it("does not include the query filter when params are '*'", () => { diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_error_rate.test.ts b/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_error_rate.test.ts index 54f72f1961588..9934f5ea27a51 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_error_rate.test.ts +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/apm_transaction_error_rate.test.ts @@ -17,32 +17,23 @@ const generator = new ApmTransactionErrorRateTransformGenerator(); describe('APM Transaction Error Rate Transform Generator', () => { it('returns the expected transform params with every specified indicator params', async () => { - const slo = createSLO({ indicator: createAPMTransactionErrorRateIndicator() }); + const slo = createSLO({ + id: 'irrelevant', + indicator: createAPMTransactionErrorRateIndicator(), + }); const transform = generator.getTransformParams(slo); - expect(transform).toMatchSnapshot({ - transform_id: expect.any(String), - source: { runtime_mappings: { 'slo.id': { script: { source: expect.any(String) } } } }, - }); - expect(transform.transform_id).toEqual(`slo-${slo.id}-${slo.revision}`); - expect(transform.source.runtime_mappings!['slo.id']).toMatchObject({ - script: { source: `emit('${slo.id}')` }, - }); - expect(transform.source.runtime_mappings!['slo.revision']).toMatchObject({ - script: { source: `emit(${slo.revision})` }, - }); + expect(transform).toMatchSnapshot(); }); it('returns the expected transform params for timeslices slo', async () => { const slo = createSLOWithTimeslicesBudgetingMethod({ + id: 'irrelevant', indicator: createAPMTransactionErrorRateIndicator(), }); const transform = generator.getTransformParams(slo); - expect(transform).toMatchSnapshot({ - transform_id: expect.any(String), - source: { runtime_mappings: { 'slo.id': { script: { source: expect.any(String) } } } }, - }); + expect(transform).toMatchSnapshot(); }); it("does not include the query filter when params are '*'", async () => { diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/histogram.test.ts b/x-pack/plugins/observability/server/services/slo/transform_generators/histogram.test.ts index fdf0bc27563fe..77ef70e75ca5f 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_generators/histogram.test.ts +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/histogram.test.ts @@ -30,6 +30,7 @@ describe('Histogram Transform Generator', () => { }); expect(() => generator.getTransformParams(anSLO)).toThrow(/Invalid KQL: foo:/); }); + it('throws when the total filter is invalid', () => { const anSLO = createSLO({ indicator: createHistogramIndicator({ @@ -42,6 +43,7 @@ describe('Histogram Transform Generator', () => { }); expect(() => generator.getTransformParams(anSLO)).toThrow(/Invalid KQL: foo:/); }); + it('throws when the query_filter is invalid', () => { const anSLO = createSLO({ indicator: createHistogramIndicator({ filter: '{ kql.query: invalid' }), @@ -51,32 +53,20 @@ describe('Histogram Transform Generator', () => { }); it('returns the expected transform params with every specified indicator params', async () => { - const anSLO = createSLO({ indicator: createHistogramIndicator() }); + const anSLO = createSLO({ id: 'irrelevant', indicator: createHistogramIndicator() }); const transform = generator.getTransformParams(anSLO); - expect(transform).toMatchSnapshot({ - transform_id: expect.any(String), - source: { runtime_mappings: { 'slo.id': { script: { source: expect.any(String) } } } }, - }); - expect(transform.transform_id).toEqual(`slo-${anSLO.id}-${anSLO.revision}`); - expect(transform.source.runtime_mappings!['slo.id']).toMatchObject({ - script: { source: `emit('${anSLO.id}')` }, - }); - expect(transform.source.runtime_mappings!['slo.revision']).toMatchObject({ - script: { source: `emit(${anSLO.revision})` }, - }); + expect(transform).toMatchSnapshot(); }); it('returns the expected transform params for timeslices slo', async () => { const anSLO = createSLOWithTimeslicesBudgetingMethod({ + id: 'irrelevant', indicator: createHistogramIndicator(), }); const transform = generator.getTransformParams(anSLO); - expect(transform).toMatchSnapshot({ - transform_id: expect.any(String), - source: { runtime_mappings: { 'slo.id': { script: { source: expect.any(String) } } } }, - }); + expect(transform).toMatchSnapshot(); }); it('filters the source using the kql query', async () => { diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/kql_custom.test.ts b/x-pack/plugins/observability/server/services/slo/transform_generators/kql_custom.test.ts index dd4511ec44bfd..1512bb5a655ab 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_generators/kql_custom.test.ts +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/kql_custom.test.ts @@ -37,32 +37,20 @@ describe('KQL Custom Transform Generator', () => { }); it('returns the expected transform params with every specified indicator params', async () => { - const anSLO = createSLO({ indicator: createKQLCustomIndicator() }); + const anSLO = createSLO({ id: 'irrelevant', indicator: createKQLCustomIndicator() }); const transform = generator.getTransformParams(anSLO); - expect(transform).toMatchSnapshot({ - transform_id: expect.any(String), - source: { runtime_mappings: { 'slo.id': { script: { source: expect.any(String) } } } }, - }); - expect(transform.transform_id).toEqual(`slo-${anSLO.id}-${anSLO.revision}`); - expect(transform.source.runtime_mappings!['slo.id']).toMatchObject({ - script: { source: `emit('${anSLO.id}')` }, - }); - expect(transform.source.runtime_mappings!['slo.revision']).toMatchObject({ - script: { source: `emit(${anSLO.revision})` }, - }); + expect(transform).toMatchSnapshot(); }); it('returns the expected transform params for timeslices slo', async () => { const anSLO = createSLOWithTimeslicesBudgetingMethod({ + id: 'irrelevant', indicator: createKQLCustomIndicator(), }); const transform = generator.getTransformParams(anSLO); - expect(transform).toMatchSnapshot({ - transform_id: expect.any(String), - source: { runtime_mappings: { 'slo.id': { script: { source: expect.any(String) } } } }, - }); + expect(transform).toMatchSnapshot(); }); it('filters the source using the kql query', async () => { diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/metric_custom.test.ts b/x-pack/plugins/observability/server/services/slo/transform_generators/metric_custom.test.ts index 69685bad0c09e..9ebacde28f0ed 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_generators/metric_custom.test.ts +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/metric_custom.test.ts @@ -69,32 +69,20 @@ describe('Metric Custom Transform Generator', () => { }); it('returns the expected transform params with every specified indicator params', async () => { - const anSLO = createSLO({ indicator: createMetricCustomIndicator() }); + const anSLO = createSLO({ id: 'irrelevant', indicator: createMetricCustomIndicator() }); const transform = generator.getTransformParams(anSLO); - expect(transform).toMatchSnapshot({ - transform_id: expect.any(String), - source: { runtime_mappings: { 'slo.id': { script: { source: expect.any(String) } } } }, - }); - expect(transform.transform_id).toEqual(`slo-${anSLO.id}-${anSLO.revision}`); - expect(transform.source.runtime_mappings!['slo.id']).toMatchObject({ - script: { source: `emit('${anSLO.id}')` }, - }); - expect(transform.source.runtime_mappings!['slo.revision']).toMatchObject({ - script: { source: `emit(${anSLO.revision})` }, - }); + expect(transform).toMatchSnapshot(); }); it('returns the expected transform params for timeslices slo', async () => { const anSLO = createSLOWithTimeslicesBudgetingMethod({ + id: 'irrelevant', indicator: createMetricCustomIndicator(), }); const transform = generator.getTransformParams(anSLO); - expect(transform).toMatchSnapshot({ - transform_id: expect.any(String), - source: { runtime_mappings: { 'slo.id': { script: { source: expect.any(String) } } } }, - }); + expect(transform).toMatchSnapshot(); }); it('filters the source using the kql query', async () => { diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/timeslice_metric.test.ts b/x-pack/plugins/observability/server/services/slo/transform_generators/timeslice_metric.test.ts index aa21f7e0ceb0e..7d221c25c27ed 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_generators/timeslice_metric.test.ts +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/timeslice_metric.test.ts @@ -70,33 +70,22 @@ describe('Timeslice Metric Transform Generator', () => { it('returns the expected transform params with every specified indicator params', async () => { const anSLO = createSLOWithTimeslicesBudgetingMethod({ + id: 'irrelevant', indicator: everythingIndicator, }); const transform = generator.getTransformParams(anSLO); - expect(transform).toMatchSnapshot({ - transform_id: expect.any(String), - source: { runtime_mappings: { 'slo.id': { script: { source: expect.any(String) } } } }, - }); - expect(transform.transform_id).toEqual(`slo-${anSLO.id}-${anSLO.revision}`); - expect(transform.source.runtime_mappings!['slo.id']).toMatchObject({ - script: { source: `emit('${anSLO.id}')` }, - }); - expect(transform.source.runtime_mappings!['slo.revision']).toMatchObject({ - script: { source: `emit(${anSLO.revision})` }, - }); + expect(transform).toMatchSnapshot(); }); it('returns the expected transform params for timeslices slo', async () => { const anSLO = createSLOWithTimeslicesBudgetingMethod({ + id: 'irrelevant', indicator: everythingIndicator, }); const transform = generator.getTransformParams(anSLO); - expect(transform).toMatchSnapshot({ - transform_id: expect.any(String), - source: { runtime_mappings: { 'slo.id': { script: { source: expect.any(String) } } } }, - }); + expect(transform).toMatchSnapshot(); }); it('filters the source using the kql query', async () => { diff --git a/x-pack/plugins/observability/server/services/slo/transform_generators/transform_generator.ts b/x-pack/plugins/observability/server/services/slo/transform_generators/transform_generator.ts index 42572e61b38ab..7085f69b76422 100644 --- a/x-pack/plugins/observability/server/services/slo/transform_generators/transform_generator.ts +++ b/x-pack/plugins/observability/server/services/slo/transform_generators/transform_generator.ts @@ -32,12 +32,6 @@ export abstract class TransformGenerator { source: `emit(${slo.revision})`, }, }, - 'slo.groupBy': { - type: 'keyword', - script: { - source: `emit('${!!slo.groupBy ? slo.groupBy : ALL_VALUE}')`, - }, - }, ...(mustIncludeAllInstanceId && { 'slo.instanceId': { type: 'keyword', @@ -46,67 +40,11 @@ export abstract class TransformGenerator { }, }, }), - 'slo.name': { - type: 'keyword', - script: { - source: `emit('${slo.name}')`, - }, - }, - 'slo.description': { - type: 'keyword', - script: { - source: `emit('${slo.description}')`, - }, - }, - 'slo.tags': { - type: 'keyword', - script: { - source: `emit('${slo.tags}')`, - }, - }, - 'slo.indicator.type': { - type: 'keyword', - script: { - source: `emit('${slo.indicator.type}')`, - }, - }, - 'slo.objective.target': { - type: 'double', - script: { - source: `emit(${slo.objective.target})`, - }, - }, - ...(slo.objective.timesliceWindow && { - 'slo.objective.sliceDurationInSeconds': { - type: 'long', - script: { - source: `emit(${slo.objective.timesliceWindow!.asSeconds()})`, - }, - }, - }), - 'slo.budgetingMethod': { - type: 'keyword', - script: { - source: `emit('${slo.budgetingMethod}')`, - }, - }, - 'slo.timeWindow.duration': { - type: 'keyword', - script: { - source: `emit('${slo.timeWindow.duration.format()}')`, - }, - }, - 'slo.timeWindow.type': { - type: 'keyword', - script: { - source: `emit('${slo.timeWindow.type}')`, - }, - }, }; } public buildDescription(slo: SLO): string { - return `Rolled-up SLI data for SLO: ${slo.name}`; + return `Rolled-up SLI data for SLO: ${slo.name} [id: ${slo.id}, revision: ${slo.revision}]`; } public buildCommonGroupBy( @@ -119,27 +57,27 @@ export abstract class TransformGenerator { fixedInterval = slo.objective.timesliceWindow!.format(); } - const instanceIdField = - slo.groupBy !== '' && slo.groupBy !== ALL_VALUE ? slo.groupBy : 'slo.instanceId'; + const groupings = + slo.groupBy !== '' && slo.groupBy !== ALL_VALUE + ? [slo.groupBy].flat().reduce( + (acc, field) => { + return { + ...acc, + [`slo.groupings.${field}`]: { + terms: { + field, + }, + }, + }; + }, + { 'slo.instanceId': { terms: { field: slo.groupBy } } } + ) + : { 'slo.instanceId': { terms: { field: 'slo.instanceId' } } }; return { 'slo.id': { terms: { field: 'slo.id' } }, 'slo.revision': { terms: { field: 'slo.revision' } }, - 'slo.groupBy': { terms: { field: 'slo.groupBy' } }, - 'slo.instanceId': { terms: { field: instanceIdField } }, - 'slo.name': { terms: { field: 'slo.name' } }, - 'slo.description': { terms: { field: 'slo.description' } }, - 'slo.tags': { terms: { field: 'slo.tags' } }, - 'slo.indicator.type': { terms: { field: 'slo.indicator.type' } }, - 'slo.objective.target': { terms: { field: 'slo.objective.target' } }, - ...(slo.objective.timesliceWindow && { - 'slo.objective.sliceDurationInSeconds': { - terms: { field: 'slo.objective.sliceDurationInSeconds' }, - }, - }), - 'slo.budgetingMethod': { terms: { field: 'slo.budgetingMethod' } }, - 'slo.timeWindow.duration': { terms: { field: 'slo.timeWindow.duration' } }, - 'slo.timeWindow.type': { terms: { field: 'slo.timeWindow.type' } }, + ...groupings, ...extraGroupByFields, // @timestamp field defined in the destination index '@timestamp': { diff --git a/x-pack/plugins/observability/server/services/slo/update_slo.test.ts b/x-pack/plugins/observability/server/services/slo/update_slo.test.ts index 1fb6550e12c47..a8642cfa921f2 100644 --- a/x-pack/plugins/observability/server/services/slo/update_slo.test.ts +++ b/x-pack/plugins/observability/server/services/slo/update_slo.test.ts @@ -6,11 +6,13 @@ */ import { ElasticsearchClient } from '@kbn/core/server'; -import { elasticsearchServiceMock } from '@kbn/core/server/mocks'; +import { elasticsearchServiceMock, loggingSystemMock } from '@kbn/core/server/mocks'; +import { MockedLogger } from '@kbn/logging-mocks'; import { UpdateSLOParams } from '@kbn/slo-schema'; -import { cloneDeep, pick, omit } from 'lodash'; +import { cloneDeep, omit, pick } from 'lodash'; import { + getSLOSummaryTransformId, getSLOTransformId, SLO_DESTINATION_INDEX_PATTERN, SLO_SUMMARY_DESTINATION_INDEX_PATTERN, @@ -22,7 +24,12 @@ import { createSLO, createSLOWithTimeslicesBudgetingMethod, } from './fixtures/slo'; -import { createSLORepositoryMock, createTransformManagerMock } from './mocks'; +import { weeklyCalendarAligned } from './fixtures/time_window'; +import { + createSLORepositoryMock, + createSummaryTransformManagerMock, + createTransformManagerMock, +} from './mocks'; import { SLORepository } from './slo_repository'; import { TransformManager } from './transform_manager'; import { UpdateSLO } from './update_slo'; @@ -31,13 +38,24 @@ describe('UpdateSLO', () => { let mockRepository: jest.Mocked; let mockTransformManager: jest.Mocked; let mockEsClient: jest.Mocked; + let loggerMock: jest.Mocked; + let mockSummaryTransformManager: jest.Mocked; let updateSLO: UpdateSLO; beforeEach(() => { mockRepository = createSLORepositoryMock(); mockTransformManager = createTransformManagerMock(); + loggerMock = loggingSystemMock.createLogger(); + mockSummaryTransformManager = createSummaryTransformManagerMock(); mockEsClient = elasticsearchServiceMock.createElasticsearchClient(); - updateSLO = new UpdateSLO(mockRepository, mockTransformManager, mockEsClient); + updateSLO = new UpdateSLO( + mockRepository, + mockTransformManager, + mockSummaryTransformManager, + mockEsClient, + loggerMock, + 'some-space' + ); }); describe('when the update payload does not change the original SLO', () => { @@ -45,12 +63,18 @@ describe('UpdateSLO', () => { expect(mockTransformManager.stop).not.toBeCalled(); expect(mockTransformManager.uninstall).not.toBeCalled(); expect(mockTransformManager.install).not.toBeCalled(); - expect(mockTransformManager.preview).not.toBeCalled(); expect(mockTransformManager.start).not.toBeCalled(); + + expect(mockSummaryTransformManager.stop).not.toBeCalled(); + expect(mockSummaryTransformManager.uninstall).not.toBeCalled(); + expect(mockSummaryTransformManager.install).not.toBeCalled(); + expect(mockSummaryTransformManager.start).not.toBeCalled(); + expect(mockEsClient.deleteByQuery).not.toBeCalled(); + expect(mockEsClient.ingest.putPipeline).not.toBeCalled(); } - it('returns early with a full identical SLO payload', async () => { + it('returns early with a fully identical SLO payload', async () => { const slo = createSLO(); mockRepository.findById.mockResolvedValueOnce(slo); const updatePayload: UpdateSLOParams = omit(cloneDeep(slo), [ @@ -58,6 +82,7 @@ describe('UpdateSLO', () => { 'revision', 'createdAt', 'updatedAt', + 'version', 'enabled', ]); @@ -157,111 +182,116 @@ describe('UpdateSLO', () => { }); }); - it('updates the settings correctly', async () => { - const slo = createSLO(); - mockRepository.findById.mockResolvedValueOnce(slo); + describe('handles breaking changes', () => { + it('consideres a settings change as a breaking change', async () => { + const slo = createSLO(); + mockRepository.findById.mockResolvedValueOnce(slo); - const newSettings = { ...slo.settings, timestamp_field: 'newField' }; - await updateSLO.execute(slo.id, { settings: newSettings }); + const newSettings = { ...slo.settings, timestamp_field: 'newField' }; + await updateSLO.execute(slo.id, { settings: newSettings }); + + expectDeletionOfOriginalSLOResources(slo); + expect(mockRepository.save).toHaveBeenCalledWith( + expect.objectContaining({ + ...slo, + settings: newSettings, + revision: 2, + updatedAt: expect.anything(), + }) + ); + expectInstallationOfUpdatedSLOResources(); + }); - expectDeletionOfOriginalSLO(slo); - expect(mockRepository.save).toBeCalledWith( - expect.objectContaining({ - ...slo, - settings: newSettings, - revision: 2, - updatedAt: expect.anything(), - }) - ); - expectInstallationOfNewSLOTransform(); - }); + it('consideres a budgeting method change as a breaking change', async () => { + const slo = createSLO({ budgetingMethod: 'occurrences' }); + mockRepository.findById.mockResolvedValueOnce(slo); - it('updates the budgeting method correctly', async () => { - const slo = createSLO({ budgetingMethod: 'occurrences' }); - mockRepository.findById.mockResolvedValueOnce(slo); - - await updateSLO.execute(slo.id, { - budgetingMethod: 'timeslices', - objective: { - target: slo.objective.target, - timesliceTarget: 0.9, - timesliceWindow: oneMinute(), - }, + await updateSLO.execute(slo.id, { + budgetingMethod: 'timeslices', + objective: { + target: slo.objective.target, + timesliceTarget: 0.9, + timesliceWindow: oneMinute(), + }, + }); + + expectInstallationOfUpdatedSLOResources(); + expectDeletionOfOriginalSLOResources(slo); }); - expectDeletionOfOriginalSLO(slo); - expectInstallationOfNewSLOTransform(); - }); + it('consideres a timeWindow change as a breaking change', async () => { + const slo = createSLOWithTimeslicesBudgetingMethod(); + mockRepository.findById.mockResolvedValueOnce(slo); - it('updates the timeslice target correctly', async () => { - const slo = createSLOWithTimeslicesBudgetingMethod(); - mockRepository.findById.mockResolvedValueOnce(slo); + await updateSLO.execute(slo.id, { + timeWindow: weeklyCalendarAligned(), + }); - await updateSLO.execute(slo.id, { - objective: { - target: slo.objective.target, - timesliceTarget: 0.1, - timesliceWindow: slo.objective.timesliceWindow, - }, + expectInstallationOfUpdatedSLOResources(); + expectDeletionOfOriginalSLOResources(slo); }); - expectDeletionOfOriginalSLO(slo); - expectInstallationOfNewSLOTransform(); - }); + it('consideres a timeslice target change as a breaking change', async () => { + const slo = createSLOWithTimeslicesBudgetingMethod(); + mockRepository.findById.mockResolvedValueOnce(slo); - it('consideres a timeslice window change as a breaking change', async () => { - const slo = createSLOWithTimeslicesBudgetingMethod(); - mockRepository.findById.mockResolvedValueOnce(slo); + await updateSLO.execute(slo.id, { + objective: { + target: slo.objective.target, + timesliceTarget: 0.1, + timesliceWindow: slo.objective.timesliceWindow, + }, + }); - await updateSLO.execute(slo.id, { - objective: { - target: slo.objective.target, - timesliceTarget: slo.objective.timesliceTarget, - timesliceWindow: fiveMinute(), - }, + expectInstallationOfUpdatedSLOResources(); + expectDeletionOfOriginalSLOResources(slo); }); - expectDeletionOfOriginalSLO(slo); - expectInstallationOfNewSLOTransform(); - }); + it('consideres a timeslice window change as a breaking change', async () => { + const slo = createSLOWithTimeslicesBudgetingMethod(); + mockRepository.findById.mockResolvedValueOnce(slo); - it('index a temporary summary document', async () => { - const slo = createSLO({ - id: 'unique-id', - indicator: createAPMTransactionErrorRateIndicator({ environment: 'development' }), + await updateSLO.execute(slo.id, { + objective: { + target: slo.objective.target, + timesliceTarget: slo.objective.timesliceTarget, + timesliceWindow: fiveMinute(), + }, + }); + + expectInstallationOfUpdatedSLOResources(); + expectDeletionOfOriginalSLOResources(slo); }); - mockRepository.findById.mockResolvedValueOnce(slo); - const newIndicator = createAPMTransactionErrorRateIndicator({ environment: 'production' }); - await updateSLO.execute(slo.id, { indicator: newIndicator }); + it('consideres an indicator change as a breaking change', async () => { + const slo = createSLOWithTimeslicesBudgetingMethod(); + mockRepository.findById.mockResolvedValueOnce(slo); - expect(mockEsClient.index.mock.calls[0]).toMatchSnapshot(); - }); + await updateSLO.execute(slo.id, { + indicator: createAPMTransactionErrorRateIndicator(), + }); - it('removes the original data from the original SLO', async () => { - const slo = createSLO({ - indicator: createAPMTransactionErrorRateIndicator({ environment: 'development' }), + expectInstallationOfUpdatedSLOResources(); + expectDeletionOfOriginalSLOResources(slo); }); - mockRepository.findById.mockResolvedValueOnce(slo); - const newIndicator = createAPMTransactionErrorRateIndicator({ environment: 'production' }); - await updateSLO.execute(slo.id, { indicator: newIndicator }); + it('consideres a groupBy change as a breaking change', async () => { + const slo = createSLOWithTimeslicesBudgetingMethod(); + mockRepository.findById.mockResolvedValueOnce(slo); - expect(mockRepository.save).toBeCalledWith( - expect.objectContaining({ - ...slo, - indicator: newIndicator, - revision: 2, - updatedAt: expect.anything(), - }) - ); - expectInstallationOfNewSLOTransform(); - expectDeletionOfOriginalSLO(slo); + await updateSLO.execute(slo.id, { + groupBy: 'new-field', + }); + + expectInstallationOfUpdatedSLOResources(); + expectDeletionOfOriginalSLOResources(slo); + }); }); - describe('when error happens during the transform installation step', () => { + describe('when error happens during the update', () => { it('restores the previous SLO definition in the repository', async () => { const slo = createSLO({ + id: 'original-id', indicator: createAPMTransactionErrorRateIndicator({ environment: 'development' }), }); mockRepository.findById.mockResolvedValueOnce(slo); @@ -274,47 +304,38 @@ describe('UpdateSLO', () => { ); expect(mockRepository.save).toHaveBeenCalledWith(slo); - expect(mockTransformManager.preview).not.toHaveBeenCalled(); - expect(mockTransformManager.start).not.toHaveBeenCalled(); - expect(mockTransformManager.stop).not.toHaveBeenCalled(); - expect(mockTransformManager.uninstall).not.toHaveBeenCalled(); - expect(mockEsClient.deleteByQuery).not.toHaveBeenCalled(); + + // these calls are related to the updated slo + expect(mockSummaryTransformManager.stop).toMatchSnapshot(); + expect(mockSummaryTransformManager.uninstall).toMatchSnapshot(); + expect(mockTransformManager.stop).toMatchSnapshot(); + expect(mockTransformManager.uninstall).toMatchSnapshot(); + expect(mockEsClient.ingest.deletePipeline).toMatchSnapshot(); }); }); - describe('when error happens during the transform start step', () => { - it('removes the new transform and restores the previous SLO definition in the repository', async () => { - const slo = createSLO({ - indicator: createAPMTransactionErrorRateIndicator({ environment: 'development' }), - }); - mockRepository.findById.mockResolvedValueOnce(slo); - mockTransformManager.start.mockRejectedValueOnce(new Error('Transform start error')); - - const newIndicator = createAPMTransactionErrorRateIndicator({ environment: 'production' }); + function expectInstallationOfUpdatedSLOResources() { + expect(mockTransformManager.install).toHaveBeenCalled(); + expect(mockTransformManager.start).toHaveBeenCalled(); - await expect(updateSLO.execute(slo.id, { indicator: newIndicator })).rejects.toThrowError( - 'Transform start error' - ); + expect(mockEsClient.ingest.putPipeline).toHaveBeenCalled(); - expect(mockTransformManager.uninstall).toHaveBeenCalledWith( - getSLOTransformId(slo.id, slo.revision + 1) - ); - expect(mockRepository.save).toHaveBeenCalledWith(slo); - expect(mockTransformManager.stop).not.toHaveBeenCalled(); - expect(mockEsClient.deleteByQuery).not.toHaveBeenCalled(); - }); - }); + expect(mockSummaryTransformManager.install).toHaveBeenCalled(); + expect(mockSummaryTransformManager.start).toHaveBeenCalled(); - function expectInstallationOfNewSLOTransform() { - expect(mockTransformManager.install).toBeCalled(); - expect(mockTransformManager.preview).toBeCalled(); - expect(mockTransformManager.start).toBeCalled(); + expect(mockEsClient.index).toHaveBeenCalled(); } - function expectDeletionOfOriginalSLO(originalSlo: SLO) { + function expectDeletionOfOriginalSLOResources(originalSlo: SLO) { const transformId = getSLOTransformId(originalSlo.id, originalSlo.revision); - expect(mockTransformManager.stop).toBeCalledWith(transformId); - expect(mockTransformManager.uninstall).toBeCalledWith(transformId); + expect(mockTransformManager.stop).toHaveBeenCalledWith(transformId); + expect(mockTransformManager.uninstall).toHaveBeenCalledWith(transformId); + + const summaryTransformId = getSLOSummaryTransformId(originalSlo.id, originalSlo.revision); + expect(mockSummaryTransformManager.stop).toHaveBeenCalledWith(summaryTransformId); + expect(mockSummaryTransformManager.uninstall).toHaveBeenCalledWith(summaryTransformId); + + expect(mockEsClient.ingest.deletePipeline).toHaveBeenCalled(); expect(mockEsClient.deleteByQuery).toHaveBeenCalledTimes(2); expect(mockEsClient.deleteByQuery).toHaveBeenNthCalledWith( diff --git a/x-pack/plugins/observability/server/services/slo/update_slo.ts b/x-pack/plugins/observability/server/services/slo/update_slo.ts index 0d039c93c1ab0..1a73d54decbee 100644 --- a/x-pack/plugins/observability/server/services/slo/update_slo.ts +++ b/x-pack/plugins/observability/server/services/slo/update_slo.ts @@ -5,26 +5,33 @@ * 2.0. */ -import { ElasticsearchClient } from '@kbn/core/server'; +import { ElasticsearchClient, Logger } from '@kbn/core/server'; import { UpdateSLOParams, UpdateSLOResponse, updateSLOResponseSchema } from '@kbn/slo-schema'; -import { isEqual } from 'lodash'; +import { isEqual, pick } from 'lodash'; import { + getSLOSummaryPipelineId, + getSLOSummaryTransformId, getSLOTransformId, SLO_DESTINATION_INDEX_PATTERN, SLO_SUMMARY_DESTINATION_INDEX_PATTERN, SLO_SUMMARY_TEMP_INDEX_NAME, } from '../../../common/slo/constants'; +import { getSLOSummaryPipelineTemplate } from '../../assets/ingest_templates/slo_summary_pipeline_template'; import { SLO } from '../../domain/models'; import { validateSLO } from '../../domain/services'; +import { retryTransientEsErrors } from '../../utils/retry'; import { SLORepository } from './slo_repository'; -import { createTempSummaryDocument } from './summary_transform/helpers/create_temp_summary'; +import { createTempSummaryDocument } from './summary_transform_generator/helpers/create_temp_summary'; import { TransformManager } from './transform_manager'; export class UpdateSLO { constructor( private repository: SLORepository, private transformManager: TransformManager, - private esClient: ElasticsearchClient + private summaryTransformManager: TransformManager, + private esClient: ElasticsearchClient, + private logger: Logger, + private spaceId: string ) {} public async execute(sloId: string, params: UpdateSLOParams): Promise { @@ -37,42 +44,81 @@ export class UpdateSLO { return this.toResponse(originalSlo); } + const fields = [ + 'indicator', + 'groupBy', + 'timeWindow', + 'objective', + 'budgetingMethod', + 'settings', + ]; + const requireRevisionBump = !isEqual(pick(originalSlo, fields), pick(updatedSlo, fields)); + updatedSlo = Object.assign(updatedSlo, { updatedAt: new Date(), - revision: originalSlo.revision + 1, + revision: requireRevisionBump ? originalSlo.revision + 1 : originalSlo.revision, }); validateSLO(updatedSlo); - - const updatedSloTransformId = getSLOTransformId(updatedSlo.id, updatedSlo.revision); await this.repository.save(updatedSlo); - try { - await this.transformManager.install(updatedSlo); - } catch (err) { - await this.repository.save(originalSlo); - throw err; + if (!requireRevisionBump) { + // At this point, we still need to update the summary pipeline to include the changes (name, desc, tags, ...) in the summary index + await retryTransientEsErrors( + () => + this.esClient.ingest.putPipeline(getSLOSummaryPipelineTemplate(updatedSlo, this.spaceId)), + { logger: this.logger } + ); + + return this.toResponse(updatedSlo); } + const updatedRollupTransformId = getSLOTransformId(updatedSlo.id, updatedSlo.revision); + const updatedSummaryTransformId = getSLOSummaryTransformId(updatedSlo.id, updatedSlo.revision); + try { - await this.transformManager.preview(updatedSloTransformId); - await this.transformManager.start(updatedSloTransformId); + await this.transformManager.install(updatedSlo); + await this.transformManager.start(updatedRollupTransformId); + + await retryTransientEsErrors( + () => + this.esClient.ingest.putPipeline(getSLOSummaryPipelineTemplate(updatedSlo, this.spaceId)), + { logger: this.logger } + ); + + await this.summaryTransformManager.install(updatedSlo); + await this.summaryTransformManager.start(updatedSummaryTransformId); + + await retryTransientEsErrors( + () => + this.esClient.index({ + index: SLO_SUMMARY_TEMP_INDEX_NAME, + id: `slo-${updatedSlo.id}`, + document: createTempSummaryDocument(updatedSlo, this.spaceId), + refresh: true, + }), + { logger: this.logger } + ); } catch (err) { - await Promise.all([ - this.transformManager.uninstall(updatedSloTransformId), - this.repository.save(originalSlo), - ]); + this.logger.error( + `Cannot update the SLO [id: ${updatedSlo.id}, revision: ${updatedSlo.revision}]. Rolling back.` + ); + + // Restore the previous slo definition + await this.repository.save(originalSlo); + // delete the created resources for the updated slo + await this.summaryTransformManager.stop(updatedSummaryTransformId); + await this.summaryTransformManager.uninstall(updatedSummaryTransformId); + await this.transformManager.stop(updatedRollupTransformId); + await this.transformManager.uninstall(updatedRollupTransformId); + await this.esClient.ingest.deletePipeline( + { id: getSLOSummaryPipelineId(updatedSlo.id, updatedSlo.revision) }, + { ignore: [404] } + ); throw err; } - await this.esClient.index({ - index: SLO_SUMMARY_TEMP_INDEX_NAME, - id: `slo-${updatedSlo.id}`, - document: createTempSummaryDocument(updatedSlo), - refresh: true, - }); - await this.deleteOriginalSLO(originalSlo); return this.toResponse(updatedSlo); @@ -80,9 +126,21 @@ export class UpdateSLO { private async deleteOriginalSLO(originalSlo: SLO) { try { - const originalSloTransformId = getSLOTransformId(originalSlo.id, originalSlo.revision); - await this.transformManager.stop(originalSloTransformId); - await this.transformManager.uninstall(originalSloTransformId); + const originalRollupTransformId = getSLOTransformId(originalSlo.id, originalSlo.revision); + await this.transformManager.stop(originalRollupTransformId); + await this.transformManager.uninstall(originalRollupTransformId); + + const originalSummaryTransformId = getSLOSummaryTransformId( + originalSlo.id, + originalSlo.revision + ); + await this.summaryTransformManager.stop(originalSummaryTransformId); + await this.summaryTransformManager.uninstall(originalSummaryTransformId); + + await this.esClient.ingest.deletePipeline( + { id: getSLOSummaryPipelineId(originalSlo.id, originalSlo.revision) }, + { ignore: [404] } + ); } catch (err) { // Any errors here should not prevent moving forward. // Worst case we keep rolling up data for the previous revision number. diff --git a/x-pack/plugins/observability/server/ui_settings.ts b/x-pack/plugins/observability/server/ui_settings.ts index 98260fff5f4c7..8029b412ebb6a 100644 --- a/x-pack/plugins/observability/server/ui_settings.ts +++ b/x-pack/plugins/observability/server/ui_settings.ts @@ -37,6 +37,7 @@ import { profilingPervCPUWattArm64, profilingAWSCostDiscountRate, profilingCostPervCPUPerHour, + enableInfrastructureProfilingIntegration, } from '../common/ui_settings_keys'; const betaLabel = i18n.translate('xpack.observability.uiSettings.betaLabel', { @@ -236,6 +237,24 @@ export const uiSettings: Record = { }), schema: schema.boolean(), }, + [enableInfrastructureProfilingIntegration]: { + category: [observabilityFeatureId], + name: i18n.translate('xpack.observability.enableInfrastructureProfilingIntegration', { + defaultMessage: 'Universal Profiling integration in Infrastructure', + }), + value: true, + description: i18n.translate( + 'xpack.observability.enableInfrastructureProfilingIntegrationDescription', + { + defaultMessage: + '{betaLabel} Enable Universal Profiling integration in the Infrastructure app.', + values: { + betaLabel: `[${betaLabel}]`, + }, + } + ), + schema: schema.boolean(), + }, [enableAwsLambdaMetrics]: { category: [observabilityFeatureId], name: i18n.translate('xpack.observability.enableAwsLambdaMetrics', { @@ -415,9 +434,9 @@ export const uiSettings: Record = { }), value: 1.7, description: i18n.translate('xpack.observability.profilingDatacenterPUEUiSettingDescription', { - defaultMessage: `Data center power usage effectiveness (PUE) measures how efficiently a data center uses energy. Defaults to 1.7, the average on-premise data center PUE according to the {uptimeLink} survey + defaultMessage: `Data center power usage effectiveness (PUE) measures how efficiently a data center uses energy. Defaults to 1.7, the average on-premise data center PUE according to the {uptimeLink} survey

- You can also use the PUE that corresponds with your cloud provider: + You can also use the PUE that corresponds with your cloud provider:
  • AWS: 1.135
  • GCP: 1.1
  • @@ -444,7 +463,7 @@ export const uiSettings: Record = { }), value: 0.000379069, description: i18n.translate('xpack.observability.profilingCo2PerKWHUiSettingDescription', { - defaultMessage: `Carbon intensity measures how clean your data center electricity is. + defaultMessage: `Carbon intensity measures how clean your data center electricity is. Specifically, it measures the average amount of CO2 emitted per kilowatt-hour (kWh) of electricity consumed in a particular region. Use the cloud carbon footprint {datasheetLink} to update this value according to your region. Defaults to US East (N. Virginia).`, values: { diff --git a/x-pack/plugins/observability/tsconfig.json b/x-pack/plugins/observability/tsconfig.json index 15e2aade480c7..61762322f9eed 100644 --- a/x-pack/plugins/observability/tsconfig.json +++ b/x-pack/plugins/observability/tsconfig.json @@ -96,7 +96,11 @@ "@kbn/lens-embeddable-utils", "@kbn/serverless", "@kbn/dashboard-plugin", - "@kbn/calculate-auto" + "@kbn/calculate-auto", + "@kbn/presentation-util-plugin", + "@kbn/task-manager-plugin", + "@kbn/core-elasticsearch-client-server-mocks", + "@kbn/core-saved-objects-api-server-mocks" ], "exclude": [ "target/**/*" diff --git a/x-pack/plugins/observability_ai_assistant/common/conversation_complete.ts b/x-pack/plugins/observability_ai_assistant/common/conversation_complete.ts new file mode 100644 index 0000000000000..f7e513efe3d01 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/common/conversation_complete.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. + */ +/* eslint-disable max-classes-per-file*/ +import { i18n } from '@kbn/i18n'; +import { Message } from './types'; + +export enum StreamingChatResponseEventType { + ChatCompletionChunk = 'chatCompletionChunk', + ConversationCreate = 'conversationCreate', + ConversationUpdate = 'conversationUpdate', + MessageAdd = 'messageAdd', + ConversationCompletionError = 'conversationCompletionError', +} + +type StreamingChatResponseEventBase< + TEventType extends StreamingChatResponseEventType, + TData extends {} +> = { + type: TEventType; +} & TData; + +type ChatCompletionChunkEvent = StreamingChatResponseEventBase< + StreamingChatResponseEventType.ChatCompletionChunk, + { + id: string; + message: { + content?: string; + function_call?: { + name?: string; + arguments?: string; + }; + }; + } +>; + +export type ConversationCreateEvent = StreamingChatResponseEventBase< + StreamingChatResponseEventType.ConversationCreate, + { + conversation: { + id: string; + title: string; + last_updated: string; + }; + } +>; + +export type ConversationUpdateEvent = StreamingChatResponseEventBase< + StreamingChatResponseEventType.ConversationUpdate, + { + conversation: { + id: string; + title: string; + last_updated: string; + }; + } +>; + +export type MessageAddEvent = StreamingChatResponseEventBase< + StreamingChatResponseEventType.MessageAdd, + { message: Message; id: string } +>; + +export type ConversationCompletionErrorEvent = StreamingChatResponseEventBase< + StreamingChatResponseEventType.ConversationCompletionError, + { error: { message: string; stack?: string; code?: ChatCompletionErrorCode } } +>; + +export type StreamingChatResponseEvent = + | ChatCompletionChunkEvent + | ConversationCreateEvent + | ConversationUpdateEvent + | MessageAddEvent + | ConversationCompletionErrorEvent; + +export enum ChatCompletionErrorCode { + InternalError = 'internalError', + NotFound = 'notFound', +} + +export class ConversationCompletionError extends Error { + code: ChatCompletionErrorCode; + + constructor(code: ChatCompletionErrorCode, message: string) { + super(message); + this.code = code; + } +} + +export class ConversationNotFoundError extends ConversationCompletionError { + constructor() { + super( + ChatCompletionErrorCode.NotFound, + i18n.translate( + 'xpack.observabilityAiAssistant.conversationCompletionError.conversationNotFound', + { + defaultMessage: 'Conversation not found', + } + ) + ); + } +} + +export function isChatCompletionError(error: Error): error is ConversationCompletionError { + return error instanceof ConversationCompletionError; +} diff --git a/x-pack/plugins/observability_ai_assistant/common/functions/lens.tsx b/x-pack/plugins/observability_ai_assistant/common/functions/lens.tsx new file mode 100644 index 0000000000000..a3d8487a83b8a --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/common/functions/lens.tsx @@ -0,0 +1,122 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FromSchema } from 'json-schema-to-ts'; +import { FIELD_FORMAT_IDS } from '@kbn/field-formats-plugin/common'; + +export enum SeriesType { + Bar = 'bar', + Line = 'line', + Area = 'area', + BarStacked = 'bar_stacked', + AreaStacked = 'area_stacked', + BarHorizontal = 'bar_horizontal', + BarPercentageStacked = 'bar_percentage_stacked', + AreaPercentageStacked = 'area_percentage_stacked', + BarHorizontalPercentageStacked = 'bar_horizontal_percentage_stacked', +} + +export const lensFunctionDefinition = { + name: 'lens', + contexts: ['core'], + description: + "Use this function to create custom visualizations, using Lens, that can be saved to dashboards. This function does not return data to the assistant, it only shows it to the user. When using this function, make sure to use the recall function to get more information about how to use it, with how you want to use it. Make sure the query also contains information about the user's request. The visualisation is displayed to the user above your reply, DO NOT try to generate or display an image yourself.", + descriptionForUser: + 'Use this function to create custom visualizations, using Lens, that can be saved to dashboards.', + parameters: { + type: 'object', + additionalProperties: false, + properties: { + layers: { + type: 'array', + items: { + type: 'object', + additionalProperties: false, + properties: { + label: { + type: 'string', + }, + formula: { + type: 'string', + description: + 'The formula for calculating the value, e.g. sum(my_field_name). Query the knowledge base to get more information about the syntax and available formulas.', + }, + filter: { + type: 'string', + description: 'A KQL query that will be used as a filter for the series', + }, + format: { + type: 'object', + additionalProperties: false, + properties: { + id: { + type: 'string', + description: + 'How to format the value. When using duration, make sure the value is seconds OR is converted to seconds using math functions. Ask the user for clarification in which unit the value is stored, or derive it from the field name.', + enum: [ + FIELD_FORMAT_IDS.BYTES, + FIELD_FORMAT_IDS.CURRENCY, + FIELD_FORMAT_IDS.DURATION, + FIELD_FORMAT_IDS.NUMBER, + FIELD_FORMAT_IDS.PERCENT, + FIELD_FORMAT_IDS.STRING, + ], + }, + }, + required: ['id'], + }, + }, + required: ['label', 'formula', 'format'], + }, + }, + timeField: { + type: 'string', + default: '@timefield', + description: + 'time field to use for XY chart. Use @timefield if its available on the index.', + }, + breakdown: { + type: 'object', + additionalProperties: false, + properties: { + field: { + type: 'string', + }, + }, + required: ['field'], + }, + indexPattern: { + type: 'string', + }, + seriesType: { + type: 'string', + enum: [ + SeriesType.Area, + SeriesType.AreaPercentageStacked, + SeriesType.AreaStacked, + SeriesType.Bar, + SeriesType.BarHorizontal, + SeriesType.BarHorizontalPercentageStacked, + SeriesType.BarPercentageStacked, + SeriesType.BarStacked, + SeriesType.Line, + ], + }, + start: { + type: 'string', + description: 'The start of the time range, in Elasticsearch datemath', + }, + end: { + type: 'string', + description: 'The end of the time range, in Elasticsearch datemath', + }, + }, + required: ['layers', 'indexPattern', 'start', 'end', 'timeField'], + } as const, +}; + +export type LensFunctionArguments = FromSchema; diff --git a/x-pack/plugins/observability_ai_assistant/common/types.ts b/x-pack/plugins/observability_ai_assistant/common/types.ts index bf4f2210538ca..2584a1c7083fd 100644 --- a/x-pack/plugins/observability_ai_assistant/common/types.ts +++ b/x-pack/plugins/observability_ai_assistant/common/types.ts @@ -5,10 +5,20 @@ * 2.0. */ -import type { FromSchema } from 'json-schema-to-ts'; import type { JSONSchema } from 'json-schema-to-ts'; -import React from 'react'; -import { Observable } from 'rxjs'; +import type { + CreateChatCompletionResponse, + CreateChatCompletionResponseChoicesInner, +} from 'openai'; +import type { Observable } from 'rxjs'; + +export type CreateChatCompletionResponseChunk = Omit & { + choices: Array< + Omit & { + delta: { content?: string; function_call?: { name?: string; arguments?: string } }; + } + >; +}; export enum MessageRole { System = 'system', @@ -89,12 +99,12 @@ export interface ContextDefinition { description: string; } -type FunctionResponse = +export type FunctionResponse = | { content?: any; data?: any; } - | Observable; + | Observable; export enum FunctionVisibility { System = 'system', @@ -102,7 +112,9 @@ export enum FunctionVisibility { All = 'all', } -interface FunctionOptions { +export interface FunctionDefinition< + TParameters extends CompatibleJSONSchema = CompatibleJSONSchema +> { name: string; description: string; visibility?: FunctionVisibility; @@ -111,36 +123,7 @@ interface FunctionOptions = ( - options: { arguments: TArguments; messages: Message[]; connectorId: string }, - signal: AbortSignal -) => Promise; - -type RenderFunction = (options: { - arguments: TArguments; - response: TResponse; -}) => React.ReactNode; - -export interface FunctionDefinition { - options: FunctionOptions; - respond: ( - options: { arguments: any; messages: Message[]; connectorId: string }, - signal: AbortSignal - ) => Promise; - render?: RenderFunction; -} - export type RegisterContextDefinition = (options: ContextDefinition) => void; -export type RegisterFunctionDefinition = < - TParameters extends CompatibleJSONSchema, - TResponse extends FunctionResponse, - TArguments = FromSchema ->( - options: FunctionOptions, - respond: RespondFunction, - render?: RenderFunction -) => void; - export type ContextRegistry = Map; export type FunctionRegistry = Map; diff --git a/x-pack/plugins/observability_ai_assistant/common/utils/concatenate_openai_chunks.ts b/x-pack/plugins/observability_ai_assistant/common/utils/concatenate_openai_chunks.ts new file mode 100644 index 0000000000000..e14b4cce868f1 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/common/utils/concatenate_openai_chunks.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { cloneDeep } from 'lodash'; +import { type Observable, scan } from 'rxjs'; +import { type CreateChatCompletionResponseChunk, MessageRole } from '../types'; + +export const concatenateOpenAiChunks = + () => + ( + source: Observable + ): Observable<{ + message: { + content: string; + role: MessageRole; + function_call: { + name: string; + arguments: string; + trigger: MessageRole.Assistant | MessageRole.User; + }; + }; + }> => + source.pipe( + scan( + (acc, { choices }) => { + acc.message.content += choices[0].delta.content ?? ''; + acc.message.function_call.name += choices[0].delta.function_call?.name ?? ''; + acc.message.function_call.arguments += choices[0].delta.function_call?.arguments ?? ''; + return cloneDeep(acc); + }, + { + message: { + content: '', + function_call: { + name: '', + arguments: '', + trigger: MessageRole.Assistant as const, + }, + role: MessageRole.Assistant, + }, + } + ) + ); diff --git a/x-pack/plugins/observability_ai_assistant/common/utils/filter_function_definitions.ts b/x-pack/plugins/observability_ai_assistant/common/utils/filter_function_definitions.ts new file mode 100644 index 0000000000000..3de6c3bce2484 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/common/utils/filter_function_definitions.ts @@ -0,0 +1,29 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { FunctionDefinition } from '../types'; + +export function filterFunctionDefinitions({ + contexts, + filter, + definitions, +}: { + contexts?: string[]; + filter?: string; + definitions: FunctionDefinition[]; +}) { + return contexts || filter + ? definitions.filter((fn) => { + const matchesContext = + !contexts || fn.contexts.some((context) => contexts.includes(context)); + const matchesFilter = + !filter || fn.name.includes(filter) || fn.description.includes(filter); + + return matchesContext && matchesFilter; + }) + : definitions; +} diff --git a/x-pack/plugins/observability_ai_assistant/common/utils/process_openai_stream.ts b/x-pack/plugins/observability_ai_assistant/common/utils/process_openai_stream.ts new file mode 100644 index 0000000000000..e0d66bbf85132 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/common/utils/process_openai_stream.ts @@ -0,0 +1,45 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +/* eslint-disable max-classes-per-file*/ +import { filter, map, Observable, tap } from 'rxjs'; +import type { CreateChatCompletionResponseChunk } from '../types'; + +class TokenLimitReachedError extends Error { + constructor() { + super(`Token limit reached`); + } +} + +class ServerError extends Error {} + +export function processOpenAiStream() { + return (source: Observable): Observable => + source.pipe( + map((line) => line.substring(6)), + filter((line) => !!line && line !== '[DONE]'), + map( + (line) => + JSON.parse(line) as CreateChatCompletionResponseChunk | { error: { message: string } } + ), + tap((line) => { + if ('error' in line) { + throw new ServerError(line.error.message); + } + if ( + 'choices' in line && + line.choices.length && + line.choices[0].finish_reason === 'length' + ) { + throw new TokenLimitReachedError(); + } + }), + filter( + (line): line is CreateChatCompletionResponseChunk => + 'object' in line && line.object === 'chat.completion.chunk' + ) + ); +} diff --git a/x-pack/plugins/observability_ai_assistant/jest.config.js b/x-pack/plugins/observability_ai_assistant/jest.config.js index 1d6798f6c7623..ff54dbc08c2b0 100644 --- a/x-pack/plugins/observability_ai_assistant/jest.config.js +++ b/x-pack/plugins/observability_ai_assistant/jest.config.js @@ -11,5 +11,8 @@ module.exports = { roots: ['/x-pack/plugins/observability_ai_assistant'], setupFiles: ['/x-pack/plugins/observability_ai_assistant/.storybook/jest_setup.js'], collectCoverage: true, + collectCoverageFrom: [ + '/x-pack/plugins/observability_ai_assistant/{common,public,server}/**/*.{js,ts,tsx}', + ], coverageReporters: ['html'], }; diff --git a/x-pack/plugins/observability_ai_assistant/kibana.jsonc b/x-pack/plugins/observability_ai_assistant/kibana.jsonc index 291c7e658de18..cd2d4b788bc78 100644 --- a/x-pack/plugins/observability_ai_assistant/kibana.jsonc +++ b/x-pack/plugins/observability_ai_assistant/kibana.jsonc @@ -8,6 +8,7 @@ "browser": true, "configPath": ["xpack", "observabilityAIAssistant"], "requiredPlugins": [ + "alerting", "actions", "dataViews", "features", @@ -21,7 +22,7 @@ "triggersActionsUi", "dataViews" ], - "requiredBundles": ["fieldFormats", "kibanaReact", "kibanaUtils"], + "requiredBundles": [ "kibanaReact", "kibanaUtils"], "optionalPlugins": [], "extraPublicDirs": [] } diff --git a/x-pack/plugins/observability_ai_assistant/public/analytics/index.ts b/x-pack/plugins/observability_ai_assistant/public/analytics/index.ts new file mode 100644 index 0000000000000..c6b382f84db22 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/analytics/index.ts @@ -0,0 +1,49 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { AnalyticsServiceSetup, AnalyticsServiceStart } from '@kbn/core-analytics-browser'; +import type { Message } from '../../common'; +import { + eventType as chatFeedbackEventType, + chatFeedbackEventSchema, + ChatFeedback, +} from './schemas/chat_feedback'; +import { + eventType as insightFeedbackEventType, + insightFeedbackEventSchema, + InsightFeedback, +} from './schemas/insight_feedback'; +import { + eventType as userSentPromptEventType, + userSentPromptEventSchema, +} from './schemas/user_sent_prompt'; + +const schemas = [chatFeedbackEventSchema, insightFeedbackEventSchema, userSentPromptEventSchema]; + +export const TELEMETRY = { + [chatFeedbackEventType]: chatFeedbackEventType, + [insightFeedbackEventType]: insightFeedbackEventType, + [userSentPromptEventType]: userSentPromptEventType, +} as const; + +export type TelemetryEventTypeWithPayload = + | { type: typeof chatFeedbackEventType; payload: ChatFeedback } + | { type: typeof insightFeedbackEventType; payload: InsightFeedback } + | { type: typeof userSentPromptEventType; payload: Message }; + +export const registerTelemetryEventTypes = (analytics: AnalyticsServiceSetup) => { + schemas.forEach((schema) => { + analytics.registerEventType<{}>(schema); + }); +}; + +export function sendEvent( + analytics: AnalyticsServiceStart, + eventType: TelemetryEventTypeWithPayload +): void { + analytics.reportEvent(eventType.type, eventType.payload); +} diff --git a/x-pack/plugins/observability_ai_assistant/public/analytics/schema.ts b/x-pack/plugins/observability_ai_assistant/public/analytics/schema.ts deleted file mode 100644 index 400225b22baef..0000000000000 --- a/x-pack/plugins/observability_ai_assistant/public/analytics/schema.ts +++ /dev/null @@ -1,95 +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 { RootSchema } from '@kbn/analytics-client'; -import { Message } from '../../common'; -import type { Feedback } from '../components/feedback_buttons'; - -export const MESSAGE_FEEDBACK = 'observability_ai_assistant_chat_message_feedback' as const; -export const INSIGHT_FEEDBACK = 'observability_ai_assistant_chat_insight_feedback' as const; - -export interface MessageFeedback extends Message { - feedback: Feedback; -} - -export interface TelemetryEvent { - eventType: typeof MESSAGE_FEEDBACK | typeof INSIGHT_FEEDBACK; - schema: RootSchema; -} - -export const MESSAGE_FEEDBACK_SCHEMA: TelemetryEvent = { - eventType: MESSAGE_FEEDBACK, - schema: { - '@timestamp': { - type: 'text', - _meta: { - description: 'The timestamp of the message.', - }, - }, - feedback: { - type: 'text', - _meta: { - description: 'Whether the user has deemed this response useful or not', - }, - }, - message: { - properties: { - content: { - type: 'text', - _meta: { - description: 'The response generated by the LLM.', - optional: true, - }, - }, - name: { - type: 'text', - _meta: { - description: 'The name of the function that was executed.', - optional: true, - }, - }, - role: { - type: 'text', - _meta: { - description: 'The actor that generated the response.', - }, - }, - data: { - type: 'text', - _meta: { - description: '', - optional: true, - }, - }, - function_call: { - properties: { - name: { - type: 'text', - _meta: { - description: 'The name of the function that was executed.', - optional: false, - }, - }, - arguments: { - type: 'text', - _meta: { - description: 'The arguments that were used when executing the function.', - optional: true, - }, - }, - trigger: { - type: 'text', - _meta: { - description: 'The actor which triggered the execution of this function.', - }, - }, - }, - }, - }, - }, - }, -}; diff --git a/x-pack/plugins/observability_ai_assistant/public/analytics/schemas/chat_feedback.ts b/x-pack/plugins/observability_ai_assistant/public/analytics/schemas/chat_feedback.ts new file mode 100644 index 0000000000000..cd302ef68e755 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/analytics/schemas/chat_feedback.ts @@ -0,0 +1,122 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { EventTypeOpts } from '@kbn/analytics-client'; +import type { Message, Conversation } from '../../../common'; +import type { Feedback } from '../../components/feedback_buttons'; +import { messageSchema } from './common'; + +export interface ChatFeedback { + messageWithFeedback: { + message: Message; + feedback: Feedback; + }; + conversation: Conversation; +} + +export const eventType = 'observability_ai_assistant_chat_feedback'; + +export const chatFeedbackEventSchema: EventTypeOpts = { + eventType, + schema: { + messageWithFeedback: { + properties: { + message: { + properties: messageSchema, + }, + feedback: { + type: 'text', + _meta: { + description: 'Whether the user has deemed this response useful or not', + }, + }, + }, + }, + conversation: { + properties: { + '@timestamp': { + type: 'text', + _meta: { + description: 'The timestamp of the conversation.', + }, + }, + user: { + properties: { + id: { + type: 'text', + _meta: { + description: 'The id of the user.', + optional: true, + }, + }, + name: { + type: 'text', + _meta: { + description: 'The name of the user.', + }, + }, + }, + }, + conversation: { + properties: { + id: { + type: 'text', + _meta: { + description: 'The id of the conversation.', + }, + }, + title: { + type: 'text', + _meta: { + description: 'The title of the conversation.', + }, + }, + last_updated: { + type: 'text', + _meta: { + description: 'The timestamp of the last message in the conversation.', + }, + }, + }, + }, + messages: { + type: 'array', + items: { + properties: messageSchema, + }, + _meta: { + description: 'The messages in the conversation.', + }, + }, + labels: { + type: 'pass_through', + _meta: { + description: 'The labels of the conversation.', + }, + }, + numeric_labels: { + type: 'pass_through', + _meta: { + description: 'The numeric labels of the conversation.', + }, + }, + namespace: { + type: 'text', + _meta: { + description: 'The namespace of the conversation.', + }, + }, + public: { + type: 'boolean', + _meta: { + description: 'Whether the conversation is public or not.', + }, + }, + }, + }, + }, +}; diff --git a/x-pack/plugins/observability_ai_assistant/public/analytics/schemas/common.ts b/x-pack/plugins/observability_ai_assistant/public/analytics/schemas/common.ts new file mode 100644 index 0000000000000..3c4d4ee0c75e0 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/analytics/schemas/common.ts @@ -0,0 +1,77 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { RootSchema } from '@kbn/analytics-client'; +import type { Message } from '../../../common'; + +export const messageSchema: RootSchema = { + '@timestamp': { + type: 'text', + _meta: { + description: 'The timestamp of the message.', + }, + }, + message: { + properties: { + content: { + type: 'text', + _meta: { + description: 'The response generated by the LLM.', + optional: true, + }, + }, + name: { + type: 'text', + _meta: { + description: 'The name of the function that was executed.', + optional: true, + }, + }, + role: { + type: 'text', + _meta: { + description: 'The actor that generated the response.', + }, + }, + data: { + type: 'text', + _meta: { + description: '', + optional: true, + }, + }, + function_call: { + _meta: { + description: 'The function call that was executed.', + optional: true, + }, + properties: { + name: { + type: 'text', + _meta: { + description: 'The name of the function that was executed.', + optional: false, + }, + }, + arguments: { + type: 'text', + _meta: { + description: 'The arguments that were used when executing the function.', + optional: true, + }, + }, + trigger: { + type: 'text', + _meta: { + description: 'The actor which triggered the execution of this function.', + }, + }, + }, + }, + }, + }, +}; diff --git a/x-pack/plugins/observability_ai_assistant/public/analytics/schemas/insight_feedback.ts b/x-pack/plugins/observability_ai_assistant/public/analytics/schemas/insight_feedback.ts new file mode 100644 index 0000000000000..5142beaa216a2 --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/analytics/schemas/insight_feedback.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 { EventTypeOpts } from '@kbn/analytics-client'; +import type { Message } from '../../../common'; +import type { Feedback } from '../../components/feedback_buttons'; +import { messageSchema } from './common'; + +export interface InsightFeedback { + feedback: Feedback; + message: Message; +} + +export const eventType = 'observability_ai_assistant_insight_feedback'; + +export const insightFeedbackEventSchema: EventTypeOpts = { + eventType, + schema: { + feedback: { + type: 'text', + _meta: { + description: 'Whether the user has deemed this response useful or not', + }, + }, + message: { + properties: messageSchema, + }, + }, +}; diff --git a/x-pack/plugins/observability_ai_assistant/public/analytics/schemas/user_sent_prompt.ts b/x-pack/plugins/observability_ai_assistant/public/analytics/schemas/user_sent_prompt.ts new file mode 100644 index 0000000000000..4d24dd146573d --- /dev/null +++ b/x-pack/plugins/observability_ai_assistant/public/analytics/schemas/user_sent_prompt.ts @@ -0,0 +1,16 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { EventTypeOpts } from '@kbn/analytics-client'; +import type { Message } from '../../../common'; +import { messageSchema } from './common'; + +export const eventType = 'observability_ai_assistant_user_sent_prompt_in_chat'; +export const userSentPromptEventSchema: EventTypeOpts = { + eventType, + schema: messageSchema, +}; diff --git a/x-pack/plugins/observability_ai_assistant/public/application.tsx b/x-pack/plugins/observability_ai_assistant/public/application.tsx index 9ae03a3d72f6a..10e5403bba436 100644 --- a/x-pack/plugins/observability_ai_assistant/public/application.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/application.tsx @@ -20,6 +20,8 @@ import type { ObservabilityAIAssistantService, } from './types'; +// This is the Conversation application. + export function Application({ coreStart, history, @@ -36,12 +38,14 @@ export function Application({ const theme = useMemo(() => { return { theme$ }; }, [theme$]); + return ( ) : ( - + )} diff --git a/x-pack/plugins/observability_ai_assistant/public/components/buttons/ask_assistant_button.tsx b/x-pack/plugins/observability_ai_assistant/public/components/buttons/ask_assistant_button.tsx index f3ac2d2a735ad..e13ba34110434 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/buttons/ask_assistant_button.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/buttons/ask_assistant_button.tsx @@ -85,7 +85,7 @@ export function AskAssistantButton({ { + toggleActionsMenu(); + handleNavigateToSettings(); + }, + }, { name: (
    @@ -86,15 +95,6 @@ export function ChatActionsMenu({ ), panel: 1, }, - { - name: i18n.translate('xpack.observabilityAiAssistant.chatHeader.actions.settings', { - defaultMessage: 'AI Assistant Settings', - }), - onClick: () => { - toggleActionsMenu(); - handleNavigateToSettings(); - }, - }, { name: i18n.translate( 'xpack.observabilityAiAssistant.chatHeader.actions.knowledgeBase', diff --git a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_body.tsx b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_body.tsx index cc805ded3341c..64375b0bd9a98 100644 --- a/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_body.tsx +++ b/x-pack/plugins/observability_ai_assistant/public/components/chat/chat_body.tsx @@ -5,45 +5,51 @@ * 2.0. */ +import React, { useEffect, useRef, useState } from 'react'; +import { css, keyframes } from '@emotion/css'; import { EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, - EuiLoadingSpinner, EuiPanel, EuiSpacer, + useEuiTheme, + euiScrollBarStyles, } from '@elastic/eui'; -import { css } from '@emotion/css'; import type { AuthenticatedUser } from '@kbn/security-plugin/common'; import { euiThemeVars } from '@kbn/ui-theme'; -import React, { useEffect, useRef, useState } from 'react'; import { i18n } from '@kbn/i18n'; -import { Conversation, Message, MessageRole } from '../../../common/types'; import { ChatState } from '../../hooks/use_chat'; import { useConversation } from '../../hooks/use_conversation'; -import type { UseGenAIConnectorsResult } from '../../hooks/use_genai_connectors'; -import type { UseKnowledgeBaseResult } from '../../hooks/use_knowledge_base'; import { useLicense } from '../../hooks/use_license'; import { useObservabilityAIAssistantChatService } from '../../hooks/use_observability_ai_assistant_chat_service'; -import { StartedFrom } from '../../utils/get_timeline_items_from_conversation'; +import type { UseGenAIConnectorsResult } from '../../hooks/use_genai_connectors'; +import type { UseKnowledgeBaseResult } from '../../hooks/use_knowledge_base'; +import { type Conversation, type Message, MessageRole } from '../../../common/types'; import { ChatHeader } from './chat_header'; import { ChatPromptEditor } from './chat_prompt_editor'; import { ChatTimeline } from './chat_timeline'; -import { ExperimentalFeatureBanner } from './experimental_feature_banner'; +import { Feedback } from '../feedback_buttons'; import { IncorrectLicensePanel } from './incorrect_license_panel'; -import { InitialSetupPanel } from './initial_setup_panel'; -import { ChatActionClickType } from './types'; +import { WelcomeMessage } from './welcome_message'; import { EMPTY_CONVERSATION_TITLE } from '../../i18n'; -import { Feedback } from '../feedback_buttons'; -import { MESSAGE_FEEDBACK } from '../../analytics/schema'; +import { ChatActionClickType } from './types'; +import type { StartedFrom } from '../../utils/get_timeline_items_from_conversation'; +import { TELEMETRY, sendEvent } from '../../analytics'; + +const fullHeightClassName = css` + height: 100%; +`; -const timelineClassName = css` +const timelineClassName = (scrollBarStyles: string) => css` overflow-y: auto; + ${scrollBarStyles} `; -const loadingSpinnerContainerClassName = css` - align-self: center; +const promptEditorClassname = css` + overflow: hidden; + transition: height ${euiThemeVars.euiAnimSpeedFast} ${euiThemeVars.euiAnimSlightResistance}; `; const incorrectLicenseContainer = css` @@ -55,6 +61,31 @@ const chatBodyContainerClassNameWithError = css` align-self: center; `; +const promptEditorContainerClassName = css` + padding-top: 12px; + padding-bottom: 8px; +`; + +const fadeInAnimation = keyframes` + from { + opacity: 0; + transform: scale(0.9); + } + to { + opacity: 1; + transform: scale(1); + } +`; + +const animClassName = css` + height: 100%; + opacity: 0; + animation: ${fadeInAnimation} ${euiThemeVars.euiAnimSpeedNormal} + ${euiThemeVars.euiAnimSlightBounce} ${euiThemeVars.euiAnimSpeedNormal} forwards; +`; + +const PADDING_AND_BORDER = 32; + export function ChatBody({ initialTitle, initialMessages, @@ -74,10 +105,12 @@ export function ChatBody({ connectorsManagementHref: string; currentUser?: Pick; startedFrom?: StartedFrom; - onConversationUpdate: (conversation: Conversation) => void; + onConversationUpdate: (conversation: { conversation: Conversation['conversation'] }) => void; }) { const license = useLicense(); const hasCorrectLicense = license?.hasAtLeast('enterprise'); + const euiTheme = useEuiTheme(); + const scrollBarStyles = euiScrollBarStyles(euiTheme); const chatService = useObservabilityAIAssistantChatService(); @@ -113,9 +146,26 @@ export function ChatBody({ const isAtBottom = (parent: HTMLElement) => parent.scrollTop + parent.clientHeight >= parent.scrollHeight; + const [promptEditorHeight, setPromptEditorHeight] = useState(0); + const handleFeedback = (message: Message, feedback: Feedback) => { - const feedbackEvent = { ...message, feedback }; - chatService.analytics.reportEvent(MESSAGE_FEEDBACK, feedbackEvent); + if (conversation.value?.conversation && 'user' in conversation.value) { + sendEvent(chatService.analytics, { + type: TELEMETRY.observability_ai_assistant_chat_feedback, + payload: { + messageWithFeedback: { message, feedback }, + conversation: conversation.value, + }, + }); + } + }; + + const handleChangeHeight = (editorHeight: number) => { + if (editorHeight === 0) { + setPromptEditorHeight(0); + } else { + setPromptEditorHeight(editorHeight + PADDING_AND_BORDER); + } }; useEffect(() => { @@ -165,97 +215,112 @@ export function ChatBody({ ); - } else if ( - connectors.loading || - knowledgeBase.status.loading || - (!conversation.value && conversation.loading) - ) { - footer = ( - - - - ); - } else if (connectors.connectors?.length === 0 && !initialConversationId) { - footer = ( - - ); + } else if (!conversation.value && conversation.loading) { + footer = null; } else { footer = ( <> - -
    - - { - const indexOf = messages.indexOf(editedMessage); - next(messages.slice(0, indexOf).concat(newMessage)); - }} - onFeedback={handleFeedback} - onRegenerate={(message) => { - const indexOf = messages.indexOf(message); - next(messages.slice(0, indexOf)); - }} - onStopGenerating={() => { - stop(); - }} - onActionClick={(payload) => { - setStickToBottom(true); - switch (payload.type) { - case ChatActionClickType.executeEsqlQuery: - next( - messages.concat({ - '@timestamp': new Date().toISOString(), - message: { - role: MessageRole.Assistant, - content: '', - function_call: { - name: 'execute_query', - arguments: JSON.stringify({ - query: payload.query, - }), - trigger: MessageRole.User, - }, - }, - }) - ); - break; + +
    + + {connectors.connectors?.length === 0 || messages.length === 1 ? ( + + ) : ( + { + const indexOf = messages.indexOf(editedMessage); + next(messages.slice(0, indexOf).concat(newMessage)); + }} + onFeedback={handleFeedback} + onRegenerate={(message) => { + const indexOf = messages.indexOf(message); + next(messages.slice(0, indexOf)); + }} + onSendTelemetry={(eventWithPayload) => + sendEvent(chatService.analytics, eventWithPayload) } - }} - /> + onStopGenerating={() => { + stop(); + }} + onActionClick={(payload) => { + setStickToBottom(true); + switch (payload.type) { + case ChatActionClickType.executeEsqlQuery: + next( + messages.concat({ + '@timestamp': new Date().toISOString(), + message: { + role: MessageRole.Assistant, + content: '', + function_call: { + name: 'execute_query', + arguments: JSON.stringify({ + query: payload.query, + }), + trigger: MessageRole.User, + }, + }, + }) + ); + break; + } + }} + /> + )}
    - + + - - - +
    diff --git a/x-pack/plugins/security_solution/common/api/detection_engine/users/suggest_user_profiles_route.schema.yaml b/x-pack/plugins/security_solution/common/api/detection_engine/users/suggest_user_profiles_route.schema.yaml index babaedf1486ff..c9e390da9fa17 100644 --- a/x-pack/plugins/security_solution/common/api/detection_engine/users/suggest_user_profiles_route.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/detection_engine/users/suggest_user_profiles_route.schema.yaml @@ -3,7 +3,7 @@ info: title: Suggest user profiles API endpoint version: '2023-10-31' paths: - /api/detection_engine/signals/_find: + /internal/detection_engine/users/_find: summary: Suggests user profiles based on provided search term post: operationId: SuggestUserProfiles diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/create_asset_criticality.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/create_asset_criticality.schema.yaml index 198fe9a35339b..bb5e682155064 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/create_asset_criticality.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/create_asset_criticality.schema.yaml @@ -2,6 +2,13 @@ openapi: 3.0.0 info: version: 1.0.0 title: Asset Criticality Create Record Schema +servers: + - url: 'http://{kibana_host}:{port}' + variables: + kibana_host: + default: localhost + port: + default: '5601' paths: /internal/asset_criticality: post: @@ -11,13 +18,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/CreateAssetCriticalityRecord' + $ref: './common.schema.yaml#/components/schemas/CreateAssetCriticalityRecord' responses: '200': description: Successful response content: application/json: schema: - $ref: '#/components/schemas/SingleAssetCriticality' + $ref: './common.schema.yaml#/components/schemas/AssetCriticalityRecord' '400': description: Invalid request \ No newline at end of file diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/delete_asset_criticality.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/delete_asset_criticality.schema.yaml index e8334c48205f4..fbdb4feb19e52 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/delete_asset_criticality.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/delete_asset_criticality.schema.yaml @@ -2,13 +2,20 @@ openapi: 3.0.0 info: version: 1.0.0 title: Asset Criticality Delete Record Schema +servers: + - url: 'http://{kibana_host}:{port}' + variables: + kibana_host: + default: localhost + port: + default: '5601' paths: /internal/asset_criticality: delete: summary: Delete Criticality Record parameters: - - $ref: '#/components/parameters/id_value' - - $ref: '#/components/parameters/id_field' + - $ref: './common.schema.yaml#/components/parameters/id_value' + - $ref: './common.schema.yaml#/components/parameters/id_field' responses: '200': description: Successful response diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality.schema.yaml index 2aa88dcbc862c..1411f2a08734f 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality.schema.yaml @@ -2,20 +2,27 @@ openapi: 3.0.0 info: version: 1.0.0 title: Asset Criticality Get Record Schema +servers: + - url: 'http://{kibana_host}:{port}' + variables: + kibana_host: + default: localhost + port: + default: '5601' paths: /internal/asset_criticality: get: summary: Get Criticality Record parameters: - - $ref: '#/components/parameters/id_value' - - $ref: '#/components/parameters/id_field' + - $ref: './common.schema.yaml#/components/parameters/id_value' + - $ref: './common.schema.yaml#/components/parameters/id_field' responses: '200': description: Successful response content: application/json: schema: - $ref: '#/components/schemas/SingleAssetCriticality' + $ref: './common.schema.yaml#/components/schemas/AssetCriticalityRecord' '400': description: Invalid request '404': diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality_privileges.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality_privileges.schema.yaml new file mode 100644 index 0000000000000..b877b90efca94 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality_privileges.schema.yaml @@ -0,0 +1,29 @@ +openapi: 3.0.0 +info: + title: Get Asset Criticality Privileges Schema + version: 1.0.0 +servers: + - url: 'http://{kibana_host}:{port}' + variables: + kibana_host: + default: localhost + port: + default: '5601' +paths: + /internal/asset_criticality/privileges: + get: + summary: Get Asset Criticality Privileges + responses: + '200': + description: Successful response + content: + application/json: + schema: + $ref: '../common/common.schema.yaml#/components/schemas/EntityAnalyticsPrivileges' + example: + elasticsearch: + index: + ".asset-criticality.asset-criticality-*": + read: true + write: false + has_all_required: false \ No newline at end of file diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality_status.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality_status.schema.yaml index 976833fae0706..a5a6b50354688 100644 --- a/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality_status.schema.yaml +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/asset_criticality/get_asset_criticality_status.schema.yaml @@ -2,6 +2,13 @@ openapi: 3.0.0 info: version: 1.0.0 title: Asset Criticality Status Schema +servers: + - url: 'http://{kibana_host}:{port}' + variables: + kibana_host: + default: localhost + port: + default: '5601' paths: /internal/asset_criticality/status: get: diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.gen.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.gen.ts new file mode 100644 index 0000000000000..460e8f1fdae3d --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.gen.ts @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { z } from 'zod'; + +/* + * NOTICE: Do not edit this file manually. + * This file is automatically generated by the OpenAPI Generator, @kbn/openapi-generator. + */ + +export type EntityAnalyticsPrivileges = z.infer; +export const EntityAnalyticsPrivileges = z.object({ + has_all_required: z.boolean(), + privileges: z.object({ + elasticsearch: z.object({ + cluster: z + .object({ + manage_index_templates: z.boolean().optional(), + manage_transform: z.boolean().optional(), + }) + .optional(), + index: z + .object({}) + .catchall( + z.object({ + read: z.boolean().optional(), + write: z.boolean().optional(), + }) + ) + .optional(), + }), + }), +}); diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.schema.yaml b/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.schema.yaml new file mode 100644 index 0000000000000..ab8707059f022 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/common/common.schema.yaml @@ -0,0 +1,40 @@ +openapi: 3.0.0 +info: + title: Entity Analytics Common Schema + description: Common schema for Entity Analytics + version: 1.0.0 +paths: { } +components: + schemas: + EntityAnalyticsPrivileges: + type: object + properties: + has_all_required: + type: boolean + privileges: + type: object + properties: + elasticsearch: + type: object + properties: + cluster: + type: object + properties: + manage_index_templates: + type: boolean + manage_transform: + type: boolean + index: + type: object + additionalProperties: + type: object + properties: + read: + type: boolean + write: + type: boolean + required: + - elasticsearch + required: + - has_all_required + - privileges diff --git a/x-pack/plugins/security_solution/common/api/entity_analytics/common/index.ts b/x-pack/plugins/security_solution/common/api/entity_analytics/common/index.ts new file mode 100644 index 0000000000000..9e607aedc5f83 --- /dev/null +++ b/x-pack/plugins/security_solution/common/api/entity_analytics/common/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './common.gen'; diff --git a/x-pack/plugins/security_solution/common/constants.ts b/x-pack/plugins/security_solution/common/constants.ts index 86865ccb2cd7e..52a2bcfc859b1 100644 --- a/x-pack/plugins/security_solution/common/constants.ts +++ b/x-pack/plugins/security_solution/common/constants.ts @@ -266,6 +266,7 @@ export const RISK_ENGINE_DISABLE_URL = `${RISK_ENGINE_URL}/disable`; export const RISK_ENGINE_PRIVILEGES_URL = `${RISK_ENGINE_URL}/privileges`; export const ASSET_CRITICALITY_URL = `/internal/asset_criticality`; +export const ASSET_CRITICALITY_PRIVILEGES_URL = `/internal/asset_criticality/privileges`; export const ASSET_CRITICALITY_STATUS_URL = `${ASSET_CRITICALITY_URL}/status`; /** @@ -283,6 +284,8 @@ export const INTERNAL_TAGS_URL = `/internal/tags`; export const INTERNAL_DETECTION_ENGINE_URL = '/internal/detection_engine' as const; export const DETECTION_ENGINE_ALERTS_INDEX_URL = `${INTERNAL_DETECTION_ENGINE_URL}/signal/index` as const; +export const DETECTION_ENGINE_ALERT_SUGGEST_USERS_URL = + `${INTERNAL_DETECTION_ENGINE_URL}/users/_find` as const; /** * Telemetry detection endpoint for any previews requested of what data we are @@ -325,8 +328,6 @@ export const DETECTION_ENGINE_SIGNALS_FINALIZE_MIGRATION_URL = export const DETECTION_ENGINE_ALERT_TAGS_URL = `${DETECTION_ENGINE_SIGNALS_URL}/tags` as const; export const DETECTION_ENGINE_ALERT_ASSIGNEES_URL = `${DETECTION_ENGINE_SIGNALS_URL}/assignees` as const; -export const DETECTION_ENGINE_ALERT_SUGGEST_USERS_URL = - `${DETECTION_ENGINE_SIGNALS_URL}/_find` as const; export const ALERTS_AS_DATA_URL = '/internal/rac/alerts' as const; export const ALERTS_AS_DATA_FIND_URL = `${ALERTS_AS_DATA_URL}/find` as const; diff --git a/x-pack/plugins/security_solution/common/endpoint/utils/package_v2.ts b/x-pack/plugins/security_solution/common/endpoint/utils/package_v2.ts index 926949bf9cd7f..fffa55ccbe514 100644 --- a/x-pack/plugins/security_solution/common/endpoint/utils/package_v2.ts +++ b/x-pack/plugins/security_solution/common/endpoint/utils/package_v2.ts @@ -7,7 +7,12 @@ import semverLte from 'semver/functions/lte'; -const MIN_ENDPOINT_PACKAGE_V2_VERSION = '8.12.0'; +function parseSemver(semver: string) { + return semver.includes('-') ? semver.substring(0, semver.indexOf('-')) : semver; +} + +const MIN_ENDPOINT_PACKAGE_V2_VERSION = '8.13.0'; export function isEndpointPackageV2(version: string) { - return semverLte(MIN_ENDPOINT_PACKAGE_V2_VERSION, version); + const parsedVersion = parseSemver(version); + return semverLte(MIN_ENDPOINT_PACKAGE_V2_VERSION, parsedVersion); } diff --git a/x-pack/plugins/security_solution/common/entity_analytics/asset_criticality/constants.ts b/x-pack/plugins/security_solution/common/entity_analytics/asset_criticality/constants.ts new file mode 100644 index 0000000000000..64746a9ba96b5 --- /dev/null +++ b/x-pack/plugins/security_solution/common/entity_analytics/asset_criticality/constants.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const ASSET_CRITICALITY_INDEX_PATTERN = '.asset-criticality.asset-criticality-*'; + +type AssetCriticalityIndexPrivilege = 'read' | 'write'; +export const ASSET_CRITICALITY_REQUIRED_ES_INDEX_PRIVILEGES = { + [ASSET_CRITICALITY_INDEX_PATTERN]: ['read', 'write'] as AssetCriticalityIndexPrivilege[], +}; diff --git a/x-pack/plugins/security_solution/common/asset_criticality/index.ts b/x-pack/plugins/security_solution/common/entity_analytics/asset_criticality/index.ts similarity index 90% rename from x-pack/plugins/security_solution/common/asset_criticality/index.ts rename to x-pack/plugins/security_solution/common/entity_analytics/asset_criticality/index.ts index af45b0f6a8ab1..4e70ff88d1bae 100644 --- a/x-pack/plugins/security_solution/common/asset_criticality/index.ts +++ b/x-pack/plugins/security_solution/common/entity_analytics/asset_criticality/index.ts @@ -6,3 +6,4 @@ */ export * from './indices'; +export * from './constants'; diff --git a/x-pack/plugins/security_solution/common/asset_criticality/indices.ts b/x-pack/plugins/security_solution/common/entity_analytics/asset_criticality/indices.ts similarity index 100% rename from x-pack/plugins/security_solution/common/asset_criticality/indices.ts rename to x-pack/plugins/security_solution/common/entity_analytics/asset_criticality/indices.ts diff --git a/x-pack/plugins/security_solution/common/risk_engine/after_keys.test.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/after_keys.test.ts similarity index 100% rename from x-pack/plugins/security_solution/common/risk_engine/after_keys.test.ts rename to x-pack/plugins/security_solution/common/entity_analytics/risk_engine/after_keys.test.ts diff --git a/x-pack/plugins/security_solution/common/risk_engine/after_keys.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/after_keys.ts similarity index 100% rename from x-pack/plugins/security_solution/common/risk_engine/after_keys.ts rename to x-pack/plugins/security_solution/common/entity_analytics/risk_engine/after_keys.ts diff --git a/x-pack/plugins/security_solution/common/risk_engine/constants.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/constants.ts similarity index 62% rename from x-pack/plugins/security_solution/common/risk_engine/constants.ts rename to x-pack/plugins/security_solution/common/entity_analytics/risk_engine/constants.ts index 46a5a99a7e21a..d876e2ebae11b 100644 --- a/x-pack/plugins/security_solution/common/risk_engine/constants.ts +++ b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/constants.ts @@ -4,14 +4,17 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - export const MAX_SPACES_COUNT = 1; +type ClusterPrivilege = 'manage_index_templates' | 'manage_transform'; export const RISK_ENGINE_REQUIRED_ES_CLUSTER_PRIVILEGES = [ 'manage_index_templates', 'manage_transform', -]; +] as ClusterPrivilege[]; + +export const RISK_SCORE_INDEX_PATTERN = 'risk-score.risk-score-*'; +type RiskEngineIndexPrivilege = 'read' | 'write'; export const RISK_ENGINE_REQUIRED_ES_INDEX_PRIVILEGES = Object.freeze({ - 'risk-score.risk-score-*': ['read', 'write'], + [RISK_SCORE_INDEX_PATTERN]: ['read', 'write'] as RiskEngineIndexPrivilege[], }); diff --git a/x-pack/plugins/security_solution/common/risk_engine/identifier_types.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/identifier_types.ts similarity index 100% rename from x-pack/plugins/security_solution/common/risk_engine/identifier_types.ts rename to x-pack/plugins/security_solution/common/entity_analytics/risk_engine/identifier_types.ts diff --git a/x-pack/plugins/security_solution/common/risk_engine/index.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/index.ts similarity index 93% rename from x-pack/plugins/security_solution/common/risk_engine/index.ts rename to x-pack/plugins/security_solution/common/entity_analytics/risk_engine/index.ts index b5f4e8f433009..3da0d8be4f0be 100644 --- a/x-pack/plugins/security_solution/common/risk_engine/index.ts +++ b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/index.ts @@ -12,3 +12,4 @@ export * from './range'; export * from './types'; export * from './indices'; export * from './constants'; +export * from './privileges'; diff --git a/x-pack/plugins/security_solution/common/risk_engine/indices.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/indices.ts similarity index 100% rename from x-pack/plugins/security_solution/common/risk_engine/indices.ts rename to x-pack/plugins/security_solution/common/entity_analytics/risk_engine/indices.ts diff --git a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/privileges.test.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/privileges.test.ts new file mode 100644 index 0000000000000..5c25db68c87a1 --- /dev/null +++ b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/privileges.test.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 { EntityAnalyticsPrivileges } from '../../api/entity_analytics/common'; +import { getMissingRiskEnginePrivileges } from './privileges'; + +describe('getMissingRiskEnginePrivileges', () => { + it('returns the missing cluster privileges', () => { + const noClusterPrivileges: EntityAnalyticsPrivileges['privileges'] = { + elasticsearch: { + cluster: { + manage_index_templates: false, + manage_transform: false, + }, + index: { + 'risk-score.risk-score-*': { + read: true, + write: true, + }, + }, + }, + }; + + const missingPrivileges = getMissingRiskEnginePrivileges(noClusterPrivileges); + + expect(missingPrivileges).toEqual({ + clusterPrivileges: ['manage_index_templates', 'manage_transform'], + indexPrivileges: [], + }); + }); + + it('returns the missing index privileges', () => { + const noIndexPrivileges: EntityAnalyticsPrivileges['privileges'] = { + elasticsearch: { + cluster: { + manage_index_templates: true, + manage_transform: true, + }, + index: { + 'risk-score.risk-score-*': { + read: false, + write: false, + }, + }, + }, + }; + + const missingPrivileges = getMissingRiskEnginePrivileges(noIndexPrivileges); + + expect(missingPrivileges).toEqual({ + clusterPrivileges: [], + indexPrivileges: [['risk-score.risk-score-*', ['read', 'write']]], + }); + }); +}); diff --git a/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/privileges.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/privileges.ts new file mode 100644 index 0000000000000..add0a0e56efce --- /dev/null +++ b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/privileges.ts @@ -0,0 +1,58 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { EntityAnalyticsPrivileges } from '../../api/entity_analytics/common'; +import { + RISK_ENGINE_REQUIRED_ES_CLUSTER_PRIVILEGES, + RISK_ENGINE_REQUIRED_ES_INDEX_PRIVILEGES, +} from './constants'; + +export type MissingClusterPrivileges = string[]; +export type MissingIndexPrivileges = Array; + +export interface MissingPrivileges { + clusterPrivileges: MissingClusterPrivileges; + indexPrivileges: MissingIndexPrivileges; +} + +export const getMissingIndexPrivileges = ( + privileges: EntityAnalyticsPrivileges['privileges']['elasticsearch']['index'] +): MissingIndexPrivileges => { + const missingIndexPrivileges: MissingIndexPrivileges = []; + + if (!privileges) { + return missingIndexPrivileges; + } + + for (const [indexName, requiredPrivileges] of Object.entries( + RISK_ENGINE_REQUIRED_ES_INDEX_PRIVILEGES + )) { + const missingPrivileges = requiredPrivileges.filter( + (privilege) => !privileges[indexName][privilege] + ); + + if (missingPrivileges.length) { + missingIndexPrivileges.push([indexName, missingPrivileges]); + } + } + + return missingIndexPrivileges; +}; + +export const getMissingRiskEnginePrivileges = ( + privileges: EntityAnalyticsPrivileges['privileges'] +): MissingPrivileges => { + const missingIndexPrivileges = getMissingIndexPrivileges(privileges.elasticsearch.index); + const missingClusterPrivileges = RISK_ENGINE_REQUIRED_ES_CLUSTER_PRIVILEGES.filter( + (privilege) => !privileges.elasticsearch.cluster?.[privilege] + ); + + return { + indexPrivileges: missingIndexPrivileges, + clusterPrivileges: missingClusterPrivileges, + }; +}; diff --git a/x-pack/plugins/security_solution/common/risk_engine/range.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/range.ts similarity index 100% rename from x-pack/plugins/security_solution/common/risk_engine/range.ts rename to x-pack/plugins/security_solution/common/entity_analytics/risk_engine/range.ts diff --git a/x-pack/plugins/security_solution/common/risk_engine/risk_score_calculation/request_schema.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_score_calculation/request_schema.ts similarity index 100% rename from x-pack/plugins/security_solution/common/risk_engine/risk_score_calculation/request_schema.ts rename to x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_score_calculation/request_schema.ts diff --git a/x-pack/plugins/security_solution/common/risk_engine/risk_score_preview/request_schema.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_score_preview/request_schema.ts similarity index 100% rename from x-pack/plugins/security_solution/common/risk_engine/risk_score_preview/request_schema.ts rename to x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_score_preview/request_schema.ts diff --git a/x-pack/plugins/security_solution/common/risk_engine/risk_weights/index.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_weights/index.ts similarity index 100% rename from x-pack/plugins/security_solution/common/risk_engine/risk_weights/index.ts rename to x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_weights/index.ts diff --git a/x-pack/plugins/security_solution/common/risk_engine/risk_weights/schema.test.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_weights/schema.test.ts similarity index 100% rename from x-pack/plugins/security_solution/common/risk_engine/risk_weights/schema.test.ts rename to x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_weights/schema.test.ts diff --git a/x-pack/plugins/security_solution/common/risk_engine/risk_weights/schema.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_weights/schema.ts similarity index 100% rename from x-pack/plugins/security_solution/common/risk_engine/risk_weights/schema.ts rename to x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_weights/schema.ts diff --git a/x-pack/plugins/security_solution/common/risk_engine/risk_weights/types.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_weights/types.ts similarity index 100% rename from x-pack/plugins/security_solution/common/risk_engine/risk_weights/types.ts rename to x-pack/plugins/security_solution/common/entity_analytics/risk_engine/risk_weights/types.ts diff --git a/x-pack/plugins/security_solution/common/risk_engine/types.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/types.ts similarity index 100% rename from x-pack/plugins/security_solution/common/risk_engine/types.ts rename to x-pack/plugins/security_solution/common/entity_analytics/risk_engine/types.ts diff --git a/x-pack/plugins/security_solution/common/risk_engine/utils.ts b/x-pack/plugins/security_solution/common/entity_analytics/risk_engine/utils.ts similarity index 100% rename from x-pack/plugins/security_solution/common/risk_engine/utils.ts rename to x-pack/plugins/security_solution/common/entity_analytics/risk_engine/utils.ts diff --git a/x-pack/plugins/security_solution/common/experimental_features.ts b/x-pack/plugins/security_solution/common/experimental_features.ts index 81e8906b73039..a9f3affba56d7 100644 --- a/x-pack/plugins/security_solution/common/experimental_features.ts +++ b/x-pack/plugins/security_solution/common/experimental_features.ts @@ -82,7 +82,7 @@ export const allowedExperimentalValues = Object.freeze({ /** * Enables expandable flyout in create rule page, alert preview */ - expandableFlyoutInCreateRuleEnabled: false, + expandableFlyoutInCreateRuleEnabled: true, /* * Enables new Set of filters on the Alerts page. * @@ -147,10 +147,22 @@ export const allowedExperimentalValues = Object.freeze({ */ entityAnalyticsAssetCriticalityEnabled: false, + /* + * Enables experimental Experimental S1 integration data to be available in Analyzer + */ + sentinelOneDataInAnalyzerEnabled: false, + /** * Enables SentinelOne manual host manipulation actions */ sentinelOneManualHostActionsEnabled: false, + + /* + * Enables experimental "Updates" tab in the prebuilt rule upgrade flyout. + * This tab shows the JSON diff between the installed prebuilt rule + * version and the latest available version. + */ + jsonPrebuiltRulesDiffingEnabled: false, }); type ExperimentalConfigKeys = Array; diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/all/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/all/index.ts index efbf12b3e5e90..f33eb664628a1 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/all/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/all/index.ts @@ -8,7 +8,7 @@ import type { IEsSearchResponse } from '@kbn/data-plugin/common'; import type { Inspect, Maybe, SortField } from '../../../common'; -import type { RiskInputs } from '../../../../risk_engine'; +import type { RiskInputs } from '../../../../entity_analytics/risk_engine'; export interface HostsRiskScoreStrategyResponse extends IEsSearchResponse { inspect?: Maybe; diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/common/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/common/index.ts index b5e0c62526a61..783533b3e49ff 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/common/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/risk_score/common/index.ts @@ -11,7 +11,7 @@ import { RiskScoreEntity, getRiskScoreLatestIndex, getRiskScoreTimeSeriesIndex, -} from '../../../../risk_engine'; +} from '../../../../entity_analytics/risk_engine'; export { RiskQueries } from '../../../../api/search_strategy'; /** diff --git a/x-pack/plugins/security_solution/common/search_strategy/security_solution/users/managed_details/index.ts b/x-pack/plugins/security_solution/common/search_strategy/security_solution/users/managed_details/index.ts index 3ab346fc7c697..49fd729c3a0d5 100644 --- a/x-pack/plugins/security_solution/common/search_strategy/security_solution/users/managed_details/index.ts +++ b/x-pack/plugins/security_solution/common/search_strategy/security_solution/users/managed_details/index.ts @@ -25,6 +25,6 @@ export interface ManagedUserHit { fields?: ManagedUserFields; } -export type ManagedUserHits = Record; +export type ManagedUserHits = Partial>; export type ManagedUserFields = Record; diff --git a/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_timeline.test.ts b/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_timeline.test.ts index dade311fa6b49..3a47134531d37 100644 --- a/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_timeline.test.ts +++ b/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_timeline.test.ts @@ -7,7 +7,7 @@ import type { SecurityAppStore } from '../../../common/store/types'; import { TimelineId } from '../../../../common/types'; -import { addProvider } from '../../../timelines/store/timeline/actions'; +import { addProvider } from '../../../timelines/store/actions'; import { createAddToTimelineCellActionFactory } from './add_to_timeline'; import type { CellActionExecutionContext } from '@kbn/cell-actions'; import { GEO_FIELD_TYPE } from '../../../timelines/components/timeline/body/renderers/constants'; diff --git a/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_timeline.ts b/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_timeline.ts index 9ce248701a75d..1b40784dcec4d 100644 --- a/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_timeline.ts +++ b/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/add_to_timeline.ts @@ -15,7 +15,7 @@ import { } from '@kbn/cell-actions/src/actions/utils'; import { ACTION_INCOMPATIBLE_VALUE_WARNING } from '@kbn/cell-actions/src/actions/translations'; import type { KBN_FIELD_TYPES } from '@kbn/field-types'; -import { addProvider } from '../../../timelines/store/timeline/actions'; +import { addProvider } from '../../../timelines/store/actions'; import { TimelineId } from '../../../../common/types'; import type { SecurityAppStore } from '../../../common/store'; import { fieldHasCellActions } from '../../utils'; diff --git a/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/investigate_in_new_timeline.test.ts b/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/investigate_in_new_timeline.test.ts index d8c83e6a336d9..ee1d1ec579f12 100644 --- a/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/investigate_in_new_timeline.test.ts +++ b/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/investigate_in_new_timeline.test.ts @@ -7,12 +7,12 @@ import type { SecurityAppStore } from '../../../common/store/types'; import { TimelineId } from '../../../../common/types'; -import { addProvider, showTimeline } from '../../../timelines/store/timeline/actions'; +import { addProvider, showTimeline } from '../../../timelines/store/actions'; import { createInvestigateInNewTimelineCellActionFactory } from './investigate_in_new_timeline'; import type { CellActionExecutionContext } from '@kbn/cell-actions'; import { GEO_FIELD_TYPE } from '../../../timelines/components/timeline/body/renderers/constants'; import { createStartServicesMock } from '../../../common/lib/kibana/kibana_react.mock'; -import { timelineActions } from '../../../timelines/store/timeline'; +import { timelineActions } from '../../../timelines/store'; import { KBN_FIELD_TYPES } from '@kbn/field-types'; const services = createStartServicesMock(); diff --git a/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/investigate_in_new_timeline.ts b/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/investigate_in_new_timeline.ts index 0ba3cf3ffc8a8..2c9dd401d415a 100644 --- a/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/investigate_in_new_timeline.ts +++ b/x-pack/plugins/security_solution/public/actions/add_to_timeline/cell_action/investigate_in_new_timeline.ts @@ -14,8 +14,8 @@ import { filterOutNullableValues, } from '@kbn/cell-actions/src/actions/utils'; import { ACTION_INCOMPATIBLE_VALUE_WARNING } from '@kbn/cell-actions/src/actions/translations'; -import { timelineActions } from '../../../timelines/store/timeline'; -import { addProvider, showTimeline } from '../../../timelines/store/timeline/actions'; +import { timelineActions } from '../../../timelines/store'; +import { addProvider, showTimeline } from '../../../timelines/store/actions'; import { TimelineId } from '../../../../common/types'; import type { SecurityAppStore } from '../../../common/store'; import { fieldHasCellActions } from '../../utils'; @@ -29,7 +29,7 @@ import { createDataProviders, isValidDataProviderField } from '../data_provider' import { SecurityCellActionType } from '../../constants'; import type { StartServices } from '../../../types'; import type { SecurityCellAction } from '../../types'; -import { timelineDefaults } from '../../../timelines/store/timeline/defaults'; +import { timelineDefaults } from '../../../timelines/store/defaults'; export const createInvestigateInNewTimelineCellActionFactory = createCellActionFactory( ({ diff --git a/x-pack/plugins/security_solution/public/actions/add_to_timeline/discover/add_to_timeline.test.ts b/x-pack/plugins/security_solution/public/actions/add_to_timeline/discover/add_to_timeline.test.ts index f8c62857e3e24..4374bf70b19bc 100644 --- a/x-pack/plugins/security_solution/public/actions/add_to_timeline/discover/add_to_timeline.test.ts +++ b/x-pack/plugins/security_solution/public/actions/add_to_timeline/discover/add_to_timeline.test.ts @@ -7,7 +7,7 @@ import type { SecurityAppStore } from '../../../common/store/types'; import { TimelineId } from '../../../../common/types'; -import { addProvider } from '../../../timelines/store/timeline/actions'; +import { addProvider } from '../../../timelines/store/actions'; import { createAddToTimelineDiscoverCellActionFactory } from './add_to_timeline'; import type { CellActionExecutionContext } from '@kbn/cell-actions'; import { GEO_FIELD_TYPE } from '../../../timelines/components/timeline/body/renderers/constants'; diff --git a/x-pack/plugins/security_solution/public/actions/add_to_timeline/lens/add_to_timeline.test.ts b/x-pack/plugins/security_solution/public/actions/add_to_timeline/lens/add_to_timeline.test.ts index ebb1f40061a72..976d0f13efab0 100644 --- a/x-pack/plugins/security_solution/public/actions/add_to_timeline/lens/add_to_timeline.test.ts +++ b/x-pack/plugins/security_solution/public/actions/add_to_timeline/lens/add_to_timeline.test.ts @@ -14,7 +14,7 @@ import { KibanaServices } from '../../../common/lib/kibana'; import { APP_UI_ID } from '../../../../common/constants'; import type { DataProvider } from '../../../../common/types'; import { TimelineId, EXISTS_OPERATOR } from '../../../../common/types'; -import { addProvider } from '../../../timelines/store/timeline/actions'; +import { addProvider } from '../../../timelines/store/actions'; import type { ActionExecutionContext } from '@kbn/ui-actions-plugin/public'; jest.mock('../../../common/lib/kibana'); diff --git a/x-pack/plugins/security_solution/public/actions/add_to_timeline/lens/add_to_timeline.ts b/x-pack/plugins/security_solution/public/actions/add_to_timeline/lens/add_to_timeline.ts index 4032a2fe1fe8f..1a536c34d77a4 100644 --- a/x-pack/plugins/security_solution/public/actions/add_to_timeline/lens/add_to_timeline.ts +++ b/x-pack/plugins/security_solution/public/actions/add_to_timeline/lens/add_to_timeline.ts @@ -10,7 +10,7 @@ import { isErrorEmbeddable, isFilterableEmbeddable } from '@kbn/embeddable-plugi import { createAction } from '@kbn/ui-actions-plugin/public'; import { KibanaServices } from '../../../common/lib/kibana'; import type { SecurityAppStore } from '../../../common/store/types'; -import { addProvider } from '../../../timelines/store/timeline/actions'; +import { addProvider } from '../../../timelines/store/actions'; import type { DataProvider } from '../../../../common/types'; import { EXISTS_OPERATOR, TimelineId } from '../../../../common/types'; import { fieldHasCellActions, isInSecurityApp, isLensEmbeddable } from '../../utils'; diff --git a/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_in.ts b/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_in.ts index 905da379d01ff..1b65b608f43a2 100644 --- a/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_in.ts +++ b/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_in.ts @@ -15,7 +15,7 @@ import { import type { KBN_FIELD_TYPES } from '@kbn/field-types'; import { ACTION_INCOMPATIBLE_VALUE_WARNING } from '@kbn/cell-actions/src/actions/translations'; import type { SecurityAppStore } from '../../../common/store'; -import { timelineSelectors } from '../../../timelines/store/timeline'; +import { timelineSelectors } from '../../../timelines/store'; import { fieldHasCellActions } from '../../utils'; import { TimelineId } from '../../../../common/types'; import { isTimelineScope } from '../../../helpers'; diff --git a/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_out.ts b/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_out.ts index 38c2e52b7c7c7..56eb27433fee5 100644 --- a/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_out.ts +++ b/x-pack/plugins/security_solution/public/actions/filter/cell_action/filter_out.ts @@ -17,7 +17,7 @@ import type { KBN_FIELD_TYPES } from '@kbn/field-types'; import { fieldHasCellActions } from '../../utils'; import type { SecurityAppStore } from '../../../common/store'; import type { StartServices } from '../../../types'; -import { timelineSelectors } from '../../../timelines/store/timeline'; +import { timelineSelectors } from '../../../timelines/store'; import { TimelineId } from '../../../../common/types'; import { isTimelineScope } from '../../../helpers'; import type { SecurityCellAction } from '../../types'; diff --git a/x-pack/plugins/security_solution/public/actions/filter/lens/create_action.ts b/x-pack/plugins/security_solution/public/actions/filter/lens/create_action.ts index 78a0a9d46ff21..461356dc7224d 100644 --- a/x-pack/plugins/security_solution/public/actions/filter/lens/create_action.ts +++ b/x-pack/plugins/security_solution/public/actions/filter/lens/create_action.ts @@ -16,7 +16,7 @@ import type { CellValueContext } from '@kbn/embeddable-plugin/public'; import { createAction } from '@kbn/ui-actions-plugin/public'; import { ACTION_INCOMPATIBLE_VALUE_WARNING } from '@kbn/cell-actions/src/actions/translations'; import { i18n } from '@kbn/i18n'; -import { timelineSelectors } from '../../../timelines/store/timeline'; +import { timelineSelectors } from '../../../timelines/store'; import { fieldHasCellActions, isInSecurityApp, isLensEmbeddable } from '../../utils'; import { TimelineId } from '../../../../common/types'; import { DefaultCellActionTypes } from '../../constants'; diff --git a/x-pack/plugins/security_solution/public/actions/toggle_column/cell_action/toggle_column.ts b/x-pack/plugins/security_solution/public/actions/toggle_column/cell_action/toggle_column.ts index 6a1d73ffa1311..2a2926f3f6a27 100644 --- a/x-pack/plugins/security_solution/public/actions/toggle_column/cell_action/toggle_column.ts +++ b/x-pack/plugins/security_solution/public/actions/toggle_column/cell_action/toggle_column.ts @@ -15,8 +15,8 @@ import { import { fieldHasCellActions } from '../../utils'; import type { SecurityAppStore } from '../../../common/store'; import { getScopedActions, isInTableScope, isTimelineScope } from '../../../helpers'; -import { timelineDefaults } from '../../../timelines/store/timeline/defaults'; -import { timelineSelectors } from '../../../timelines/store/timeline'; +import { timelineDefaults } from '../../../timelines/store/defaults'; +import { timelineSelectors } from '../../../timelines/store'; import { DEFAULT_COLUMN_MIN_WIDTH } from '../../../timelines/components/timeline/body/constants'; import type { SecurityCellAction } from '../../types'; import { SecurityCellActionType } from '../../constants'; diff --git a/x-pack/plugins/security_solution/public/app/home/global_header/index.tsx b/x-pack/plugins/security_solution/public/app/home/global_header/index.tsx index e5a12721a6292..d02eb41605e3a 100644 --- a/x-pack/plugins/security_solution/public/app/home/global_header/index.tsx +++ b/x-pack/plugins/security_solution/public/app/home/global_header/index.tsx @@ -21,8 +21,8 @@ import { useKibana } from '../../../common/lib/kibana'; import { isDetectionsPath, isDashboardViewPath } from '../../../helpers'; import { Sourcerer } from '../../../common/components/sourcerer'; import { TimelineId } from '../../../../common/types/timeline'; -import { timelineDefaults } from '../../../timelines/store/timeline/defaults'; -import { timelineSelectors } from '../../../timelines/store/timeline'; +import { timelineDefaults } from '../../../timelines/store/defaults'; +import { timelineSelectors } from '../../../timelines/store'; import { useShallowEqualSelector } from '../../../common/hooks/use_selector'; import { getScopeFromPath, showSourcererByPath } from '../../../common/containers/sourcerer'; import { useAddIntegrationsUrl } from '../../../common/hooks/use_add_integrations_url'; diff --git a/x-pack/plugins/security_solution/public/app/home/index.test.tsx b/x-pack/plugins/security_solution/public/app/home/index.test.tsx index fc62a8236f9cc..8fce33967e151 100644 --- a/x-pack/plugins/security_solution/public/app/home/index.test.tsx +++ b/x-pack/plugins/security_solution/public/app/home/index.test.tsx @@ -29,8 +29,8 @@ import type { Filter } from '@kbn/es-query'; import { createStore } from '../../common/store'; import type { TimeRange, UrlInputsModel } from '../../common/store/inputs/model'; import { SecurityPageName } from '../types'; -import type { TimelineUrl } from '../../timelines/store/timeline/model'; -import { timelineDefaults } from '../../timelines/store/timeline/defaults'; +import type { TimelineUrl } from '../../timelines/store/model'; +import { timelineDefaults } from '../../timelines/store/defaults'; import { URL_PARAM_KEY } from '../../common/hooks/use_url_state'; import { InputsModelId } from '../../common/store/inputs/constants'; import { TopValuesPopoverService } from '../components/top_values_popover/top_values_popover_service'; @@ -98,7 +98,7 @@ jest.mock('../../timelines/components/open_timeline/helpers', () => { const mockGetTimeline = jest.fn(); -jest.mock('../../timelines/store/timeline', () => ({ +jest.mock('../../timelines/store', () => ({ timelineSelectors: { getTimelineByIdSelector: () => mockGetTimeline, }, diff --git a/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.test.tsx b/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.test.tsx index cd8208a10c368..bc57219633a84 100644 --- a/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.test.tsx +++ b/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.test.tsx @@ -10,6 +10,7 @@ import React from 'react'; import { TestProviders } from '../../../common/mock'; import { SecuritySolutionTemplateWrapper } from '.'; +import { SecurityPageName } from '../../types'; const mockUseShowTimeline = jest.fn((): [boolean] => [false]); jest.mock('../../../common/utils/timeline/use_show_timeline', () => ({ @@ -49,6 +50,13 @@ jest.mock('../../../common/components/navigation/use_security_solution_navigatio }; }); +const mockUseRouteSpy = jest.fn((): [{ pageName: string }] => [ + { pageName: SecurityPageName.alerts }, +]); +jest.mock('../../../common/utils/route/use_route_spy', () => ({ + useRouteSpy: () => mockUseRouteSpy(), +})); + const renderComponent = () => { return render( diff --git a/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx b/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx index 1f174580ee0fc..c6268129fc543 100644 --- a/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx +++ b/x-pack/plugins/security_solution/public/app/home/template_wrapper/index.tsx @@ -11,7 +11,8 @@ import { EuiThemeProvider, useEuiTheme, type EuiThemeComputed } from '@elastic/e import { IS_DRAGGING_CLASS_NAME } from '@kbn/securitysolution-t-grid'; import { KibanaPageTemplate } from '@kbn/shared-ux-page-kibana-template'; import type { KibanaPageTemplateProps } from '@kbn/shared-ux-page-kibana-template'; -import { SecuritySolutionFlyout, SecuritySolutionFlyoutContextProvider } from '../../../flyout'; +import { ExpandableFlyoutProvider } from '@kbn/expandable-flyout'; +import { SecuritySolutionFlyout } from '../../../flyout'; import { useSecuritySolutionNavigation } from '../../../common/components/navigation/use_security_solution_navigation'; import { TimelineId } from '../../../../common/types/timeline'; import { getTimelineShowStatusByIdSelector } from '../../../timelines/components/flyout/selectors'; @@ -19,6 +20,8 @@ import { useDeepEqualSelector } from '../../../common/hooks/use_selector'; import { GlobalKQLHeader } from './global_kql_header'; import { SecuritySolutionBottomBar } from './bottom_bar'; import { useShowTimeline } from '../../../common/utils/timeline/use_show_timeline'; +import { useRouteSpy } from '../../../common/utils/route/use_route_spy'; +import { SecurityPageName } from '../../types'; /** * Need to apply the styles via a className to effect the containing bottom bar @@ -50,6 +53,8 @@ export const SecuritySolutionTemplateWrapper: React.FC getTimelineShowStatus(state, TimelineId.active) ); + const [routeProps] = useRouteSpy(); + const isPreview = routeProps?.pageName === SecurityPageName.rulesCreate; // The bottomBar by default has a set 'dark' colorMode that doesn't match the global colorMode from the Advanced Settings // To keep the mode in sync, we pass in the globalColorMode to the bottom bar here @@ -62,7 +67,7 @@ export const SecuritySolutionTemplateWrapper: React.FC + - + ); }); diff --git a/x-pack/plugins/security_solution/public/assistant/comment_actions/index.tsx b/x-pack/plugins/security_solution/public/assistant/comment_actions/index.tsx index 6059ef63ae6f1..1804c837c4f1d 100644 --- a/x-pack/plugins/security_solution/public/assistant/comment_actions/index.tsx +++ b/x-pack/plugins/security_solution/public/assistant/comment_actions/index.tsx @@ -17,7 +17,7 @@ import type { Note } from '../../common/lib/note'; import { appActions } from '../../common/store/actions'; import { TimelineId } from '../../../common/types'; import { updateAndAssociateNode } from '../../timelines/components/notes/helpers'; -import { timelineActions } from '../../timelines/store/timeline'; +import { timelineActions } from '../../timelines/store'; import * as i18n from './translations'; import { useIsExperimentalFeatureEnabled } from '../../common/hooks/use_experimental_features'; diff --git a/x-pack/plugins/security_solution/public/assistant/content/anonymization/index.ts b/x-pack/plugins/security_solution/public/assistant/content/anonymization/index.ts index 06be36e0a2bc9..353cc3b3afce9 100644 --- a/x-pack/plugins/security_solution/public/assistant/content/anonymization/index.ts +++ b/x-pack/plugins/security_solution/public/assistant/content/anonymization/index.ts @@ -7,6 +7,7 @@ /** By default, these fields are allowed to be sent to the assistant */ export const DEFAULT_ALLOW = [ + '_id', '@timestamp', 'cloud.availability_zone', 'cloud.provider', @@ -28,6 +29,7 @@ export const DEFAULT_ALLOW = [ 'host.risk.calculated_level', 'host.risk.calculated_score_norm', 'kibana.alert.last_detected', + 'kibana.alert.risk_score', 'kibana.alert.rule.description', 'kibana.alert.rule.name', 'kibana.alert.rule.references', @@ -42,6 +44,7 @@ export const DEFAULT_ALLOW = [ 'kibana.alert.rule.threat.technique.subtechnique.name', 'kibana.alert.rule.threat.technique.subtechnique.reference', 'kibana.alert.severity', + 'kibana.alert.workflow_status', 'process.args', 'process.command_line', 'process.executable', @@ -73,6 +76,7 @@ export const DEFAULT_ALLOW = [ /** By default, these fields will be anonymized */ export const DEFAULT_ALLOW_REPLACEMENT = [ + '_id', // the document's _id is replaced with an anonymized value 'cloud.availability_zone', 'cloud.provider', 'cloud.region', diff --git a/x-pack/plugins/security_solution/public/assistant/send_to_timeline/index.tsx b/x-pack/plugins/security_solution/public/assistant/send_to_timeline/index.tsx index 114d5deb5f3ec..fe2c23b8f60b1 100644 --- a/x-pack/plugins/security_solution/public/assistant/send_to_timeline/index.tsx +++ b/x-pack/plugins/security_solution/public/assistant/send_to_timeline/index.tsx @@ -33,7 +33,7 @@ import { showTimeline, updateDataView, updateEqlOptions, -} from '../../timelines/store/timeline/actions'; +} from '../../timelines/store/actions'; import { useDiscoverInTimelineContext } from '../../common/components/discover_in_timeline/use_discover_in_timeline_context'; import { useShowTimeline } from '../../common/utils/timeline/use_show_timeline'; diff --git a/x-pack/plugins/security_solution/public/cases/components/use_insert_timeline/index.tsx b/x-pack/plugins/security_solution/public/cases/components/use_insert_timeline/index.tsx index e12d973d0be6e..cd5b4bcde7822 100644 --- a/x-pack/plugins/security_solution/public/cases/components/use_insert_timeline/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/components/use_insert_timeline/index.tsx @@ -11,9 +11,9 @@ import { isEmpty } from 'lodash/fp'; import { getTimelineUrl, useFormatUrl } from '../../../common/components/link_to'; import { useShallowEqualSelector } from '../../../common/hooks/use_selector'; -import { timelineSelectors, timelineActions } from '../../../timelines/store/timeline'; +import { timelineSelectors, timelineActions } from '../../../timelines/store'; import { SecurityPageName } from '../../../app/types'; -import { setInsertTimeline } from '../../../timelines/store/timeline/actions'; +import { setInsertTimeline } from '../../../timelines/store/actions'; export interface UseInsertTimelineReturn { handleOnTimelineChange: (title: string, id: string | null, graphEventId?: string) => void; diff --git a/x-pack/plugins/security_solution/public/cases/pages/index.tsx b/x-pack/plugins/security_solution/public/cases/pages/index.tsx index ab5b170e423c0..ba920046837ff 100644 --- a/x-pack/plugins/security_solution/public/cases/pages/index.tsx +++ b/x-pack/plugins/security_solution/public/cases/pages/index.tsx @@ -28,7 +28,7 @@ import { ENABLE_EXPANDABLE_FLYOUT_SETTING, SecurityPageName, } from '../../../common/constants'; -import { timelineActions } from '../../timelines/store/timeline'; +import { timelineActions } from '../../timelines/store'; import { useSourcererDataView } from '../../common/containers/sourcerer'; import { SourcererScopeName } from '../../common/store/sourcerer/model'; import { CaseDetailsRefreshContext } from '../../common/components/endpoint/host_isolation/endpoint_host_isolation_cases_context'; diff --git a/x-pack/plugins/security_solution/public/common/components/charts/common.test.tsx b/x-pack/plugins/security_solution/public/common/components/charts/common.test.tsx index 572706719eb88..71c3c0793d72b 100644 --- a/x-pack/plugins/security_solution/public/common/components/charts/common.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/charts/common.test.tsx @@ -19,7 +19,7 @@ import { WrappedByAutoSizer, useThemes, } from './common'; -import { LIGHT_THEME, DARK_THEME } from '@elastic/charts'; +import { LEGACY_LIGHT_THEME, LEGACY_DARK_THEME } from '@elastic/charts'; jest.mock('../../lib/kibana'); @@ -181,14 +181,14 @@ describe('checkIfAllValuesAreZero', () => { (useUiSetting as jest.Mock).mockImplementation(() => false); const { result } = renderHook(() => useThemes()); - expect(result.current.baseTheme).toBe(LIGHT_THEME); + expect(result.current.baseTheme).toBe(LEGACY_LIGHT_THEME); }); it('should return dark baseTheme when isDarkMode true', () => { (useUiSetting as jest.Mock).mockImplementation(() => true); const { result } = renderHook(() => useThemes()); - expect(result.current.baseTheme).toBe(DARK_THEME); + expect(result.current.baseTheme).toBe(LEGACY_DARK_THEME); }); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/charts/common.tsx b/x-pack/plugins/security_solution/public/common/components/charts/common.tsx index bf8f561cdfdf3..73fab4a3a8890 100644 --- a/x-pack/plugins/security_solution/public/common/components/charts/common.tsx +++ b/x-pack/plugins/security_solution/public/common/components/charts/common.tsx @@ -17,7 +17,7 @@ import type { BarSeriesStyle, Theme, } from '@elastic/charts'; -import { DARK_THEME, LIGHT_THEME, Position } from '@elastic/charts'; +import { LEGACY_DARK_THEME, LEGACY_LIGHT_THEME, Position } from '@elastic/charts'; import { EuiFlexGroup } from '@elastic/eui'; import React from 'react'; import styled from 'styled-components'; @@ -114,8 +114,8 @@ const theme: PartialTheme = { }; export const useThemes = (): { baseTheme: Theme; theme: PartialTheme } => { const isDarkMode = useUiSetting(DEFAULT_DARK_MODE); - // TODO use the EUI charts theme see src/plugins/charts/public/services/theme/README.md - const baseTheme = isDarkMode ? DARK_THEME : LIGHT_THEME; + // TODO connect to charts.theme service see src/plugins/charts/public/services/theme/README.md + const baseTheme = isDarkMode ? LEGACY_DARK_THEME : LEGACY_LIGHT_THEME; return { baseTheme, theme, diff --git a/x-pack/plugins/security_solution/public/common/components/charts/donutchart.test.tsx b/x-pack/plugins/security_solution/public/common/components/charts/donutchart.test.tsx index 18313b002b223..e29b72fbc0004 100644 --- a/x-pack/plugins/security_solution/public/common/components/charts/donutchart.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/charts/donutchart.test.tsx @@ -7,7 +7,7 @@ import React from 'react'; import type { Severity } from '@kbn/securitysolution-io-ts-alerting-types'; -import { LIGHT_THEME, Partition, Settings } from '@elastic/charts'; +import { LEGACY_LIGHT_THEME, Partition, Settings } from '@elastic/charts'; import { parsedMockAlertsData } from '../../../overview/components/detection_response/alerts_by_status/mock_data'; import { render } from '@testing-library/react'; import type { DonutChartProps } from './donutchart'; @@ -47,7 +47,7 @@ jest.mock('./draggable_legend', () => { }; }); -const mockBaseTheme = LIGHT_THEME; +const mockBaseTheme = LEGACY_LIGHT_THEME; jest.mock('./common', () => { return { useThemes: jest.fn(() => ({ @@ -93,7 +93,7 @@ describe('DonutChart', () => { expect(container.querySelector(`[data-test-subj="es-chart-settings"]`)).toBeInTheDocument(); const settingsProps = (Settings as jest.Mock).mock.calls[0][0]; - expect(settingsProps.baseTheme).toEqual(LIGHT_THEME); + expect(settingsProps.baseTheme).toEqual(LEGACY_LIGHT_THEME); expect(settingsProps.theme[0]).toEqual({ chartMargins: { bottom: 0, left: 0, right: 0, top: 0 }, partition: { diff --git a/x-pack/plugins/security_solution/public/common/components/control_columns/row_action/index.tsx b/x-pack/plugins/security_solution/public/common/components/control_columns/row_action/index.tsx index 93a29b9ea4436..e729ccce92578 100644 --- a/x-pack/plugins/security_solution/public/common/components/control_columns/row_action/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/control_columns/row_action/index.tsx @@ -11,7 +11,7 @@ import { useDispatch } from 'react-redux'; import { useExpandableFlyoutContext } from '@kbn/expandable-flyout'; import { dataTableActions, TableId } from '@kbn/securitysolution-data-table'; import { useUiSetting$ } from '@kbn/kibana-react-plugin/public'; -import { timelineActions } from '../../../../timelines/store/timeline'; +import { timelineActions } from '../../../../timelines/store'; import { ENABLE_EXPANDABLE_FLYOUT_SETTING } from '../../../../../common/constants'; import { DocumentDetailsRightPanelKey } from '../../../../flyout/document_details/right'; import type { diff --git a/x-pack/plugins/security_solution/public/common/components/discover_in_timeline/use_discover_in_timeline_actions.test.tsx b/x-pack/plugins/security_solution/public/common/components/discover_in_timeline/use_discover_in_timeline_actions.test.tsx index fddf6a1afc6e8..b2d1ac052a1e0 100644 --- a/x-pack/plugins/security_solution/public/common/components/discover_in_timeline/use_discover_in_timeline_actions.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/discover_in_timeline/use_discover_in_timeline_actions.test.tsx @@ -23,7 +23,7 @@ import { useKibana } from '../../lib/kibana'; import type { State } from '../../store'; import { createStore } from '../../store'; import { TimelineId } from '../../../../common/types'; -import * as timelineActions from '../../../timelines/store/timeline/actions'; +import * as timelineActions from '../../../timelines/store/actions'; import type { ComponentType, FC, PropsWithChildren } from 'react'; import React from 'react'; import type { DataView } from '@kbn/data-views-plugin/common'; diff --git a/x-pack/plugins/security_solution/public/common/components/discover_in_timeline/use_discover_in_timeline_actions.tsx b/x-pack/plugins/security_solution/public/common/components/discover_in_timeline/use_discover_in_timeline_actions.tsx index 3479612b8e7b2..3c8ab819527b6 100644 --- a/x-pack/plugins/security_solution/public/common/components/discover_in_timeline/use_discover_in_timeline_actions.tsx +++ b/x-pack/plugins/security_solution/public/common/components/discover_in_timeline/use_discover_in_timeline_actions.tsx @@ -14,9 +14,10 @@ import type { SavedSearch } from '@kbn/saved-search-plugin/common'; import type { DiscoverAppState } from '@kbn/discover-plugin/public/application/main/services/discover_app_state_container'; import type { TimeRange } from '@kbn/es-query'; import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { timelineDefaults } from '../../../timelines/store/timeline/defaults'; +import { defaultHeaders } from '@kbn/securitysolution-data-table'; +import { timelineDefaults } from '../../../timelines/store/defaults'; import { TimelineId } from '../../../../common/types'; -import { timelineActions, timelineSelectors } from '../../../timelines/store/timeline'; +import { timelineActions, timelineSelectors } from '../../../timelines/store'; import { useAppToasts } from '../../hooks/use_app_toasts'; import { useShallowEqualSelector } from '../../hooks/use_selector'; import { useKibana } from '../../lib/kibana'; @@ -80,10 +81,12 @@ export const useDiscoverInTimelineActions = ( const localDataViewId = dataViewId ?? 'security-solution-default'; const dataView = await dataViewService.get(localDataViewId); - + const defaultColumns = defaultHeaders.map((header) => header.id); return { query: { - esql: dataView ? `from ${dataView.getIndexPattern()} | limit 10` : '', + esql: dataView + ? `from ${dataView.getIndexPattern()} | limit 10 | keep ${defaultColumns.join(', ')}` + : '', }, sort: [['@timestamp', 'desc']], columns: [], diff --git a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx index a1455f7e5cc09..437309762732d 100644 --- a/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx +++ b/x-pack/plugins/security_solution/public/common/components/drag_and_drop/drag_drop_context_wrapper.tsx @@ -16,7 +16,7 @@ import { IS_DRAGGING_CLASS_NAME } from '@kbn/securitysolution-t-grid'; import type { BrowserFields } from '../../containers/source'; import { dragAndDropSelectors } from '../../store'; -import { timelineSelectors } from '../../../timelines/store/timeline'; +import { timelineSelectors } from '../../../timelines/store'; import type { IdToDataProvider } from '../../store/drag_and_drop/model'; import type { DataProvider } from '../../../timelines/components/timeline/data_providers/data_provider'; import { reArrangeProviders } from '../../../timelines/components/timeline/data_providers/helpers'; @@ -39,7 +39,7 @@ import { } from './helpers'; import { useDeepEqualSelector } from '../../hooks/use_selector'; import { useKibana } from '../../lib/kibana'; -import { timelineDefaults } from '../../../timelines/store/timeline/defaults'; +import { timelineDefaults } from '../../../timelines/store/defaults'; import { defaultAlertsHeaders } from '../events_viewer/default_alert_headers'; // @ts-expect-error diff --git a/x-pack/plugins/security_solution/public/common/components/endpoint/route_capture.tsx b/x-pack/plugins/security_solution/public/common/components/endpoint/route_capture.tsx index 1fd12db245fcc..e1d0751337fbf 100644 --- a/x-pack/plugins/security_solution/public/common/components/endpoint/route_capture.tsx +++ b/x-pack/plugins/security_solution/public/common/components/endpoint/route_capture.tsx @@ -10,7 +10,7 @@ import { useLocation } from 'react-router-dom'; import { useDispatch } from 'react-redux'; import { TimelineId } from '../../../../common/types'; import type { AppLocation } from '../../../../common/endpoint/types'; -import { timelineActions } from '../../../timelines/store/timeline'; +import { timelineActions } from '../../../timelines/store'; /** * This component should be used above all routes, but below the Provider. diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.tsx b/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.tsx index 8f5fdf54efdc9..7d3f5feb93f32 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.tsx +++ b/x-pack/plugins/security_solution/public/common/components/event_details/event_fields_browser.tsx @@ -22,12 +22,12 @@ import { import { dataTableSelectors, tableDefaults } from '@kbn/securitysolution-data-table'; import { isInTableScope, isTimelineScope } from '../../../helpers'; import { ADD_TIMELINE_BUTTON_CLASS_NAME } from '../../../timelines/components/flyout/add_timeline_button'; -import { timelineSelectors } from '../../../timelines/store/timeline'; +import { timelineSelectors } from '../../../timelines/store'; import type { BrowserFields } from '../../containers/source'; import { getAllFieldsByName } from '../../containers/source'; import type { TimelineEventsDetailsItem } from '../../../../common/search_strategy/timeline'; import { getColumnHeaders } from '../../../timelines/components/timeline/body/column_headers/helpers'; -import { timelineDefaults } from '../../../timelines/store/timeline/defaults'; +import { timelineDefaults } from '../../../timelines/store/defaults'; import { getColumns } from './columns'; import { EVENT_FIELDS_TABLE_CLASS_NAME, onEventDetailsTabKeyPressed, search } from './helpers'; import { useDeepEqualSelector } from '../../hooks/use_selector'; diff --git a/x-pack/plugins/security_solution/public/common/components/event_details/overview/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/common/components/event_details/overview/__snapshots__/index.test.tsx.snap index c676809c3c2a5..d22fb49ab6627 100644 --- a/x-pack/plugins/security_solution/public/common/components/event_details/overview/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/common/components/event_details/overview/__snapshots__/index.test.tsx.snap @@ -82,33 +82,29 @@ exports[`Event Details Overview Cards renders rows and spacers correctly 1`] = ` class="c2" >
    -
    - -
    + + +
= ({ ); }, [ecsData, eventType]); - const isDisabled = useMemo(() => !isInvestigateInResolverActionEnabled(ecsData), [ecsData]); + const isDisabled = !useIsInvestigateInResolverActionEnabled(ecsData); const { setGlobalFullScreen } = useGlobalFullScreen(); const { setTimelineFullScreen } = useTimelineFullScreen(); const scopedActions = getScopedActions(timelineId); diff --git a/x-pack/plugins/security_solution/public/common/components/header_actions/header_actions.test.tsx b/x-pack/plugins/security_solution/public/common/components/header_actions/header_actions.test.tsx index 6a8f06696bada..c8ab77c05fdbe 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_actions/header_actions.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/header_actions/header_actions.test.tsx @@ -12,7 +12,7 @@ import { mockTriggersActionsUi } from '../../mock/mock_triggers_actions_ui_plugi import type { ColumnHeaderOptions, HeaderActionProps } from '../../../../common/types'; import { TimelineTabs } from '../../../../common/types'; import { HeaderActions } from './header_actions'; -import { timelineActions } from '../../../timelines/store/timeline'; +import { timelineActions } from '../../../timelines/store'; import { getColumnHeader } from '../../../timelines/components/timeline/body/column_headers/helpers'; jest.mock('../../../timelines/components/row_renderers_browser', () => ({ diff --git a/x-pack/plugins/security_solution/public/common/components/header_actions/header_actions.tsx b/x-pack/plugins/security_solution/public/common/components/header_actions/header_actions.tsx index 231ed8f8b4e66..cbac23af41205 100644 --- a/x-pack/plugins/security_solution/public/common/components/header_actions/header_actions.tsx +++ b/x-pack/plugins/security_solution/public/common/components/header_actions/header_actions.tsx @@ -16,7 +16,7 @@ import { TimelineTabs, TimelineId } from '../../../../common/types'; import { isFullScreen } from '../../../timelines/components/timeline/body/column_headers'; import { isActiveTimeline } from '../../../helpers'; import { getColumnHeader } from '../../../timelines/components/timeline/body/column_headers/helpers'; -import { timelineActions, timelineSelectors } from '../../../timelines/store/timeline'; +import { timelineActions, timelineSelectors } from '../../../timelines/store'; import { useDeepEqualSelector } from '../../hooks/use_selector'; import { useGlobalFullScreen, useTimelineFullScreen } from '../../containers/use_full_screen'; import { useKibana } from '../../lib/kibana'; diff --git a/x-pack/plugins/security_solution/public/common/components/hover_actions/use_hover_action_items.tsx b/x-pack/plugins/security_solution/public/common/components/hover_actions/use_hover_action_items.tsx index 4acebe2d3a12c..95c69b4e5c4aa 100644 --- a/x-pack/plugins/security_solution/public/common/components/hover_actions/use_hover_action_items.tsx +++ b/x-pack/plugins/security_solution/public/common/components/hover_actions/use_hover_action_items.tsx @@ -14,13 +14,13 @@ import { isEmpty } from 'lodash'; import { FilterManager } from '@kbn/data-plugin/public'; import { useDispatch } from 'react-redux'; import { isActiveTimeline } from '../../../helpers'; -import { timelineSelectors } from '../../../timelines/store/timeline'; +import { timelineSelectors } from '../../../timelines/store'; import { useKibana } from '../../lib/kibana'; import { allowTopN } from '../drag_and_drop/helpers'; import type { ColumnHeaderOptions, DataProvider } from '../../../../common/types/timeline'; import { TimelineId } from '../../../../common/types/timeline'; import { ShowTopNButton } from './actions/show_top_n'; -import { addProvider } from '../../../timelines/store/timeline/actions'; +import { addProvider } from '../../../timelines/store/actions'; import { useDeepEqualSelector } from '../../hooks/use_selector'; export interface UseHoverActionItemsProps { dataProvider?: DataProvider | DataProvider[]; diff --git a/x-pack/plugins/security_solution/public/common/components/ml_popover/api.ts b/x-pack/plugins/security_solution/public/common/components/ml_popover/api.ts index b1c8c3975197e..a3d9d5f732cf0 100644 --- a/x-pack/plugins/security_solution/public/common/components/ml_popover/api.ts +++ b/x-pack/plugins/security_solution/public/common/components/ml_popover/api.ts @@ -33,7 +33,7 @@ export const checkRecognizer = async ({ signal, }: CheckRecognizerProps): Promise => KibanaServices.get().http.fetch( - `/internal/ml/modules/recognize/${indexPatternName}`, + `/internal/ml/modules/recognize/${indexPatternName.map((i) => encodeURIComponent(i))}`, { method: 'GET', version: '1', @@ -52,13 +52,16 @@ export const checkRecognizer = async ({ * @throws An error if response is not OK */ export const getModules = async ({ moduleId = '', signal }: GetModulesProps): Promise => - KibanaServices.get().http.fetch(`/internal/ml/modules/get_module/${moduleId}`, { - method: 'GET', - version: '1', - asSystemRequest: true, - signal, - query: { filter: 'security' }, - }); + KibanaServices.get().http.fetch( + `/internal/ml/modules/get_module/${encodeURIComponent(moduleId)}`, + { + method: 'GET', + version: '1', + asSystemRequest: true, + signal, + query: { filter: 'security' }, + } + ); /** * Creates ML Jobs + Datafeeds for the given configTemplate + indexPatternName @@ -79,7 +82,7 @@ export const setupMlJob = async ({ prefix = '', }: MlSetupArgs): Promise => { const response = await KibanaServices.get().http.fetch( - `/internal/ml/modules/setup/${configTemplate}`, + `/internal/ml/modules/setup/${encodeURIComponent(configTemplate)}`, { method: 'POST', version: '1', diff --git a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/use_breadcrumbs_nav.ts b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/use_breadcrumbs_nav.ts index 5a467464670a5..7825435fafcad 100644 --- a/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/use_breadcrumbs_nav.ts +++ b/x-pack/plugins/security_solution/public/common/components/navigation/breadcrumbs/use_breadcrumbs_nav.ts @@ -12,7 +12,7 @@ import type { ChromeBreadcrumb } from '@kbn/core/public'; import type { Dispatch } from 'redux'; import { SecurityPageName } from '../../../../app/types'; import type { RouteSpyState } from '../../../utils/route/types'; -import { timelineActions } from '../../../../timelines/store/timeline'; +import { timelineActions } from '../../../../timelines/store'; import { TimelineId } from '../../../../../common/types/timeline'; import type { GetSecuritySolutionUrl } from '../../link_to'; import { useGetSecuritySolutionUrl } from '../../link_to'; diff --git a/x-pack/plugins/security_solution/public/common/components/search_bar/index.tsx b/x-pack/plugins/security_solution/public/common/components/search_bar/index.tsx index c9906c6e70eac..af4c0aa7b0c19 100644 --- a/x-pack/plugins/security_solution/public/common/components/search_bar/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/search_bar/index.tsx @@ -35,7 +35,7 @@ import { startSelector, toStrSelector, } from './selectors'; -import { timelineActions } from '../../../timelines/store/timeline'; +import { timelineActions } from '../../../timelines/store'; import { useKibana } from '../../lib/kibana'; import { usersActions } from '../../../explore/users/store'; import { hostsActions } from '../../../explore/hosts/store'; diff --git a/x-pack/plugins/security_solution/public/common/components/sourcerer/temporary.tsx b/x-pack/plugins/security_solution/public/common/components/sourcerer/temporary.tsx index 1c2c73abcc042..63bdd45073200 100644 --- a/x-pack/plugins/security_solution/public/common/components/sourcerer/temporary.tsx +++ b/x-pack/plugins/security_solution/public/common/components/sourcerer/temporary.tsx @@ -22,9 +22,9 @@ import { Blockquote, ResetButton } from './helpers'; import { UpdateDefaultDataViewModal } from './update_default_data_view_modal'; import { TimelineId } from '../../../../common/types'; import { TimelineType } from '../../../../common/api/timeline'; -import { timelineSelectors } from '../../../timelines/store/timeline'; +import { timelineSelectors } from '../../../timelines/store'; import { useDeepEqualSelector } from '../../hooks/use_selector'; -import { timelineDefaults } from '../../../timelines/store/timeline/defaults'; +import { timelineDefaults } from '../../../timelines/store/defaults'; import { BadCurrentPatternsMessage, CurrentPatternsMessage, diff --git a/x-pack/plugins/security_solution/public/common/components/super_date_picker/index.tsx b/x-pack/plugins/security_solution/public/common/components/super_date_picker/index.tsx index 56f81529600d8..d37e0c0515ae9 100644 --- a/x-pack/plugins/security_solution/public/common/components/super_date_picker/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/super_date_picker/index.tsx @@ -9,6 +9,7 @@ import dateMath from '@kbn/datemath'; import type { EuiSuperDatePickerProps, EuiSuperDatePickerRecentRange, + EuiSuperUpdateButtonProps, OnRefreshChangeProps, OnRefreshProps, OnTimeChangeProps, @@ -23,7 +24,7 @@ import deepEqual from 'fast-deep-equal'; import { isQueryInput } from '../../store/inputs/helpers'; import { DEFAULT_TIMEPICKER_QUICK_RANGES } from '../../../../common/constants'; -import { timelineActions } from '../../../timelines/store/timeline'; +import { timelineActions } from '../../../timelines/store'; import { useUiSetting$ } from '../../lib/kibana'; import type { inputsModel, State } from '../../store'; import { inputsActions } from '../../store/actions'; @@ -42,6 +43,10 @@ import { } from './selectors'; import type { Inputs } from '../../store/inputs/model'; +const refreshButtonProps: EuiSuperUpdateButtonProps = { + fill: false, +}; + const MAX_RECENTLY_USED_RANGES = 9; interface Range { @@ -219,6 +224,7 @@ export const SuperDatePickerComponent = React.memo( isDisabled={disabled} width={width} compressed={compressed} + updateButtonProps={refreshButtonProps} /> ); }, diff --git a/x-pack/plugins/security_solution/public/common/components/threat_match/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/threat_match/index.test.tsx index 9415d8d0a9b3b..f2365cd064a02 100644 --- a/x-pack/plugins/security_solution/public/common/components/threat_match/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/threat_match/index.test.tsx @@ -80,8 +80,12 @@ describe('ThreatMatchComponent', () => { ); expect(wrapper.find('EuiFlexGroup[data-test-subj="itemEntryContainer"]')).toHaveLength(1); - expect(wrapper.find('[data-test-subj="entryField"]').text()).toEqual('Search'); - expect(wrapper.find('[data-test-subj="threatEntryField"]').text()).toEqual('Search'); + expect(wrapper.find('[data-test-subj="entryField"] input').props().placeholder).toEqual( + 'Search' + ); + expect(wrapper.find('[data-test-subj="threatEntryField"] input').props().placeholder).toEqual( + 'Search' + ); }); test('it displays "Search" for "listItems" that are passed in', async () => { @@ -108,7 +112,9 @@ describe('ThreatMatchComponent', () => { ); expect(wrapper.find('EuiFlexGroup[data-test-subj="itemEntryContainer"]')).toHaveLength(1); - expect(wrapper.find('[data-test-subj="entryField"]').at(0).text()).toEqual('Search'); + expect(wrapper.find('[data-test-subj="entryField"] input').at(0).props().placeholder).toEqual( + 'Search' + ); wrapper.unmount(); }); @@ -171,10 +177,18 @@ describe('ThreatMatchComponent', () => { await waitFor(() => { expect(wrapper.find('EuiFlexGroup[data-test-subj="itemEntryContainer"]')).toHaveLength(2); - expect(wrapper.find('[data-test-subj="entryField"]').at(0).text()).toEqual('Search'); - expect(wrapper.find('[data-test-subj="threatEntryField"]').at(0).text()).toEqual('Search'); - expect(wrapper.find('[data-test-subj="entryField"]').at(1).text()).toEqual('Search'); - expect(wrapper.find('[data-test-subj="threatEntryField"]').at(1).text()).toEqual('Search'); + expect(wrapper.find('[data-test-subj="entryField"] input').at(0).props().placeholder).toEqual( + 'Search' + ); + expect( + wrapper.find('[data-test-subj="threatEntryField"] input').at(0).props().placeholder + ).toEqual('Search'); + expect(wrapper.find('[data-test-subj="entryField"] input').at(1).props().placeholder).toEqual( + 'Search' + ); + expect( + wrapper.find('[data-test-subj="threatEntryField"] input').at(1).props().placeholder + ).toEqual('Search'); }); }); @@ -208,10 +222,18 @@ describe('ThreatMatchComponent', () => { await waitFor(() => { expect(wrapper.find('EuiFlexGroup[data-test-subj="entriesContainer"]')).toHaveLength(2); - expect(wrapper.find('[data-test-subj="entryField"]').at(0).text()).toEqual('Search'); - expect(wrapper.find('[data-test-subj="threatEntryField"]').at(0).text()).toEqual('Search'); - expect(wrapper.find('[data-test-subj="entryField"]').at(1).text()).toEqual('Search'); - expect(wrapper.find('[data-test-subj="threatEntryField"]').at(1).text()).toEqual('Search'); + expect(wrapper.find('[data-test-subj="entryField"] input').at(0).props().placeholder).toEqual( + 'Search' + ); + expect( + wrapper.find('[data-test-subj="threatEntryField"] input').at(0).props().placeholder + ).toEqual('Search'); + expect(wrapper.find('[data-test-subj="entryField"] input').at(1).props().placeholder).toEqual( + 'Search' + ); + expect( + wrapper.find('[data-test-subj="threatEntryField"] input').at(1).props().placeholder + ).toEqual('Search'); }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx index eed95e5caa9cb..9a329d369e7e4 100644 --- a/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/top_n/index.test.tsx @@ -42,7 +42,7 @@ jest.mock('react-router-dom', () => { jest.mock('../link_to'); jest.mock('../../lib/kibana'); -jest.mock('../../../timelines/store/timeline/actions'); +jest.mock('../../../timelines/store/actions'); jest.mock('../visualization_actions/actions'); jest.mock('../visualization_actions/lens_embeddable'); const field = 'process.name'; diff --git a/x-pack/plugins/security_solution/public/common/components/top_n/index.tsx b/x-pack/plugins/security_solution/public/common/components/top_n/index.tsx index 45cb33aa199b5..d845f2604c69b 100644 --- a/x-pack/plugins/security_solution/public/common/components/top_n/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/top_n/index.tsx @@ -19,9 +19,9 @@ import { useKibana } from '../../lib/kibana'; import { combineQueries } from '../../lib/kuery'; import type { inputsModel, State } from '../../store'; import { inputsSelectors } from '../../store'; -import { timelineDefaults } from '../../../timelines/store/timeline/defaults'; -import { timelineSelectors } from '../../../timelines/store/timeline'; -import type { TimelineModel } from '../../../timelines/store/timeline/model'; +import { timelineDefaults } from '../../../timelines/store/defaults'; +import { timelineSelectors } from '../../../timelines/store'; +import type { TimelineModel } from '../../../timelines/store/model'; import { getOptions, isDetectionsAlertsTable } from './helpers'; import { TopN } from './top_n'; diff --git a/x-pack/plugins/security_solution/public/common/components/use_combo_box_reset/index.test.tsx b/x-pack/plugins/security_solution/public/common/components/use_combo_box_reset/index.test.tsx index e9082e2ca72a9..24f813cf81a5c 100644 --- a/x-pack/plugins/security_solution/public/common/components/use_combo_box_reset/index.test.tsx +++ b/x-pack/plugins/security_solution/public/common/components/use_combo_box_reset/index.test.tsx @@ -65,21 +65,21 @@ describe('useEuiComboBoxReset', () => { render(); - const initialValue = screen.getByTestId('comboBoxInput'); // EuiComboBox does NOT render the current selection via it's input; it uses this div - expect(initialValue).toHaveTextContent(options[0].label); + const initialValue = screen.getByRole('combobox'); + expect(initialValue).toHaveValue(options[0].label); // update the EuiComboBox input to an invalid value: - const searchInput = screen.getByTestId('comboBoxSearchInput'); // the actual controlled by EuiComboBox + const searchInput = screen.getByRole('combobox'); // the actual controlled by EuiComboBox fireEvent.change(searchInput, { target: { value: invalidValue } }); - const afterInvalidInput = screen.getByTestId('comboBoxInput'); + const afterInvalidInput = screen.getByRole('combobox'); expect(searchInput).toHaveValue(invalidValue); // the EuiComboBox is now in the "error state" expect(afterInvalidInput).not.toHaveTextContent(invalidValue); // Value should not have been applied const resetButton = screen.getByRole('button', { name: 'Reset' }); fireEvent.click(resetButton); // clicking invokes onReset() - const afterReset = screen.getByTestId('comboBoxInput'); - expect(afterReset).toHaveTextContent(options[0].label); // back to the default + const afterReset = screen.getByRole('combobox'); + expect(afterReset).toHaveValue(options[0].label); // back to the default }); }); diff --git a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/risk_score_summary.test.ts b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/risk_score_summary.test.ts index 4e3aaa69129fc..e8af897c51cea 100644 --- a/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/risk_score_summary.test.ts +++ b/x-pack/plugins/security_solution/public/common/components/visualization_actions/lens_attributes/common/risk_scores/risk_score_summary.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { RiskScoreEntity } from '../../../../../../../common/risk_engine'; +import { RiskScoreEntity } from '../../../../../../../common/entity_analytics/risk_engine'; import { renderHook } from '@testing-library/react-hooks'; import { wrapper } from '../../../mocks'; import { useLensAttributes } from '../../../use_lens_attributes'; diff --git a/x-pack/plugins/security_solution/public/common/components/with_hover_actions/index.tsx b/x-pack/plugins/security_solution/public/common/components/with_hover_actions/index.tsx index 85b1def4c4101..509d7610cb0fb 100644 --- a/x-pack/plugins/security_solution/public/common/components/with_hover_actions/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/with_hover_actions/index.tsx @@ -11,20 +11,12 @@ import { IS_DRAGGING_CLASS_NAME, } from '@kbn/securitysolution-t-grid'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import styled from 'styled-components'; /** * To avoid expensive changes to the DOM, delay showing the popover menu */ const HOVER_INTENT_DELAY = 100; // ms -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const WithHoverActionsPopover = styled(EuiPopover as any)` - .euiPopover__anchor { - width: 100%; - } -` as unknown as typeof EuiPopover; - interface Props { /** * Always show the hover menu contents (default: false) @@ -157,7 +149,7 @@ export const WithHoverActions = React.memo( className={alwaysShow ? HOVER_ACTIONS_ALWAYS_SHOW_CLASS_NAME : ''} onMouseLeave={onMouseLeave} > - ( repositionOnScroll={true} > {isOpen ?
{hoverContent}
: null} -
+
); } diff --git a/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.tsx b/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.tsx index de784c876fd66..2e9c6cf796e57 100644 --- a/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.tsx +++ b/x-pack/plugins/security_solution/public/common/containers/sourcerer/index.tsx @@ -18,7 +18,7 @@ import type { } from '../../store/sourcerer/model'; import { SourcererScopeName } from '../../store/sourcerer/model'; import { useUserInfo } from '../../../detections/components/user_info'; -import { timelineSelectors } from '../../../timelines/store/timeline'; +import { timelineSelectors } from '../../../timelines/store'; import { ALERTS_PATH, HOSTS_PATH, diff --git a/x-pack/plugins/security_solution/public/common/hooks/timeline/use_init_timeline_url_param.ts b/x-pack/plugins/security_solution/public/common/hooks/timeline/use_init_timeline_url_param.ts index 4d6ef73c643de..1a3058cb138e5 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/timeline/use_init_timeline_url_param.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/timeline/use_init_timeline_url_param.ts @@ -15,8 +15,8 @@ import { dispatchUpdateTimeline, queryTimelineById, } from '../../../timelines/components/open_timeline/helpers'; -import type { TimelineUrl } from '../../../timelines/store/timeline/model'; -import { timelineActions } from '../../../timelines/store/timeline'; +import type { TimelineUrl } from '../../../timelines/store/model'; +import { timelineActions } from '../../../timelines/store'; import { URL_PARAM_KEY } from '../use_url_state'; export const useInitTimelineFromUrlParam = () => { diff --git a/x-pack/plugins/security_solution/public/common/hooks/timeline/use_query_timeline_by_id_on_url_change.test.ts b/x-pack/plugins/security_solution/public/common/hooks/timeline/use_query_timeline_by_id_on_url_change.test.ts index 3966ebc6697b2..ef5fe57f16e98 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/timeline/use_query_timeline_by_id_on_url_change.test.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/timeline/use_query_timeline_by_id_on_url_change.test.ts @@ -8,7 +8,7 @@ import { queryTimelineById } from '../../../timelines/components/open_timeline/helpers'; import { useQueryTimelineByIdOnUrlChange } from './use_query_timeline_by_id_on_url_change'; import { renderHook } from '@testing-library/react-hooks'; -import { timelineDefaults } from '../../../timelines/store/timeline/defaults'; +import { timelineDefaults } from '../../../timelines/store/defaults'; jest.mock('../../../timelines/components/open_timeline/helpers'); diff --git a/x-pack/plugins/security_solution/public/common/hooks/timeline/use_query_timeline_by_id_on_url_change.ts b/x-pack/plugins/security_solution/public/common/hooks/timeline/use_query_timeline_by_id_on_url_change.ts index e56131cd2603c..a7ecab2d5bb50 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/timeline/use_query_timeline_by_id_on_url_change.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/timeline/use_query_timeline_by_id_on_url_change.ts @@ -11,8 +11,8 @@ import { useLocation } from 'react-router-dom'; import usePrevious from 'react-use/lib/usePrevious'; import { useDispatch } from 'react-redux'; import { safeDecode } from '@kbn/rison'; -import type { TimelineUrl } from '../../../timelines/store/timeline/model'; -import { timelineActions, timelineSelectors } from '../../../timelines/store/timeline'; +import type { TimelineUrl } from '../../../timelines/store/model'; +import { timelineActions, timelineSelectors } from '../../../timelines/store'; import { TimelineId, TimelineTabs } from '../../../../common/types'; import { useShallowEqualSelector } from '../use_selector'; diff --git a/x-pack/plugins/security_solution/public/common/hooks/timeline/use_sync_timeline_url_param.ts b/x-pack/plugins/security_solution/public/common/hooks/timeline/use_sync_timeline_url_param.ts index f016cbfd57cc8..4d3aa88c4eaf5 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/timeline/use_sync_timeline_url_param.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/timeline/use_sync_timeline_url_param.ts @@ -8,8 +8,8 @@ import { useEffect, useMemo } from 'react'; import { useUpdateUrlParam } from '../../utils/global_query_string'; -import type { TimelineUrl } from '../../../timelines/store/timeline/model'; -import { timelineSelectors } from '../../../timelines/store/timeline'; +import type { TimelineUrl } from '../../../timelines/store/model'; +import { timelineSelectors } from '../../../timelines/store'; import { TimelineId } from '../../../../common/types'; import { useShallowEqualSelector } from '../use_selector'; import { URL_PARAM_KEY } from '../use_url_state'; diff --git a/x-pack/plugins/security_solution/public/common/hooks/timeline/use_timeline_save_prompt.ts b/x-pack/plugins/security_solution/public/common/hooks/timeline/use_timeline_save_prompt.ts index 54422da6fe038..4eb7adf15f52e 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/timeline/use_timeline_save_prompt.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/timeline/use_timeline_save_prompt.ts @@ -17,7 +17,7 @@ import { useKibana } from '../../lib/kibana'; import { useDeepEqualSelector } from '../use_selector'; import { APP_ID, APP_PATH } from '../../../../common/constants'; import { getTimelineShowStatusByIdSelector } from '../../../timelines/components/flyout/selectors'; -import { timelineActions } from '../../../timelines/store/timeline'; +import { timelineActions } from '../../../timelines/store'; import { UNSAVED_TIMELINE_SAVE_PROMPT, UNSAVED_TIMELINE_SAVE_PROMPT_TITLE, diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_app_toasts.test.ts b/x-pack/plugins/security_solution/public/common/hooks/use_app_toasts.test.ts index ad3334e536793..f2470d197d562 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_app_toasts.test.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/use_app_toasts.test.ts @@ -6,7 +6,7 @@ */ import { renderHook } from '@testing-library/react-hooks'; -import type { IEsError } from '@kbn/data-plugin/public'; +import type { IEsError } from '@kbn/search-errors'; import type { KibanaError, SecurityAppError } from '@kbn/securitysolution-t-grid'; import { useToasts } from '../lib/kibana'; diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_app_toasts.ts b/x-pack/plugins/security_solution/public/common/hooks/use_app_toasts.ts index 99500a42b8c35..7a810fa9247d2 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_app_toasts.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/use_app_toasts.ts @@ -10,8 +10,7 @@ import { isString } from 'lodash/fp'; import type { AppError } from '@kbn/securitysolution-t-grid'; import { isAppError, isKibanaError, isSecurityAppError } from '@kbn/securitysolution-t-grid'; -import type { IEsError } from '@kbn/data-plugin/public'; -import { isEsError } from '@kbn/data-plugin/public'; +import { type IEsError, isEsError } from '@kbn/search-errors'; import type { ErrorToastOptions, ToastsStart, Toast } from '@kbn/core/public'; import { useToasts } from '../lib/kibana'; diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_resolve_conflict.test.tsx b/x-pack/plugins/security_solution/public/common/hooks/use_resolve_conflict.test.tsx index e69f70234153e..3e3b28cd028f7 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_resolve_conflict.test.tsx +++ b/x-pack/plugins/security_solution/public/common/hooks/use_resolve_conflict.test.tsx @@ -20,7 +20,7 @@ jest.mock('react-router-dom', () => { }); jest.mock('../lib/kibana'); jest.mock('./use_selector'); -jest.mock('../../timelines/store/timeline', () => ({ +jest.mock('../../timelines/store', () => ({ timelineSelectors: { getTimelineByIdSelector: () => jest.fn(), }, diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_resolve_conflict.tsx b/x-pack/plugins/security_solution/public/common/hooks/use_resolve_conflict.tsx index 40eace1bd6e6e..c73b21c3b8418 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_resolve_conflict.tsx +++ b/x-pack/plugins/security_solution/public/common/hooks/use_resolve_conflict.tsx @@ -11,9 +11,9 @@ import { EuiSpacer } from '@elastic/eui'; import { safeDecode, encode } from '@kbn/rison'; import { useDeepEqualSelector } from './use_selector'; import { TimelineId } from '../../../common/types/timeline'; -import { timelineSelectors } from '../../timelines/store/timeline'; -import type { TimelineUrl } from '../../timelines/store/timeline/model'; -import { timelineDefaults } from '../../timelines/store/timeline/defaults'; +import { timelineSelectors } from '../../timelines/store'; +import type { TimelineUrl } from '../../timelines/store/model'; +import { timelineDefaults } from '../../timelines/store/defaults'; import { useKibana } from '../lib/kibana'; import { URL_PARAM_KEY } from './use_url_state'; diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_resolve_redirect.test.ts b/x-pack/plugins/security_solution/public/common/hooks/use_resolve_redirect.test.ts index 6aeb2a8b42d83..113a18dad705c 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_resolve_redirect.test.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/use_resolve_redirect.test.ts @@ -21,7 +21,7 @@ jest.mock('react-router-dom', () => { }); jest.mock('../lib/kibana'); jest.mock('./use_selector'); -jest.mock('../../timelines/store/timeline', () => ({ +jest.mock('../../timelines/store', () => ({ timelineSelectors: { getTimelineByIdSelector: () => jest.fn(), }, diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_resolve_redirect.ts b/x-pack/plugins/security_solution/public/common/hooks/use_resolve_redirect.ts index e771995e25031..35d9b4a365f13 100644 --- a/x-pack/plugins/security_solution/public/common/hooks/use_resolve_redirect.ts +++ b/x-pack/plugins/security_solution/public/common/hooks/use_resolve_redirect.ts @@ -10,10 +10,10 @@ import { useLocation } from 'react-router-dom'; import { safeDecode, encode } from '@kbn/rison'; import { useDeepEqualSelector } from './use_selector'; import { TimelineId } from '../../../common/types/timeline'; -import { timelineSelectors } from '../../timelines/store/timeline'; -import { timelineDefaults } from '../../timelines/store/timeline/defaults'; +import { timelineSelectors } from '../../timelines/store'; +import { timelineDefaults } from '../../timelines/store/defaults'; import { useKibana } from '../lib/kibana'; -import type { TimelineUrl } from '../../timelines/store/timeline/model'; +import type { TimelineUrl } from '../../timelines/store/model'; import { URL_PARAM_KEY } from './use_url_state'; /** diff --git a/x-pack/plugins/security_solution/public/common/lib/cell_actions/add_to_timeline.tsx b/x-pack/plugins/security_solution/public/common/lib/cell_actions/add_to_timeline.tsx index 3686a33814d7f..9e970bea2e639 100644 --- a/x-pack/plugins/security_solution/public/common/lib/cell_actions/add_to_timeline.tsx +++ b/x-pack/plugins/security_solution/public/common/lib/cell_actions/add_to_timeline.tsx @@ -20,7 +20,7 @@ import { } from '../../../timelines/components/timeline/data_providers/data_provider'; import { escapeDataProviderId } from '../../components/drag_and_drop/helpers'; import { EmptyComponent, useKibanaServices } from './helpers'; -import { addProvider } from '../../../timelines/store/timeline/actions'; +import { addProvider } from '../../../timelines/store/actions'; export const getAddToTimelineCellAction = ({ data, diff --git a/x-pack/plugins/security_solution/public/common/lib/telemetry/middleware.ts b/x-pack/plugins/security_solution/public/common/lib/telemetry/middleware.ts index 0085cdc999ac7..658f5b170d586 100644 --- a/x-pack/plugins/security_solution/public/common/lib/telemetry/middleware.ts +++ b/x-pack/plugins/security_solution/public/common/lib/telemetry/middleware.ts @@ -8,7 +8,7 @@ import type { Action, Dispatch, MiddlewareAPI } from 'redux'; import { track, METRIC_TYPE, TELEMETRY_EVENT } from '.'; -import * as timelineActions from '../../../timelines/store/timeline/actions'; +import * as timelineActions from '../../../timelines/store/actions'; export const telemetryMiddleware = (api: MiddlewareAPI) => (next: Dispatch) => (action: Action) => { if (timelineActions.endTimelineSaving.match(action)) { diff --git a/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts b/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts index ba567b623ab51..732097aedbc2b 100644 --- a/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts +++ b/x-pack/plugins/security_solution/public/common/mock/timeline_results.ts @@ -17,8 +17,8 @@ import type { OpenTimelineResult } from '../../timelines/components/open_timelin import type { TimelineEventsDetailsItem } from '../../../common/search_strategy'; import { Direction } from '../../../common/search_strategy'; import type { CreateTimelineProps } from '../../detections/components/alerts_table/types'; -import type { TimelineModel } from '../../timelines/store/timeline/model'; -import { timelineDefaults } from '../../timelines/store/timeline/defaults'; +import type { TimelineModel } from '../../timelines/store/model'; +import { timelineDefaults } from '../../timelines/store/defaults'; export const mockOpenTimelineQueryResults = { totalCount: 11, diff --git a/x-pack/plugins/security_solution/public/common/mock/utils.ts b/x-pack/plugins/security_solution/public/common/mock/utils.ts index 3816d72e2586c..c8a1cacf2b486 100644 --- a/x-pack/plugins/security_solution/public/common/mock/utils.ts +++ b/x-pack/plugins/security_solution/public/common/mock/utils.ts @@ -8,7 +8,7 @@ import { hostsReducer } from '../../explore/hosts/store'; import { networkReducer } from '../../explore/network/store'; import { makeUsersReducer } from '../../explore/users/store'; -import { timelineReducer } from '../../timelines/store/timeline/reducer'; +import { timelineReducer } from '../../timelines/store/reducer'; import { managementReducer } from '../../management/store/reducer'; import type { ManagementPluginReducer } from '../../management'; import type { SubPluginsInitReducer } from '../store'; diff --git a/x-pack/plugins/security_solution/public/common/store/data_table/epic_local_storage.ts b/x-pack/plugins/security_solution/public/common/store/data_table/epic_local_storage.ts index aea4c18e8b1fa..f84b29341b712 100644 --- a/x-pack/plugins/security_solution/public/common/store/data_table/epic_local_storage.ts +++ b/x-pack/plugins/security_solution/public/common/store/data_table/epic_local_storage.ts @@ -12,10 +12,10 @@ import { get } from 'lodash/fp'; import { dataTableActions } from '@kbn/securitysolution-data-table'; import type { TableIdLiteral } from '@kbn/securitysolution-data-table'; -import { updateTotalCount } from '../../../timelines/store/timeline/actions'; +import { updateTotalCount } from '../../../timelines/store/actions'; import { addTableInStorage } from '../../../timelines/containers/local_storage'; -import type { TimelineEpicDependencies } from '../../../timelines/store/timeline/types'; +import type { TimelineEpicDependencies } from '../../../timelines/store/types'; const { applyDeltaToColumnWidth, diff --git a/x-pack/plugins/security_solution/public/common/store/epic.ts b/x-pack/plugins/security_solution/public/common/store/epic.ts index 7659b3542f5de..20311d6c4a163 100644 --- a/x-pack/plugins/security_solution/public/common/store/epic.ts +++ b/x-pack/plugins/security_solution/public/common/store/epic.ts @@ -8,16 +8,15 @@ import type { Epic } from 'redux-observable'; import { combineEpics } from 'redux-observable'; import type { Action } from 'redux'; - import type { Observable } from 'rxjs'; import type { Storage } from '@kbn/kibana-utils-plugin/public'; import type { CoreStart } from '@kbn/core/public'; -import { createTimelineEpic } from '../../timelines/store/timeline/epic'; -import { createTimelineChangedEpic } from '../../timelines/store/timeline/epic_changed'; -import { createTimelineFavoriteEpic } from '../../timelines/store/timeline/epic_favorite'; -import { createTimelineNoteEpic } from '../../timelines/store/timeline/epic_note'; -import { createTimelinePinnedEventEpic } from '../../timelines/store/timeline/epic_pinned_event'; -import type { TimelineEpicDependencies } from '../../timelines/store/timeline/types'; +import { createTimelineEpic } from '../../timelines/store/epic'; +import { createTimelineChangedEpic } from '../../timelines/store/epic_changed'; +import { createTimelineFavoriteEpic } from '../../timelines/store/epic_favorite'; +import { createTimelineNoteEpic } from '../../timelines/store/epic_note'; +import { createTimelinePinnedEventEpic } from '../../timelines/store/epic_pinned_event'; +import type { TimelineEpicDependencies } from '../../timelines/store/types'; import { createDataTableLocalStorageEpic } from './data_table/epic_local_storage'; import { createUserAssetTableLocalStorageEpic } from '../../explore/users/store/epic_storage'; import type { State } from './types'; diff --git a/x-pack/plugins/security_solution/public/common/store/reducer.ts b/x-pack/plugins/security_solution/public/common/store/reducer.ts index e60f801537f4b..d7b13a93caa70 100644 --- a/x-pack/plugins/security_solution/public/common/store/reducer.ts +++ b/x-pack/plugins/security_solution/public/common/store/reducer.ts @@ -19,7 +19,7 @@ import { sourcererReducer, sourcererModel } from './sourcerer'; import type { HostsPluginReducer } from '../../explore/hosts/store'; import type { NetworkPluginReducer } from '../../explore/network/store'; import type { UsersPluginReducer } from '../../explore/users/store'; -import type { TimelinePluginReducer } from '../../timelines/store/timeline'; +import type { TimelinePluginReducer } from '../../timelines/store'; import type { SecuritySubPlugins } from '../../app/types'; import type { ManagementPluginReducer } from '../../management'; diff --git a/x-pack/plugins/security_solution/public/common/store/store.ts b/x-pack/plugins/security_solution/public/common/store/store.ts index cf53e2f795ead..b9f29867a32b5 100644 --- a/x-pack/plugins/security_solution/public/common/store/store.ts +++ b/x-pack/plugins/security_solution/public/common/store/store.ts @@ -35,9 +35,9 @@ import { } from '../../../common/constants'; import { telemetryMiddleware } from '../lib/telemetry'; import { appSelectors } from './app'; -import { timelineSelectors } from '../../timelines/store/timeline'; -import * as timelineActions from '../../timelines/store/timeline/actions'; -import type { TimelineModel } from '../../timelines/store/timeline/model'; +import { timelineSelectors } from '../../timelines/store'; +import * as timelineActions from '../../timelines/store/actions'; +import type { TimelineModel } from '../../timelines/store/model'; import { inputsSelectors } from './inputs'; import type { SubPluginsInitReducer } from './reducer'; import { createInitialState, createReducer } from './reducer'; @@ -45,7 +45,7 @@ import { createRootEpic } from './epic'; import type { AppAction } from './actions'; import type { Immutable } from '../../../common/endpoint/types'; import type { State } from './types'; -import type { TimelineEpicDependencies, TimelineState } from '../../timelines/store/timeline/types'; +import type { TimelineEpicDependencies, TimelineState } from '../../timelines/store/types'; import type { KibanaDataView, SourcererModel, SourcererDataView } from './sourcerer/model'; import { initDataView } from './sourcerer/model'; import type { AppObservableLibs, StartedSubPlugins, StartPlugins } from '../../types'; diff --git a/x-pack/plugins/security_solution/public/common/store/types.ts b/x-pack/plugins/security_solution/public/common/store/types.ts index ec41cdda85ef2..359a65d4fb122 100644 --- a/x-pack/plugins/security_solution/public/common/store/types.ts +++ b/x-pack/plugins/security_solution/public/common/store/types.ts @@ -17,7 +17,7 @@ import type { InputsState } from './inputs/reducer'; import type { SourcererState } from './sourcerer/reducer'; import type { HostsPluginState } from '../../explore/hosts/store'; import type { DragAndDropState } from './drag_and_drop/reducer'; -import type { TimelinePluginState } from '../../timelines/store/timeline'; +import type { TimelinePluginState } from '../../timelines/store'; import type { NetworkPluginState } from '../../explore/network/store'; import type { ManagementPluginState } from '../../management'; import type { UsersPluginState } from '../../explore/users/store'; diff --git a/x-pack/plugins/security_solution/public/common/utils/timeline/use_timeline_click.tsx b/x-pack/plugins/security_solution/public/common/utils/timeline/use_timeline_click.tsx index 2b0bb060d7d9b..dca72a31414ba 100644 --- a/x-pack/plugins/security_solution/public/common/utils/timeline/use_timeline_click.tsx +++ b/x-pack/plugins/security_solution/public/common/utils/timeline/use_timeline_click.tsx @@ -12,7 +12,7 @@ import { queryTimelineById, } from '../../../timelines/components/open_timeline/helpers'; import type { TimelineErrorCallback } from '../../../timelines/components/open_timeline/types'; -import { updateIsLoading as dispatchUpdateIsLoading } from '../../../timelines/store/timeline/actions'; +import { updateIsLoading as dispatchUpdateIsLoading } from '../../../timelines/store/actions'; export const useTimelineClick = () => { const dispatch = useDispatch(); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.tsx index 9eefb96be62c9..1533e3d0a2a56 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/add_exception_flyout/index.tsx @@ -500,106 +500,112 @@ export const AddExceptionFlyout = memo(function AddExceptionFlyout({ - - - {errorSubmitting != null && ( - <> - } + {errorSubmitting != null && ( + <> + + {i18n.SUBMIT_ERROR_DISMISS_MESSAGE} + + - {i18n.SUBMIT_ERROR_DISMISS_MESSAGE} - - - {i18n.SUBMIT_ERROR_DISMISS_BUTTON} - - - - - )} - - - - - {listType !== ExceptionListTypeEnum.ENDPOINT && !sharedListToAddTo?.length && ( - <> - - - - )} - - -

{i18n.COMMENTS_SECTION_TITLE(newComment ? 1 : 0)}

- - } - initialIsOpen={!!newComment} - newCommentValue={newComment} - newCommentOnChange={setComment} - setCommentError={setCommentError} - /> - {listType !== ExceptionListTypeEnum.ENDPOINT && ( - <> - - - - )} - {showAlertCloseOptions && ( - <> - - - - )} -
+ {i18n.SUBMIT_ERROR_DISMISS_BUTTON} + + + + + )} + + + + + {listType !== ExceptionListTypeEnum.ENDPOINT && !sharedListToAddTo?.length && ( + <> + + + + )} + + +

{i18n.COMMENTS_SECTION_TITLE(newComment ? 1 : 0)}

+ + } + initialIsOpen={!!newComment} + newCommentValue={newComment} + newCommentOnChange={setComment} + setCommentError={setCommentError} + /> + {listType !== ExceptionListTypeEnum.ENDPOINT && ( + <> + + + + )} + {showAlertCloseOptions && ( + <> + + + + )}
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/edit_exception_flyout/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/edit_exception_flyout/index.tsx index 6d2526cdbf239..8c27b5da22450 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/edit_exception_flyout/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_exceptions/components/edit_exception_flyout/index.tsx @@ -370,8 +370,8 @@ const EditExceptionFlyoutComponent: React.FC = ({ - {isLoading && } + {isLoading && } void; + hunks: HunkData[]; +} + +/** + * @param {HunkData[]} hunks - An array of hunk objects representing changes in a section of a string. Sections normally span multiple lines. + * @param {string} oldSource - Original string, before changes + * @returns {UseExpandReturn} - "expandRange" is function that triggers expansion, "hunks" is an array of hunks with hidden section expanded. + * + * @description + * Sections of diff without changes are hidden by default, because they are not present in the "hunks" array. + * "useExpand" allows to show these hidden sections when user clicks on "Expand hidden lines" button. + * Calling "expandRange" basically merges two adjacent hunks into one: + * - takes first hunk + * - appends all the lines between the first hunk and the second hunk + * - finally appends the second hunk + * returned "hunks" is the resulting array of hunks with hidden section expanded. + */ +const useExpand = (hunks: HunkData[], oldSource: string): UseExpandReturn => { + const [hunksWithSourceExpanded, expandRange] = useSourceExpansion(hunks, oldSource); + const hunksWithMinLinesCollapsed = useMinCollapsedLines(0, hunksWithSourceExpanded, oldSource); + + return { + expandRange, + hunks: hunksWithMinLinesCollapsed, + }; +}; + +const useTokens = ( + hunks: HunkData[], + diffMethod: DiffMethod, + oldSource: string +): HunkTokens | undefined => { + if (!hunks) { + return undefined; + } + + const options: TokenizeOptions = { + oldSource, + highlight: false, + enhancers: [ + /* + This custom "markEdits" function is a slightly modified version of "markEdits" + enhancer from react-diff-view with added support for word-level highlighting. + */ + markEdits(hunks, diffMethod), + ], + }; + + try { + /* + Synchroniously apply all the enhancers to the hunks and return an array of tokens. + */ + return tokenize(hunks, options); + } catch (ex) { + return undefined; + } +}; + +const renderGutter: RenderGutter = ({ change }) => { + /* + Custom gutter: rendering "+" or "-" so the diff is readable by colorblind people. + */ + if (change.type === 'insert') { + return {'+'}; + } + + if (change.type === 'delete') { + return {'-'}; + } + + return null; +}; + +const convertToDiffFile = (oldSource: string, newSource: string) => { + /* + "diffLines" call converts two strings of text into an array of Change objects. + */ + const changes = unidiff.diffLines(oldSource, newSource); + + /* + Then "formatLines" takes an array of Change objects and turns it into a single "unified diff" string. + More info about the "unified diff" format: https://en.wikipedia.org/wiki/Diff_utility#Unified_format + Unified diff is a string with change markers added. Looks something like: + ` + @@ -3,16 +3,15 @@ + "author": ["Elastic"], + - "from": "now-540s", + + "from": "now-9m", + "history_window_start": "now-14d", + ` + */ + const unifiedDiff: string = unidiff.formatLines(changes, { + context: 3, + }); + + /* + "parseDiff" converts a unified diff string into a gitdiff-parser File object. + + File object contains some metadata and the "hunks" property - an array of Hunk objects. + Hunks represent changed lines of code plus a few unchanged lines above and below for context. + */ + const [diffFile] = parseDiff(unifiedDiff, { + nearbySequences: 'zip', + }); + + return diffFile; +}; + +const CustomStyles: React.FC = ({ children }) => { + const { euiTheme } = useEuiTheme(); + + const customCss = css` + .${CODE_CLASS_NAME}.diff-code { + padding: 0 ${euiTheme.size.l} 0 ${euiTheme.size.m}; + } + + .${TABLE_CLASS_NAME} .diff-gutter-col { + width: ${euiTheme.size.xl}; + } + + /* Vertical line separating two sides of the diff view */ + .${GUTTER_CLASS_NAME}:nth-child(3) { + border-left: 1px solid ${euiTheme.colors.mediumShade}; + } + + /* Gutter of a line with deletions */ + .${GUTTER_CLASS_NAME}.diff-gutter-delete { + font-weight: bold; + background: ${COLORS.light.gutterBackground.deletion}; + } + .${DARK_THEME_CLASS_NAME} .${GUTTER_CLASS_NAME}.diff-gutter-delete { + background: ${COLORS.dark.gutterBackground.deletion}; + } + + /* Gutter of a line with insertions */ + .${GUTTER_CLASS_NAME}.diff-gutter-insert { + font-weight: bold; + background: ${COLORS.light.gutterBackground.insertion}; + } + .${DARK_THEME_CLASS_NAME} .${GUTTER_CLASS_NAME}.diff-gutter-insert { + background: ${COLORS.dark.gutterBackground.insertion}; + } + + /* Background of a line with deletions */ + .${CODE_CLASS_NAME}.diff-code-delete { + background: ${COLORS.light.lineBackground.deletion}; + } + .${DARK_THEME_CLASS_NAME} .${CODE_CLASS_NAME}.diff-code-delete { + background: ${COLORS.dark.lineBackground.deletion}; + } + + /* Background of a line with insertions */ + .${CODE_CLASS_NAME}.diff-code-insert { + background: ${COLORS.light.lineBackground.insertion}; + } + .${DARK_THEME_CLASS_NAME} .${CODE_CLASS_NAME}.diff-code-insert { + background: ${COLORS.dark.lineBackground.insertion}; + } + + /* Accented background of removed characters / words */ + .${CODE_CLASS_NAME}.diff-code-delete .diff-code-edit { + font-weight: 700; + background: ${COLORS.light.characterBackground.deletion}; + } + .${DARK_THEME_CLASS_NAME} .${CODE_CLASS_NAME}.diff-code-delete .diff-code-edit { + background: ${COLORS.dark.characterBackground.deletion}; + } + + /* Accented background of inserted characters / words */ + .${CODE_CLASS_NAME}.diff-code-insert .diff-code-edit { + font-weight: 700; + background: ${COLORS.light.characterBackground.insertion}; + } + .${DARK_THEME_CLASS_NAME} .${CODE_CLASS_NAME}.diff-code-insert .diff-code-edit { + background: ${COLORS.dark.characterBackground.insertion}; + } + `; + + return ( + <> + + {children} + + ); +}; + +interface DiffViewProps extends Partial { + oldSource: string; + newSource: string; + diffMethod?: DiffMethod; +} + +export const DiffView = ({ + oldSource, + newSource, + diffMethod = DiffMethod.WORDS, +}: DiffViewProps) => { + /* + "react-diff-view" components consume diffs not as a strings, but as something they call "hunks". + So we first need to convert our "before" and "after" strings into these "hunk" objects. + "hunks" describe changed sections of code plus a few unchanged lines above and below for context. + */ + + /* + "diffFile" is essentially an object containing an array of hunks plus some metadata. + */ + const diffFile = useMemo(() => convertToDiffFile(oldSource, newSource), [oldSource, newSource]); + + /* + Sections of diff without changes are hidden by default, because they are not present in the "hunks" array. + "useExpand" allows to show these hidden sections when a user clicks on "Expand hidden lines" button. + */ + const { expandRange, hunks } = useExpand(diffFile.hunks, oldSource); + + /* + Go over each hunk and extract tokens from it. For example, split strings into words or characters, + so we can highlight them later. + */ + const tokens = useTokens(hunks, diffMethod, oldSource); + + const { colorMode } = useEuiTheme(); + + const tableClassName = classNames(TABLE_CLASS_NAME, { + [DARK_THEME_CLASS_NAME]: colorMode === COLOR_MODES_STANDARD.dark, + }); + + return ( + + + {/* eslint-disable-next-line @typescript-eslint/no-shadow */} + {(hunks) => } + + + ); +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/json_diff/hunks.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/json_diff/hunks.tsx new file mode 100644 index 0000000000000..a1bada553bab7 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/json_diff/hunks.tsx @@ -0,0 +1,124 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React, { useCallback } from 'react'; +import type { ReactElement } from 'react'; +import { Hunk, Decoration, getCollapsedLinesCountBetween } from 'react-diff-view'; +import type { HunkData, DecorationProps } from 'react-diff-view'; +import { EuiSpacer, EuiIcon, EuiLink, EuiFlexGroup, EuiText } from '@elastic/eui'; +import * as i18n from './translations'; + +interface UnfoldButtonProps extends Omit { + start: number; + end: number; + onExpand: (start: number, end: number) => void; +} + +const UnfoldButton = ({ start, end, onExpand, ...props }: UnfoldButtonProps) => { + const expand = useCallback(() => onExpand(start, end), [onExpand, start, end]); + + const linesCount = end - start; + + return ( + + + {start > 1 && } + + + + + {i18n.EXPAND_UNCHANGED_LINES(linesCount)} + + + + + + + ); +}; + +interface UnfoldCollapsedProps { + previousHunk: HunkData; + currentHunk?: HunkData; + linesCount: number; + onExpand: (start: number, end: number) => void; +} + +const UnfoldCollapsed = ({ + previousHunk, + currentHunk, + linesCount, + onExpand, +}: UnfoldCollapsedProps) => { + if (!currentHunk) { + const nextStart = previousHunk.oldStart + previousHunk.oldLines; + const collapsedLines = linesCount - nextStart + 1; + + if (collapsedLines <= 0) { + return null; + } + + return ; + } + + const collapsedLines = getCollapsedLinesCountBetween(previousHunk, currentHunk); + + if (!previousHunk) { + if (!collapsedLines) { + return null; + } + + return ; + } + + const collapsedStart = previousHunk.oldStart + previousHunk.oldLines; + const collapsedEnd = currentHunk.oldStart; + + return ; +}; + +interface HunksProps { + hunks: HunkData[]; + oldSource: string; + expandRange: (start: number, end: number) => void; +} + +export const Hunks = ({ hunks, oldSource, expandRange }: HunksProps) => { + const linesCount = oldSource.split('\n').length; + + const hunkElements = hunks.reduce((children: ReactElement[], hunk: HunkData, index: number) => { + const previousElement = children[children.length - 1]; + + children.push( + + ); + + children.push(); + + const isLastHunk = index === hunks.length - 1; + if (isLastHunk && oldSource) { + children.push( + + ); + } + + return children; + }, []); + + return <>{hunkElements}; +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/json_diff/mark_edits.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/json_diff/mark_edits.tsx new file mode 100644 index 0000000000000..95eaefdc37f15 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/json_diff/mark_edits.tsx @@ -0,0 +1,290 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { findIndex, flatMap, flatten } from 'lodash'; +import * as diff from 'diff'; +import type { Change as DiffJsChange } from 'diff'; +import { isDelete, isInsert, isNormal, pickRanges } from 'react-diff-view'; +import type { ChangeData, HunkData, RangeTokenNode, TokenizeEnhancer } from 'react-diff-view'; + +enum DmpChangeType { + DELETE = -1, + EQUAL = 0, + INSERT = 1, +} + +type Diff = [DmpChangeType, string]; + +type StringDiffFn = (oldString: string, newString: string) => DiffJsChange[]; + +interface JsDiff { + diffChars: StringDiffFn; + diffWords: StringDiffFn; + diffWordsWithSpace: StringDiffFn; + diffLines: StringDiffFn; + diffTrimmedLines: StringDiffFn; + diffSentences: StringDiffFn; + diffCss: StringDiffFn; +} + +const jsDiff: JsDiff = diff; + +export enum DiffMethod { + CHARS = 'diffChars', + WORDS = 'diffWords', + WORDS_WITH_SPACE = 'diffWordsWithSpace', + LINES = 'diffLines', + TRIMMED_LINES = 'diffTrimmedLines', + SENTENCES = 'diffSentences', + CSS = 'diffCss', +} + +/** + * @param {ChangeData[]} changes - An array representing the changes in the block. + * Each hunk represents a section of a string and includes information about the changes in that section. + * Sections normally span multiple lines. + * @param {DiffMethod} diffMethod - Diffing algorithm to use for token extraction. For example, "diffWords" will tokenize the string into words. + * + * @returns {TokenizeEnhancer} A react-diff-view plugin that processes diff hunks and returns an array of tokens. + * Tokens are then used to render "added" / "removed" diff highlighting. + * + * @description + * Converts the given ChangeData array to two strings representing the old source and new source of a change block. + * The format of the strings is as follows: + */ +function findChangeBlocks(changes: ChangeData[]): ChangeData[][] { + const start = findIndex(changes, (change) => !isNormal(change)); + + if (start === -1) { + return []; + } + + const end = findIndex(changes, (change) => !!isNormal(change), start); + + if (end === -1) { + return [changes.slice(start)]; + } + + return [changes.slice(start, end), ...findChangeBlocks(changes.slice(end))]; +} + +function groupDiffs(diffs: Diff[]): [Diff[], Diff[]] { + return diffs.reduce<[Diff[], Diff[]]>( + // eslint-disable-next-line @typescript-eslint/no-shadow + ([oldDiffs, newDiffs], diff) => { + const [type] = diff; + + switch (type) { + case DmpChangeType.INSERT: + newDiffs.push(diff); + break; + case DmpChangeType.DELETE: + oldDiffs.push(diff); + break; + default: + oldDiffs.push(diff); + newDiffs.push(diff); + break; + } + + return [oldDiffs, newDiffs]; + }, + [[], []] + ); +} + +/** + * @param {Diff[]} diffs An array of changes in the diff-match-patch format + * @returns {Diff[][]} An array of arrays, where changes are grouped by a line number. + */ +function splitDiffToLines(diffs: Diff[]): Diff[][] { + return diffs.reduce( + (lines, [type, value]) => { + const currentLines = value.split('\n'); + + const [currentLineRemaining, ...nextLines] = currentLines.map( + (line: string): Diff => [type, line] + ); + const next: Diff[][] = [ + ...lines.slice(0, -1), + [...lines[lines.length - 1], currentLineRemaining], + ...nextLines.map((line) => [line]), + ]; + return next; + }, + [[]] + ); +} + +/** + * @param {Diff[]} diffs An array of changes within a single line in the diff-match-patch format + * @param {number} lineNumber Line number where the changes are found + * @returns {RangeTokenNode[]} Array of "edit" objects where each item contains + * info about line number and start / end character positions. + */ +function diffsToEdits(diffs: Diff[], lineNumber: number): RangeTokenNode[] { + const output = diffs.reduce<[RangeTokenNode[], number]>( + // eslint-disable-next-line @typescript-eslint/no-shadow + (output, diff) => { + const [edits, start] = output; + const [type, value] = diff; + if (type !== DmpChangeType.EQUAL) { + const edit: RangeTokenNode = { + type: 'edit', + lineNumber, + start, + length: value.length, + }; + edits.push(edit); + } + + return [edits, start + value.length]; + }, + [[], 0] + ); + + return output[0]; +} + +/** + * @param {Diff[][]} linesOfDiffs - Changes in a diff-match-patch format, grouped by a line number. + * @param {number} startLineNumber - Line number of the first line. + * @returns {RangeTokenNode[]} Flattened array of "edit" objects where each item contains + * info about line number and start / end character positions. + */ +function convertToLinesOfEdits(linesOfDiffs: Diff[][], startLineNumber: number): RangeTokenNode[] { + return flatMap(linesOfDiffs, (diffs, i) => diffsToEdits(diffs, startLineNumber + i)); +} + +/** + * @param {DiffMethod} diffMethod - Diffing algorithm to use for token extraction. + * @param {string} oldSource - A substring of the original source string. + * @param {string} newSource - A corresponding substring of the new source string. + * @returns {[Diff[], Diff[]]} Two arrays of changes in the diff-match-patch format. + * Every item is a tuple of two values: [, ]. + * + * @description Runs two strings through the chosen diffing algorithm using the "diff" library to determine + * which parts of the original string were added / removed / unchanged. Then returns an array of changes in + * the diff-match-patch diff format. + */ +function diffBy(diffMethod: DiffMethod, oldSource: string, newSource: string): [Diff[], Diff[]] { + /* Diff two substrings using the "diff" library */ + const jsDiffChanges: DiffJsChange[] = jsDiff[diffMethod](oldSource, newSource); + /* Convert the result to the diff-match-patch format, because that's the format react-diff-view methods expect */ + const diffs: Diff[] = diff.convertChangesToDMP(jsDiffChanges); + + if (diffs.length <= 1) { + return [[], []]; + } + + /* Split diff-match-patch formatted diffs into two arrays: one for the old source and one for the new source */ + return groupDiffs(diffs); +} + +const getLineNumber = (change: ChangeData | undefined) => { + if (!change || isNormal(change)) { + return undefined; + } + + return change.lineNumber; +}; + +/** + * @param {ChangeData[]} changes - An array of сhange objects. Each change object represents changes in a single line. + * @param {DiffMethod} diffMethod - Diffing algorithm to use for token extraction. + * @returns {[RangeTokenNode[], RangeTokenNode[]]} A tuple containing two arrays of RangeTokenNodes - one for + * the old source and another one for the new source. Each RangeTokenNode contains information about line numbers + * and character positions of changes. + * + * @description This function processes change objects and determines exactly which segments of the orginal string changed. + * It diffs old and new substrings and computes at which character position each change starts and ends, + * taking the diffing algorithm into account (by char, by word, by sentence, etc.) + */ +function diffChangeBlock( + changes: ChangeData[], + diffMethod: DiffMethod +): [RangeTokenNode[], RangeTokenNode[]] { + /* + Convert an array of change objects into two strings representing the old source and the new source of a change block. + Basically, recreate parts of the original strings from change objects so we can pass these strings to the text diffing library. + */ + const [oldSourceSnippet, newSourceSnippet] = changes.reduce( + // eslint-disable-next-line @typescript-eslint/no-shadow + ([oldSourceSnippet, newSourceSnippet], change) => + isDelete(change) + ? [oldSourceSnippet + (oldSourceSnippet ? '\n' : '') + change.content, newSourceSnippet] + : [oldSourceSnippet, newSourceSnippet + (newSourceSnippet ? '\n' : '') + change.content], + ['', ''] + ); + + /* + * Run the chosen diffing algorithm with an "old" and a "new" substrings as input. + * The result is an array of changes in the diff-match-patch format. + */ + const [oldDiffs, newDiffs] = diffBy(diffMethod, oldSourceSnippet, newSourceSnippet); + + if (oldDiffs.length === 0 && newDiffs.length === 0) { + return [[], []]; + } + + const oldStartLineNumber = getLineNumber(changes.find(isDelete)); + const newStartLineNumber = getLineNumber(changes.find(isInsert)); + + if (oldStartLineNumber === undefined || newStartLineNumber === undefined) { + throw new Error('Could not find start line number for edit'); + } + + /* + * Group changes by a line number they are found in, then determine start / end character + * positions of each change. + */ + const oldEdits = convertToLinesOfEdits(splitDiffToLines(oldDiffs), oldStartLineNumber); + const newEdits = convertToLinesOfEdits(splitDiffToLines(newDiffs), newStartLineNumber); + + return [oldEdits, newEdits]; +} + +/** + * @param {HunkData[]} hunks - An array of hunk objects. + * Each hunk represents a section of a string and includes information about the changes in that section. + * Sections normally span multiple lines. + * @param {DiffMethod} diffMethod - Diffing algorithm to use for token extraction. For example, "diffWords" will tokenize the string into words. + * + * @returns {TokenizeEnhancer} A react-diff-view plugin that processes diff hunks and returns an array of tokens. + * Tokens are then used to render "added" / "removed" diff highlighting. + * + * @description + * Converts the given ChangeData array to two strings representing the old source and new source of a change block. + * The format of the strings is as follows: + */ +export function markEdits(hunks: HunkData[], diffMethod: DiffMethod): TokenizeEnhancer { + /* + changeBlocks is an array that contains information about the lines that have changes (additions or deletions). + Unchanged lines are not included. + */ + const changeBlocks = flatMap( + hunks.map((hunk) => hunk.changes), + findChangeBlocks + ); + + const [oldEdits, newEdits] = changeBlocks + /* + diffChangeBlock diffs two substrings and determines character positions of changes, + taking the diffing algorithm into account (by char, by word, by sentence, etc.) + */ + .map((changes) => diffChangeBlock(changes, diffMethod)) + .reduce( + // eslint-disable-next-line @typescript-eslint/no-shadow + ([oldEdits, newEdits], [currentOld, currentNew]) => [ + oldEdits.concat(currentOld), + newEdits.concat(currentNew), + ], + [[], []] + ); + + return pickRanges(flatten(oldEdits), flatten(newEdits)); +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/json_diff/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/json_diff/translations.ts new file mode 100644 index 0000000000000..8720a6b57f510 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/json_diff/translations.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { i18n } from '@kbn/i18n'; + +export const EXPAND_UNCHANGED_LINES = (linesCount: number) => + i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.upgradeRules.expandHiddenDiffLinesLabel', + { + values: { linesCount }, + defaultMessage: + 'Expand {linesCount} unchanged {linesCount, plural, one {line} other {lines}}', + } + ); + +export const BASE_VERSION = i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.upgradeRules.baseVersionLabel', + { + defaultMessage: 'Base version', + } +); + +export const BASE_VERSION_DESCRIPTION = i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.upgradeRules.baseVersionDescriptionLabel', + { + defaultMessage: 'Shows currently installed rule', + } +); + +export const UPDATED_VERSION = i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.upgradeRules.updatedVersionLabel', + { + defaultMessage: 'Update', + } +); + +export const UPDATED_VERSION_DESCRIPTION = i18n.translate( + 'xpack.securitySolution.detectionEngine.rules.upgradeRules.updatedVersionDescriptionLabel', + { + defaultMessage: 'Shows rule that will be installed', + } +); diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/json_diff/unidiff.d.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/json_diff/unidiff.d.ts new file mode 100644 index 0000000000000..d7fa438700e97 --- /dev/null +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/json_diff/unidiff.d.ts @@ -0,0 +1,23 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +declare module 'unidiff' { + interface Change { + count?: number | undefined; + value: string; + added?: boolean | undefined; + removed?: boolean | undefined; + } + + export interface FormatOptions { + context?: number; + } + + export function diffLines(x: string, y: string): Change[]; + + export function formatLines(line: Change[], options?: FormatOptions): string; +} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_about_section.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_about_section.tsx index 282d3fcc8439a..125a018a1304a 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_about_section.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_about_section.tsx @@ -33,7 +33,7 @@ import { filterEmptyThreats } from '../../../rule_creation_ui/pages/rule_creatio import { ThreatEuiFlexGroup } from '../../../../detections/components/rules/description_step/threat_description'; import { BadgeList } from './badge_list'; -import { DESCRIPTION_LIST_COLUMN_WIDTHS } from './constants'; +import { DEFAULT_DESCRIPTION_LIST_COLUMN_WIDTHS } from './constants'; import * as i18n from './translations'; const OverrideColumn = styled(EuiFlexItem)` @@ -426,12 +426,14 @@ const prepareAboutSectionListItems = ( export interface RuleAboutSectionProps extends React.ComponentProps { rule: Partial; + columnWidths?: EuiDescriptionListProps['columnWidths']; hideName?: boolean; hideDescription?: boolean; } export const RuleAboutSection = ({ rule, + columnWidths = DEFAULT_DESCRIPTION_LIST_COLUMN_WIDTHS, hideName, hideDescription, ...descriptionListProps @@ -445,7 +447,7 @@ export const RuleAboutSection = ({ type={descriptionListProps.type ?? 'column'} rowGutterSize={descriptionListProps.rowGutterSize ?? 'm'} listItems={aboutSectionListItems} - columnWidths={DESCRIPTION_LIST_COLUMN_WIDTHS} + columnWidths={columnWidths} data-test-subj="listItemColumnStepRuleDescription" {...descriptionListProps} /> diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_definition_section.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_definition_section.tsx index 0d284ed4eea07..c2c85fca93f03 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_definition_section.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_definition_section.tsx @@ -52,7 +52,7 @@ import { useSecurityJobs } from '../../../../common/components/ml_popover/hooks/ import { useKibana } from '../../../../common/lib/kibana/kibana_react'; import { TechnicalPreviewBadge } from '../../../../detections/components/rules/technical_preview_badge'; import { BadgeList } from './badge_list'; -import { DESCRIPTION_LIST_COLUMN_WIDTHS } from './constants'; +import { DEFAULT_DESCRIPTION_LIST_COLUMN_WIDTHS } from './constants'; import * as i18n from './translations'; import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; import type { ExperimentalFeatures } from '../../../../../common/experimental_features'; @@ -724,6 +724,7 @@ const prepareDefinitionSectionListItems = ( export interface RuleDefinitionSectionProps extends React.ComponentProps { rule: Partial; + columnWidths?: EuiDescriptionListProps['columnWidths']; isInteractive?: boolean; dataTestSubj?: string; } @@ -731,6 +732,7 @@ export interface RuleDefinitionSectionProps export const RuleDefinitionSection = ({ rule, isInteractive = false, + columnWidths = DEFAULT_DESCRIPTION_LIST_COLUMN_WIDTHS, dataTestSubj, ...descriptionListProps }: RuleDefinitionSectionProps) => { @@ -756,7 +758,7 @@ export const RuleDefinitionSection = ({ type={descriptionListProps.type ?? 'column'} rowGutterSize={descriptionListProps.rowGutterSize ?? 'm'} listItems={definitionSectionListItems} - columnWidths={DESCRIPTION_LIST_COLUMN_WIDTHS} + columnWidths={columnWidths} data-test-subj="listItemColumnStepRuleDescription" {...descriptionListProps} /> diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_details_flyout.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_details_flyout.tsx index aa221b6cdb147..c2d00c819253a 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_details_flyout.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_details_flyout.tsx @@ -21,11 +21,15 @@ import { EuiFlexGroup, EuiFlexItem, } from '@elastic/eui'; -import type { EuiTabbedContentTab, EuiTabbedContentProps } from '@elastic/eui'; +import type { EuiTabbedContentTab, EuiTabbedContentProps, EuiFlyoutProps } from '@elastic/eui'; import type { RuleResponse } from '../../../../../common/api/detection_engine/model/rule_schema'; import { RuleOverviewTab, useOverviewTabSections } from './rule_overview_tab'; import { RuleInvestigationGuideTab } from './rule_investigation_guide_tab'; +import { + DEFAULT_DESCRIPTION_LIST_COLUMN_WIDTHS, + LARGE_DESCRIPTION_LIST_COLUMN_WIDTHS, +} from './constants'; import * as i18n from './translations'; @@ -95,13 +99,15 @@ const tabPaddingClassName = css` padding: 0 ${euiThemeVars.euiSizeM} ${euiThemeVars.euiSizeXL} ${euiThemeVars.euiSizeM}; `; -const TabContentPadding: React.FC = ({ children }) => ( +export const TabContentPadding: React.FC = ({ children }) => (
{children}
); interface RuleDetailsFlyoutProps { rule: RuleResponse; ruleActions?: React.ReactNode; + size?: EuiFlyoutProps['size']; + extraTabs?: EuiTabbedContentTab[]; dataTestSubj?: string; closeFlyout: () => void; } @@ -109,6 +115,8 @@ interface RuleDetailsFlyoutProps { export const RuleDetailsFlyout = ({ rule, ruleActions, + size = 'm', + extraTabs = [], dataTestSubj, closeFlyout, }: RuleDetailsFlyoutProps) => { @@ -122,13 +130,18 @@ export const RuleDetailsFlyout = ({ ), }), - [rule, expandedOverviewSections, toggleOverviewSection] + [rule, size, expandedOverviewSections, toggleOverviewSection] ); const investigationGuideTab: EuiTabbedContentTab = useMemo( @@ -146,11 +159,11 @@ export const RuleDetailsFlyout = ({ const tabs = useMemo(() => { if (rule.note) { - return [overviewTab, investigationGuideTab]; + return [...extraTabs, overviewTab, investigationGuideTab]; } else { - return [overviewTab]; + return [...extraTabs, overviewTab]; } - }, [overviewTab, investigationGuideTab, rule.note]); + }, [overviewTab, investigationGuideTab, rule.note, extraTabs]); const [selectedTabId, setSelectedTabId] = useState(tabs[0].id); const selectedTab = tabs.find((tab) => tab.id === selectedTabId) ?? tabs[0]; @@ -168,7 +181,7 @@ export const RuleDetailsFlyout = ({ return ( ): string => + stringify(jsObject, { space: 2 }); + +interface RuleDiffTabProps { + oldRule: RuleResponse; + newRule: RuleResponse; +} + +export const RuleDiffTab = ({ oldRule, newRule }: RuleDiffTabProps) => { + const [oldSource, newSource] = useMemo(() => { + const visibleOldRuleProperties = omit(oldRule, 'revision'); + const visibleNewRuleProperties = omit(newRule, 'revision'); + + return [ + sortAndStringifyJson(visibleOldRuleProperties), + sortAndStringifyJson(visibleNewRuleProperties), + ]; + }, [oldRule, newRule]); + + return ( + <> + + + + + + +
{i18n.BASE_VERSION}
+
+
+ + + +
{i18n.UPDATED_VERSION}
+
+
+
+ + +
+ + ); +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_overview_tab.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_overview_tab.tsx index 9f8be8c180f94..7fd3cc270286c 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_overview_tab.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_overview_tab.tsx @@ -14,11 +14,13 @@ import { EuiHorizontalRule, useGeneratedHtmlId, } from '@elastic/eui'; +import type { EuiDescriptionListProps } from '@elastic/eui'; import type { RuleResponse } from '../../../../../common/api/detection_engine/model/rule_schema'; import { RuleAboutSection, Description } from './rule_about_section'; import { RuleDefinitionSection } from './rule_definition_section'; import { RuleScheduleSection } from './rule_schedule_section'; import { RuleSetupGuideSection } from './rule_setup_guide_section'; +import { DEFAULT_DESCRIPTION_LIST_COLUMN_WIDTHS } from './constants'; import * as i18n from './translations'; @@ -87,52 +89,56 @@ const ExpandableSection = ({ title, isOpen, toggle, children }: ExpandableSectio interface RuleOverviewTabProps { rule: RuleResponse; + columnWidths?: EuiDescriptionListProps['columnWidths']; expandedOverviewSections: Record; toggleOverviewSection: Record void>; } export const RuleOverviewTab = ({ rule, + columnWidths = DEFAULT_DESCRIPTION_LIST_COLUMN_WIDTHS, expandedOverviewSections, toggleOverviewSection, -}: RuleOverviewTabProps) => ( - <> - - - {rule.description && } - - - - - - - - - - - {rule.setup && ( - <> - - - - - - )} - -); +}: RuleOverviewTabProps) => { + return ( + <> + + + {rule.description && } + + + + + + + + + + + {rule.setup && ( + <> + + + + + + )} + + ); +}; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_schedule_section.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_schedule_section.tsx index 938499ff4f31b..0d77961a5206d 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_schedule_section.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/rule_schedule_section.tsx @@ -7,9 +7,10 @@ import React from 'react'; import { EuiDescriptionList, EuiText } from '@elastic/eui'; +import type { EuiDescriptionListProps } from '@elastic/eui'; import type { RuleResponse } from '../../../../../common/api/detection_engine/model/rule_schema'; import { getHumanizedDuration } from '../../../../detections/pages/detection_engine/rules/helpers'; -import { DESCRIPTION_LIST_COLUMN_WIDTHS } from './constants'; +import { DEFAULT_DESCRIPTION_LIST_COLUMN_WIDTHS } from './constants'; import * as i18n from './translations'; interface IntervalProps { @@ -35,10 +36,12 @@ const From = ({ from, interval }: FromProps) => ( export interface RuleScheduleSectionProps extends React.ComponentProps { rule: Partial; + columnWidths?: EuiDescriptionListProps['columnWidths']; } export const RuleScheduleSection = ({ rule, + columnWidths = DEFAULT_DESCRIPTION_LIST_COLUMN_WIDTHS, ...descriptionListProps }: RuleScheduleSectionProps) => { if (!rule.interval || !rule.from) { @@ -64,7 +67,7 @@ export const RuleScheduleSection = ({ type={descriptionListProps.type ?? 'column'} rowGutterSize={descriptionListProps.rowGutterSize ?? 'm'} listItems={ruleSectionListItems} - columnWidths={DESCRIPTION_LIST_COLUMN_WIDTHS} + columnWidths={columnWidths} {...descriptionListProps} />
diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/translations.ts b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/translations.ts index 1d159bf24a392..dffdf89d31feb 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/translations.ts +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management/components/rule_details/translations.ts @@ -21,6 +21,13 @@ export const INVESTIGATION_GUIDE_TAB_LABEL = i18n.translate( } ); +export const UPDATES_TAB_LABEL = i18n.translate( + 'xpack.securitySolution.detectionEngine.ruleDetails.updatesTabLabel', + { + defaultMessage: 'Updates', + } +); + export const DISMISS_BUTTON_LABEL = i18n.translate( 'xpack.securitySolution.detectionEngine.ruleDetails.dismissButtonLabel', { diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table_filters/rule_search_field.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table_filters/rule_search_field.tsx index 75d132b761654..b23786f07f379 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table_filters/rule_search_field.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/rules_table_filters/rule_search_field.tsx @@ -14,8 +14,7 @@ import * as i18n from '../../../../../detections/pages/detection_engine/rules/tr const SearchBarWrapper = styled(EuiFlexItem)` min-width: 200px; - & .euiPopover, - & .euiPopover__anchor { + & .euiPopover { // This is needed to "cancel" styles passed down from EuiTourStep that // interfere with EuiFieldSearch and don't allow it to take the full width display: block; diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/upgrade_prebuilt_rules_table_context.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/upgrade_prebuilt_rules_table_context.tsx index 290f85ade3a03..4931943c3c114 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/upgrade_prebuilt_rules_table_context.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/components/rules_table/upgrade_prebuilt_rules_table/upgrade_prebuilt_rules_table_context.tsx @@ -8,6 +8,7 @@ import type { Dispatch, SetStateAction } from 'react'; import React, { createContext, useCallback, useContext, useMemo, useState } from 'react'; import { EuiButton } from '@elastic/eui'; +import type { EuiTabbedContentTab } from '@elastic/eui'; import { useIsUpgradingSecurityPackages } from '../../../../rule_management/logic/use_upgrade_security_packages'; import { useInstalledSecurityJobs } from '../../../../../common/components/ml/hooks/use_installed_security_jobs'; import { useBoolState } from '../../../../../common/hooks/use_bool_state'; @@ -24,10 +25,15 @@ import type { UpgradePrebuiltRulesTableFilterOptions } from './use_filter_prebui import { useFilterPrebuiltRulesToUpgrade } from './use_filter_prebuilt_rules_to_upgrade'; import { useAsyncConfirmation } from '../rules_table/use_async_confirmation'; import { useRuleDetailsFlyout } from '../../../../rule_management/components/rule_details/use_rule_details_flyout'; -import { RuleDetailsFlyout } from '../../../../rule_management/components/rule_details/rule_details_flyout'; -import * as i18n from './translations'; - +import { + RuleDetailsFlyout, + TabContentPadding, +} from '../../../../rule_management/components/rule_details/rule_details_flyout'; +import { RuleDiffTab } from '../../../../rule_management/components/rule_details/rule_diff_tab'; import { MlJobUpgradeModal } from '../../../../../detections/components/modals/ml_job_upgrade_modal'; +import { useIsExperimentalFeatureEnabled } from '../../../../../common/hooks/use_experimental_features'; +import * as ruleDetailsI18n from '../../../../rule_management/components/rule_details/translations'; +import * as i18n from './translations'; export interface UpgradePrebuiltRulesTableState { /** @@ -111,6 +117,10 @@ export const UpgradePrebuiltRulesTableContextProvider = ({ tags: [], }); + const isJsonPrebuiltRulesDiffingEnabled = useIsExperimentalFeatureEnabled( + 'jsonPrebuiltRulesDiffingEnabled' + ); + const isUpgradingSecurityPackages = useIsUpgradingSecurityPackages(); const { @@ -257,6 +267,29 @@ export const UpgradePrebuiltRulesTableContextProvider = ({ actions, ]); + const extraTabs = useMemo(() => { + const activeRule = + isJsonPrebuiltRulesDiffingEnabled && + previewedRule && + filteredRules.find(({ id }) => id === previewedRule.id); + + if (!activeRule) { + return []; + } + + return [ + { + id: 'updates', + name: ruleDetailsI18n.UPDATES_TAB_LABEL, + content: ( + + + + ), + }, + ]; + }, [previewedRule, filteredRules, isJsonPrebuiltRulesDiffingEnabled]); + return ( <> @@ -271,6 +304,7 @@ export const UpgradePrebuiltRulesTableContextProvider = ({ {previewedRule && ( } + extraTabs={extraTabs} /> )} diff --git a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/rule_management/index.tsx b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/rule_management/index.tsx index 3e390eb29b42d..17c5368a4b1c5 100644 --- a/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/rule_management/index.tsx +++ b/x-pack/plugins/security_solution/public/detection_engine/rule_management_ui/pages/rule_management/index.tsx @@ -35,8 +35,6 @@ import { AllRules } from '../../components/rules_table'; import { RulesTableContextProvider } from '../../components/rules_table/rules_table/rules_table_context'; import { useInvalidateFetchCoverageOverviewQuery } from '../../../rule_management/api/hooks/use_fetch_coverage_overview_query'; import { HeaderPage } from '../../../../common/components/header_page'; -import { RulesPageTourComponent } from '../../components/rules_table/alternative_tour/tour'; -import { useIsEsqlRuleTypeEnabled } from '../../../rule_creation/hooks'; const RulesPageComponent: React.FC = () => { const [isImportModalVisible, showImportModal, hideImportModal] = useBoolState(); @@ -68,12 +66,12 @@ const RulesPageComponent: React.FC = () => { const { loading: listsConfigLoading, canWriteIndex: canWriteListsIndex, + canCreateIndex: canCreateListsIndex, needsConfiguration: needsListsConfiguration, + needsIndex: needsListsIndex, } = useListsConfig(); const loading = userInfoLoading || listsConfigLoading; - const isEsqlRuleTypeEnabled = useIsEsqlRuleTypeEnabled(); - if ( redirectToDetections( isSignalIndexExists, @@ -89,17 +87,13 @@ const RulesPageComponent: React.FC = () => { return null; } - const addNewRuleButton = ( - - {i18n.ADD_NEW_RULE} - - ); + // - if lists data stream does not exist and user doesn't have enough privileges to create it, + // lists button should be disabled + // - if data stream exists and user doesn't have enough privileges to create it, + // user still can import value lists, so button should not be disabled if user has enough other privileges + const cantCreateNonExistentListIndex = needsListsIndex && !canCreateListsIndex; + const isImportValueListDisabled = + cantCreateNonExistentListIndex || !canWriteListsIndex || !canUserCRUD || loading; return ( <> @@ -133,11 +127,18 @@ const RulesPageComponent: React.FC = () => { - + {i18n.IMPORT_VALUE_LISTS} @@ -155,11 +156,15 @@ const RulesPageComponent: React.FC = () => { - {isEsqlRuleTypeEnabled ? ( - {addNewRuleButton} - ) : ( - addNewRuleButton - )} + + {i18n.ADD_NEW_RULE} + diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/alerts_histogram.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/alerts_histogram.tsx index 7137ba563d365..9f96b375abc98 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/alerts_histogram.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_kpis/alerts_histogram_panel/alerts_histogram.tsx @@ -95,6 +95,7 @@ export const AlertsHistogram = React.memo( yScaleType={ScaleType.Linear} xAccessor="x" yAccessors={yAccessors} + stackAccessors={['true']} splitSeriesAccessors={splitSeriesAccessors} data={data} /> diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx index 6f7521c3c1d60..ea44819dae5ea 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.test.tsx @@ -57,7 +57,7 @@ import { USER, } from '@kbn/lists-plugin/common/constants.mock'; import { of } from 'rxjs'; -import { timelineDefaults } from '../../../timelines/store/timeline/defaults'; +import { timelineDefaults } from '../../../timelines/store/defaults'; jest.mock('../../../timelines/containers/api', () => ({ getTimelineTemplate: jest.fn(), diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx index 364b521c1ec30..a9a84eb0231a2 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/actions.tsx @@ -58,7 +58,7 @@ import type { TimelineEventsDetailsStrategyResponse, } from '../../../../common/search_strategy/timeline'; import { TimelineEventsQueries } from '../../../../common/search_strategy/timeline'; -import { timelineDefaults } from '../../../timelines/store/timeline/defaults'; +import { timelineDefaults } from '../../../timelines/store/defaults'; import { omitTypenameInTimeline, formatTimelineResultToModel, diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/investigate_in_resolver.test.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/investigate_in_resolver.test.tsx index 6e5c5472820a6..cb1fa0b4ef86e 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/investigate_in_resolver.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/investigate_in_resolver.test.tsx @@ -6,26 +6,40 @@ */ import type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs'; -import { isInvestigateInResolverActionEnabled } from './investigate_in_resolver'; +import { useIsInvestigateInResolverActionEnabled } from './investigate_in_resolver'; +import { renderHook } from '@testing-library/react-hooks'; +import { TestProviders } from '../../../../common/mock'; describe('InvestigateInResolverAction', () => { - describe('isInvestigateInResolverActionEnabled', () => { + describe('useIsInvestigateInResolverActionEnabled', () => { it('returns false if agent.type does not equal endpoint', () => { const data: Ecs = { _id: '1', agent: { type: ['blah'] } }; - expect(isInvestigateInResolverActionEnabled(data)).toBeFalsy(); + const { result } = renderHook(() => useIsInvestigateInResolverActionEnabled(data), { + wrapper: TestProviders, + }); + + expect(result.current).toBeFalsy(); }); it('returns false if agent.type does not have endpoint in first array index', () => { const data: Ecs = { _id: '1', agent: { type: ['blah', 'endpoint'] } }; - expect(isInvestigateInResolverActionEnabled(data)).toBeFalsy(); + const { result } = renderHook(() => useIsInvestigateInResolverActionEnabled(data), { + wrapper: TestProviders, + }); + + expect(result.current).toBeFalsy(); }); it('returns false if process.entity_id is not defined', () => { const data: Ecs = { _id: '1', agent: { type: ['endpoint'] } }; - expect(isInvestigateInResolverActionEnabled(data)).toBeFalsy(); + const { result } = renderHook(() => useIsInvestigateInResolverActionEnabled(data), { + wrapper: TestProviders, + }); + + expect(result.current).toBeFalsy(); }); it('returns true if agent.type has endpoint in first array index', () => { @@ -35,7 +49,11 @@ describe('InvestigateInResolverAction', () => { process: { entity_id: ['5'] }, }; - expect(isInvestigateInResolverActionEnabled(data)).toBeTruthy(); + const { result } = renderHook(() => useIsInvestigateInResolverActionEnabled(data), { + wrapper: TestProviders, + }); + + expect(result.current).toBeTruthy(); }); it('returns false if multiple entity_ids', () => { @@ -45,7 +63,11 @@ describe('InvestigateInResolverAction', () => { process: { entity_id: ['5', '10'] }, }; - expect(isInvestigateInResolverActionEnabled(data)).toBeFalsy(); + const { result } = renderHook(() => useIsInvestigateInResolverActionEnabled(data), { + wrapper: TestProviders, + }); + + expect(result.current).toBeFalsy(); }); it('returns false if entity_id is an empty string', () => { @@ -55,7 +77,11 @@ describe('InvestigateInResolverAction', () => { process: { entity_id: [''] }, }; - expect(isInvestigateInResolverActionEnabled(data)).toBeFalsy(); + const { result } = renderHook(() => useIsInvestigateInResolverActionEnabled(data), { + wrapper: TestProviders, + }); + + expect(result.current).toBeFalsy(); }); it('returns true for process event from sysmon via filebeat', () => { @@ -66,7 +92,11 @@ describe('InvestigateInResolverAction', () => { process: { entity_id: ['always_unique'] }, }; - expect(isInvestigateInResolverActionEnabled(data)).toBeTruthy(); + const { result } = renderHook(() => useIsInvestigateInResolverActionEnabled(data), { + wrapper: TestProviders, + }); + + expect(result.current).toBeTruthy(); }); it('returns false for process event from filebeat but not from sysmon', () => { @@ -77,7 +107,11 @@ describe('InvestigateInResolverAction', () => { process: { entity_id: ['always_unique'] }, }; - expect(isInvestigateInResolverActionEnabled(data)).toBeFalsy(); + const { result } = renderHook(() => useIsInvestigateInResolverActionEnabled(data), { + wrapper: TestProviders, + }); + + expect(result.current).toBeFalsy(); }); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/investigate_in_resolver.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/investigate_in_resolver.tsx index f6b328a63b2c5..91e30fb9d10b5 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/investigate_in_resolver.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/investigate_in_resolver.tsx @@ -5,23 +5,36 @@ * 2.0. */ +import { useMemo } from 'react'; import { get } from 'lodash/fp'; import type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs'; +import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; -export const isInvestigateInResolverActionEnabled = (ecsData?: Ecs) => { - const agentType = get(['agent', 'type', 0], ecsData); - const processEntityIds = get(['process', 'entity_id'], ecsData); - const firstProcessEntityId = get(['process', 'entity_id', 0], ecsData); - const eventModule = get(['event', 'module', 0], ecsData); - const eventDataStream = get(['event', 'dataset'], ecsData); - const datasetIncludesSysmon = - Array.isArray(eventDataStream) && - eventDataStream.some((datastream) => datastream.includes('windows.sysmon')); - const agentTypeIsEndpoint = agentType === 'endpoint'; - const agentTypeIsWinlogBeat = agentType === 'winlogbeat' && eventModule === 'sysmon'; - const isEndpointOrSysmonFromWinlogBeat = - agentTypeIsEndpoint || agentTypeIsWinlogBeat || datasetIncludesSysmon; - const hasProcessEntityId = - processEntityIds != null && processEntityIds.length === 1 && firstProcessEntityId !== ''; - return isEndpointOrSysmonFromWinlogBeat && hasProcessEntityId; +export const useIsInvestigateInResolverActionEnabled = (ecsData?: Ecs) => { + const sentinelOneDataInAnalyzerEnabled = useIsExperimentalFeatureEnabled( + 'sentinelOneDataInAnalyzerEnabled' + ); + return useMemo(() => { + const fileBeatModules = [ + ...(sentinelOneDataInAnalyzerEnabled ? ['sentinel_one_cloud_funnel', 'sentinel_one'] : []), + ] as const; + + const agentType = get(['agent', 'type', 0], ecsData); + const processEntityIds = get(['process', 'entity_id'], ecsData); + const firstProcessEntityId = get(['process', 'entity_id', 0], ecsData); + const eventModule = get(['event', 'module', 0], ecsData); + const eventDataStream = get(['event', 'dataset'], ecsData); + const datasetIncludesSysmon = + Array.isArray(eventDataStream) && + eventDataStream.some((datastream) => datastream.includes('windows.sysmon')); + const agentTypeIsEndpoint = agentType === 'endpoint'; + const agentTypeIsWinlogBeat = agentType === 'winlogbeat' && eventModule === 'sysmon'; + const agentTypeIsFilebeat = agentType === 'filebeat' && fileBeatModules.includes(eventModule); + const isAcceptedAgentType = + agentTypeIsEndpoint || agentTypeIsWinlogBeat || datasetIncludesSysmon || agentTypeIsFilebeat; + const hasProcessEntityId = + processEntityIds != null && processEntityIds.length === 1 && firstProcessEntityId !== ''; + + return isAcceptedAgentType && hasProcessEntityId; + }, [ecsData, sentinelOneDataInAnalyzerEnabled]); }; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_bulk_to_timeline.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_bulk_to_timeline.tsx index 304049fff9a72..e1841709c17ba 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_bulk_to_timeline.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_add_bulk_to_timeline.tsx @@ -22,7 +22,7 @@ import { useTimelineEventsHandler } from '../../../../timelines/containers'; import { eventsViewerSelector } from '../../../../common/components/events_viewer/selectors'; import type { State } from '../../../../common/store/types'; import { dispatchUpdateTimeline } from '../../../../timelines/components/open_timeline/helpers'; -import { timelineActions } from '../../../../timelines/store/timeline'; +import { timelineActions } from '../../../../timelines/store'; import { useCreateTimeline } from '../../../../timelines/components/timeline/properties/use_create_timeline'; import { INVESTIGATE_BULK_IN_TIMELINE } from '../translations'; import { TimelineId } from '../../../../../common/types/timeline'; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.tsx b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.tsx index 02d149860c1b4..8adcf707c98dd 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/timeline_actions/use_investigate_in_timeline.tsx @@ -19,11 +19,11 @@ import { useApi } from '@kbn/securitysolution-list-hooks'; import type { Filter } from '@kbn/es-query'; import type { EcsSecurityExtension as Ecs } from '@kbn/securitysolution-ecs'; import { createHistoryEntry } from '../../../../common/utils/global_query_string/helpers'; -import { timelineDefaults } from '../../../../timelines/store/timeline/defaults'; +import { timelineDefaults } from '../../../../timelines/store/defaults'; import { useKibana } from '../../../../common/lib/kibana'; import { TimelineId } from '../../../../../common/types/timeline'; import { TimelineType } from '../../../../../common/api/timeline'; -import { timelineActions, timelineSelectors } from '../../../../timelines/store/timeline'; +import { timelineActions, timelineSelectors } from '../../../../timelines/store'; import { sendAlertToTimelineAction } from '../actions'; import { dispatchUpdateTimeline } from '../../../../timelines/components/open_timeline/helpers'; import { useCreateTimeline } from '../../../../timelines/components/timeline/properties/use_create_timeline'; diff --git a/x-pack/plugins/security_solution/public/detections/components/alerts_table/types.ts b/x-pack/plugins/security_solution/public/detections/components/alerts_table/types.ts index b226ad81b9771..55616b5aefc29 100644 --- a/x-pack/plugins/security_solution/public/detections/components/alerts_table/types.ts +++ b/x-pack/plugins/security_solution/public/detections/components/alerts_table/types.ts @@ -12,7 +12,7 @@ import type { EuiContextMenuPanelItemDescriptorEntry } from '@elastic/eui/src/co import type { Status } from '../../../../common/api/detection_engine'; import type { Note } from '../../../../common/api/timeline'; import type { DataProvider } from '../../../timelines/components/timeline/data_providers/data_provider'; -import type { TimelineModel } from '../../../timelines/store/timeline/model'; +import type { TimelineModel } from '../../../timelines/store/model'; import type { inputsModel } from '../../../common/store'; export interface SetEventsLoadingProps { diff --git a/x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/use_missing_privileges.ts b/x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/use_missing_privileges.ts index cc5376c29d892..a25174a4f37a6 100644 --- a/x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/use_missing_privileges.ts +++ b/x-pack/plugins/security_solution/public/detections/components/callouts/missing_privileges_callout/use_missing_privileges.ts @@ -11,7 +11,7 @@ import type { Privilege } from '../../../containers/detection_engine/alerts/type import { useUserData } from '../../user_info'; import { useUserPrivileges } from '../../../../common/components/user_privileges'; -const REQUIRED_INDEX_PRIVILIGES = ['read', 'write', 'view_index_metadata', 'maintenance'] as const; +const REQUIRED_INDEX_PRIVILEGES = ['read', 'write', 'view_index_metadata', 'manage'] as const; const getIndexName = (indexPrivileges: Privilege['index']) => { const [indexName] = Object.keys(indexPrivileges); @@ -24,7 +24,7 @@ const getMissingIndexPrivileges = ( ): MissingIndexPrivileges | undefined => { const indexName = getIndexName(indexPrivileges); const privileges = indexPrivileges[indexName]; - const missingPrivileges = REQUIRED_INDEX_PRIVILIGES.filter((privelege) => !privileges[privelege]); + const missingPrivileges = REQUIRED_INDEX_PRIVILEGES.filter((privilege) => !privileges[privilege]); if (missingPrivileges.length) { return [indexName, missingPrivileges]; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/index.test.tsx index 5ee4bb4bb1f47..113f81631a06a 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/index.test.tsx @@ -14,6 +14,7 @@ import { buildListItems, getDescriptionItem, } from '.'; +import type { Type } from '@kbn/securitysolution-io-ts-alerting-types'; import { FilterManager, UI_SETTINGS } from '@kbn/data-plugin/public'; import type { Filter } from '@kbn/es-query'; @@ -534,5 +535,207 @@ describe('description_step', () => { expect(React.isValidElement(result[0].description)).toBeTruthy(); }); }); + + describe('alert suppression', () => { + const ruleTypesWithoutSuppression: Type[] = [ + 'eql', + 'esql', + 'machine_learning', + 'new_terms', + 'threat_match', + ]; + const suppressionFields = { + groupByDuration: { + unit: 'm', + value: 50, + }, + groupByRadioSelection: 'per-time-period', + enableThresholdSuppression: true, + groupByFields: ['agent.name'], + suppressionMissingFields: 'suppress', + }; + describe('groupByDuration', () => { + ruleTypesWithoutSuppression.forEach((ruleType) => { + test(`should be empty if rule is ${ruleType}`, () => { + const result: ListItems[] = getDescriptionItem( + 'groupByDuration', + 'label', + { + ruleType, + ...suppressionFields, + }, + mockFilterManager, + mockLicenseService + ); + + expect(result).toEqual([]); + }); + }); + + ['query', 'saved_query'].forEach((ruleType) => { + test(`should be empty if groupByFields empty for ${ruleType} rule`, () => { + const result: ListItems[] = getDescriptionItem( + 'groupByDuration', + 'label', + { + ruleType: 'query', + ...suppressionFields, + groupByFields: [], + }, + mockFilterManager, + mockLicenseService + ); + + expect(result).toEqual([]); + }); + + test(`should return item for ${ruleType} rule`, () => { + const result: ListItems[] = getDescriptionItem( + 'groupByDuration', + 'label', + { + ruleType: 'query', + ...suppressionFields, + }, + mockFilterManager, + mockLicenseService + ); + + expect(result[0].description).toBe('50m'); + }); + }); + + test('should return item for threshold rule', () => { + const result: ListItems[] = getDescriptionItem( + 'groupByDuration', + 'label', + { + ruleType: 'threshold', + ...suppressionFields, + }, + mockFilterManager, + mockLicenseService + ); + + expect(result[0].description).toBe('50m'); + }); + + test('should return item for threshold rule if groupByFields empty', () => { + const result: ListItems[] = getDescriptionItem( + 'groupByDuration', + 'label', + { + ruleType: 'threshold', + ...suppressionFields, + groupByFields: [], + }, + mockFilterManager, + mockLicenseService + ); + + expect(result[0].description).toBe('50m'); + }); + + test('should be empty for threshold rule if suppression not enabled', () => { + const result: ListItems[] = getDescriptionItem( + 'groupByDuration', + 'label', + { + ruleType: 'threshold', + ...suppressionFields, + enableThresholdSuppression: false, + }, + mockFilterManager, + mockLicenseService + ); + + expect(result).toEqual([]); + }); + }); + + describe('groupByFields', () => { + [...ruleTypesWithoutSuppression, 'threshold'].forEach((ruleType) => { + test(`should be empty if rule is ${ruleType}`, () => { + const result: ListItems[] = getDescriptionItem( + 'groupByFields', + 'label', + { + ruleType, + ...suppressionFields, + }, + mockFilterManager, + mockLicenseService + ); + + expect(result).toEqual([]); + }); + }); + ['query', 'saved_query'].forEach((ruleType) => { + test(`should return item for ${ruleType} rule`, () => { + const result: ListItems[] = getDescriptionItem( + 'groupByFields', + 'label', + { + ruleType, + ...suppressionFields, + }, + mockFilterManager, + mockLicenseService + ); + expect(mount(result[0].description as React.ReactElement).text()).toBe('agent.name'); + }); + }); + }); + + describe('suppressionMissingFields', () => { + [...ruleTypesWithoutSuppression, 'threshold'].forEach((ruleType) => { + test(`should be empty if rule is ${ruleType}`, () => { + const result: ListItems[] = getDescriptionItem( + 'suppressionMissingFields', + 'label', + { + ruleType, + ...suppressionFields, + }, + mockFilterManager, + mockLicenseService + ); + + expect(result).toEqual([]); + }); + }); + ['query', 'saved_query'].forEach((ruleType) => { + test(`should return item for ${ruleType} rule`, () => { + const result: ListItems[] = getDescriptionItem( + 'suppressionMissingFields', + 'label', + { + ruleType, + ...suppressionFields, + }, + mockFilterManager, + mockLicenseService + ); + expect(result[0].description).toContain('Suppress'); + }); + + test(`should be empty if groupByFields empty for ${ruleType} rule`, () => { + const result: ListItems[] = getDescriptionItem( + 'suppressionMissingFields', + 'label', + { + ruleType: 'query', + ...suppressionFields, + groupByFields: [], + }, + mockFilterManager, + mockLicenseService + ); + + expect(result).toEqual([]); + }); + }); + }); + }); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/index.tsx index 7dc9ca3bd41f4..883c01430e644 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/description_step/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/description_step/index.tsx @@ -56,6 +56,7 @@ import { THREAT_QUERY_LABEL } from './translations'; import { filterEmptyThreats } from '../../../../detection_engine/rule_creation_ui/pages/rule_creation/helpers'; import { useLicense } from '../../../../common/hooks/use_license'; import type { LicenseService } from '../../../../../common/license'; +import { isThresholdRule, isQueryRule } from '../../../../../common/detection_engine/utils'; const DescriptionListContainer = styled(EuiDescriptionList)` max-width: 600px; @@ -204,12 +205,29 @@ export const getDescriptionItem = ( } else if (field === 'responseActions') { return []; } else if (field === 'groupByFields') { + const ruleType: Type = get('ruleType', data); + const ruleCanHaveGroupByFields = isQueryRule(ruleType); + if (!ruleCanHaveGroupByFields) { + return []; + } const values: string[] = get(field, data); return buildAlertSuppressionDescription(label, values); } else if (field === 'groupByRadioSelection') { return []; } else if (field === 'groupByDuration') { - if (get('groupByFields', data).length > 0) { + const ruleType: Type = get('ruleType', data); + const ruleCanHaveDuration = isQueryRule(ruleType) || isThresholdRule(ruleType); + if (!ruleCanHaveDuration) { + return []; + } + + // threshold rule has suppression duration without grouping fields, but suppression should be explicitly enabled by user + // query rule have suppression duration only if group by fields selected + const showDuration = isThresholdRule(ruleType) + ? get('enableThresholdSuppression', data) === true + : get('groupByFields', data).length > 0; + + if (showDuration) { const value: Duration = get(field, data); return buildAlertSuppressionWindowDescription( label, @@ -220,6 +238,11 @@ export const getDescriptionItem = ( return []; } } else if (field === 'suppressionMissingFields') { + const ruleType: Type = get('ruleType', data); + const ruleCanHaveSuppressionMissingFields = isQueryRule(ruleType); + if (!ruleCanHaveSuppressionMissingFields) { + return []; + } if (get('groupByFields', data).length > 0) { const value = get(field, data); return buildAlertSuppressionMissingFieldsDescription(label, value); diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/query_bar/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/query_bar/index.tsx index c68194187e4b7..0a293955d0f78 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/query_bar/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/query_bar/index.tsx @@ -18,7 +18,7 @@ import { OpenTimelineModal } from '../../../../timelines/components/open_timelin import type { ActionTimelineToShow } from '../../../../timelines/components/open_timeline/types'; import { QueryBar } from '../../../../common/components/query_bar'; import { useKibana } from '../../../../common/lib/kibana'; -import type { TimelineModel } from '../../../../timelines/store/timeline/model'; +import type { TimelineModel } from '../../../../timelines/store/model'; import { useSavedQueryServices } from '../../../../common/utils/saved_query_services'; import type { FieldHook } from '../../../../shared_imports'; import { getFieldValidityAndErrorMessage } from '../../../../shared_imports'; diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/risk_score_mapping/index.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/risk_score_mapping/index.tsx index f022560f9f50c..fc55b8b60df5e 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/risk_score_mapping/index.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/risk_score_mapping/index.tsx @@ -51,6 +51,7 @@ interface RiskScoreFieldProps { idAria: string; indices: DataViewBase; isDisabled: boolean; + isActive: boolean; placeholder?: string; } @@ -60,6 +61,7 @@ export const RiskScoreField = ({ idAria, indices, isDisabled, + isActive, placeholder, }: RiskScoreFieldProps) => { const { value, isMappingChecked, mapping } = field.value; @@ -147,29 +149,39 @@ export const RiskScoreField = ({ return ( - - - + { + // TODO: https://github.com/elastic/kibana/issues/161456 + // The About step page contains EuiRange component which does not work properly within memoized parents. + // EUI team suggested not to memoize EuiRange/EuiDualRange: https://github.com/elastic/eui/issues/6846 + // Workaround: We force EuiRange re-rendering by removing/adding it into the DOM. + // NOTE: We should remove this workaround once EUI team fixed EuiRange. + // Related ticket: https://github.com/elastic/kibana/issues/160561 + } + {isActive && ( + + + + )} = ({ dataTestSubj: 'detectionEngineStepAboutRuleRiskScore', idAria: 'detectionEngineStepAboutRuleRiskScore', isDisabled: isLoading || indexPatternLoading, + isActive, indices: indexPattern, }} /> diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule_details/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule_details/index.test.tsx index 8ec9c52e0b773..e1a6440edd0d0 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule_details/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_about_rule_details/index.test.tsx @@ -148,11 +148,7 @@ describe('StepAboutRuleToggleDetails', () => { expect(wrapper.find('[idSelected="details"]').exists()).toBeTruthy(); expect(wrapper.find('[idSelected="notes"]').exists()).toBeFalsy(); - wrapper - .find('[title="Investigation guide"]') - .at(0) - .find('input') - .simulate('change', { target: { value: 'notes' } }); + wrapper.find('button[title="Investigation guide"]').simulate('click'); expect(wrapper.find('[idSelected="details"]').exists()).toBeFalsy(); expect(wrapper.find('[idSelected="notes"]').exists()).toBeTruthy(); @@ -174,11 +170,7 @@ describe('StepAboutRuleToggleDetails', () => { ); - wrapper - .find('[title="Investigation guide"]') - .at(0) - .find('input') - .simulate('change', { target: { value: 'notes' } }); + wrapper.find('button[title="Investigation guide"]').simulate('click'); expect(wrapper.find('EuiButtonGroup[idSelected="notes"]').exists()).toBeTruthy(); expect(wrapper.find('div.euiMarkdownFormat').text()).toEqual( @@ -254,11 +246,7 @@ describe('StepAboutRuleToggleDetails', () => { expect(wrapper.find('[idSelected="notes"]').exists()).toBeFalsy(); expect(wrapper.find('[idSelected="setup"]').exists()).toBeFalsy(); - wrapper - .find('[title="Setup guide"]') - .at(0) - .find('input') - .simulate('change', { target: { value: 'setup' } }); + wrapper.find('button[title="Setup guide"]').simulate('click'); expect(wrapper.find('[idSelected="details"]').exists()).toBeFalsy(); expect(wrapper.find('[idSelected="notes"]').exists()).toBeFalsy(); @@ -281,11 +269,7 @@ describe('StepAboutRuleToggleDetails', () => { ); - wrapper - .find('[title="Setup guide"]') - .at(0) - .find('input') - .simulate('change', { target: { value: 'setup' } }); + wrapper.find('button[title="Setup guide"]').simulate('click'); expect(wrapper.find('EuiButtonGroup[idSelected="setup"]').exists()).toBeTruthy(); expect(wrapper.find('div.euiMarkdownFormat').text()).toEqual( diff --git a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.test.tsx b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.test.tsx index b38f10994eb13..c8707897d18a8 100644 --- a/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/components/rules/step_define_rule/index.test.tsx @@ -393,16 +393,15 @@ describe('StepDefineRule', () => { ); expect(getByTestId(`eqlQueryBarTextInput`).textContent).toEqual(eqlQuery.queryBar.query.query); fireEvent.click(getByTestId(`eql-settings-trigger`)); - expect( - within(getByTestId(`eql-event-category-field`)).queryByText( - eqlQuery.eqlOptions.eventCategoryField - ) - ).toBeInTheDocument(); - expect( - within(getByTestId(`eql-tiebreaker-field`)).queryByText(eqlQuery.eqlOptions.tiebreakerField) - ).toBeInTheDocument(); - expect( - within(getByTestId(`eql-timestamp-field`)).queryByText(eqlQuery.eqlOptions.timestampField) - ).toBeInTheDocument(); + + expect(within(getByTestId(`eql-event-category-field`)).queryByRole('combobox')).toHaveValue( + eqlQuery.eqlOptions.eventCategoryField + ); + expect(within(getByTestId(`eql-tiebreaker-field`)).queryByRole('combobox')).toHaveValue( + eqlQuery.eqlOptions.tiebreakerField + ); + expect(within(getByTestId(`eql-timestamp-field`)).queryByRole('combobox')).toHaveValue( + eqlQuery.eqlOptions.timestampField + ); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/lists/use_lists_config.mock.ts b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/lists/use_lists_config.mock.ts index 94d506b1e7af7..2b8c2f5df996f 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/lists/use_lists_config.mock.ts +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/lists/use_lists_config.mock.ts @@ -13,4 +13,6 @@ export const getUseListsConfigMock: () => jest.Mocked = () enabled: true, loading: false, needsConfiguration: false, + needsIndex: false, + canCreateIndex: false, }); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/lists/use_lists_config.test.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/lists/use_lists_config.test.tsx index 81eaede2a821e..b8439e2f9a861 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/lists/use_lists_config.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/lists/use_lists_config.test.tsx @@ -13,10 +13,14 @@ import { useListsPrivileges } from './use_lists_privileges'; import { getUseListsIndexMock } from './use_lists_index.mock'; import { getUseListsPrivilegesMock } from './use_lists_privileges.mock'; import { useListsConfig } from './use_lists_config'; +import { useUserPrivileges } from '../../../../common/components/user_privileges'; jest.mock('../../../../common/lib/kibana'); jest.mock('./use_lists_index'); jest.mock('./use_lists_privileges'); +jest.mock('../../../../common/components/user_privileges'); + +const useUserPrivilegesMock = useUserPrivileges as jest.Mock; describe('useListsConfig', () => { let listsIndexMock: ReturnType; @@ -27,16 +31,19 @@ describe('useListsConfig', () => { listsPrivilegesMock = getUseListsPrivilegesMock(); (useListsIndex as jest.Mock).mockReturnValue(listsIndexMock); (useListsPrivileges as jest.Mock).mockReturnValue(listsPrivilegesMock); + useUserPrivilegesMock.mockReturnValue({ + detectionEnginePrivileges: { result: { cluster: { manage: false } } }, + }); }); it("returns the user's write permissions", () => { listsPrivilegesMock.canWriteIndex = false; const { result } = renderHook(() => useListsConfig()); - expect(result.current.canWriteIndex).toEqual(false); + expect(result.current.canWriteIndex).toBe(false); listsPrivilegesMock.canWriteIndex = true; const { result: result2 } = renderHook(() => useListsConfig()); - expect(result2.current.canWriteIndex).toEqual(true); + expect(result2.current.canWriteIndex).toBe(true); }); describe('when lists are disabled', () => { @@ -46,8 +53,8 @@ describe('useListsConfig', () => { it('indicates that lists are not enabled, and need configuration', () => { const { result } = renderHook(() => useListsConfig()); - expect(result.current.enabled).toEqual(false); - expect(result.current.needsConfiguration).toEqual(true); + expect(result.current.enabled).toBe(false); + expect(result.current.needsConfiguration).toBe(true); }); }); @@ -61,16 +68,26 @@ describe('useListsConfig', () => { listsPrivilegesMock.canManageIndex = false; const { result } = renderHook(() => useListsConfig()); - expect(result.current.needsConfiguration).toEqual(true); + expect(result.current.needsConfiguration).toBe(true); expect(listsIndexMock.createIndex).not.toHaveBeenCalled(); }); - it('attempts to create the indexes if the user can manage indexes', () => { + it('attempts to create the indexes if the user can manage indexes and have cluster privilege', () => { + useUserPrivilegesMock.mockReturnValue({ + detectionEnginePrivileges: { result: { cluster: { manage: true } } }, + }); listsPrivilegesMock.canManageIndex = true; renderHook(() => useListsConfig()); expect(listsIndexMock.createIndex).toHaveBeenCalled(); }); + + it('does not call create index if the user can manage indexes but not cluster privilege', () => { + listsPrivilegesMock.canManageIndex = true; + + renderHook(() => useListsConfig()); + expect(listsIndexMock.createIndex).not.toHaveBeenCalled(); + }); }); describe('when lists are enabled and indexes exist', () => { @@ -81,7 +98,39 @@ describe('useListsConfig', () => { it('does not need configuration', () => { const { result } = renderHook(() => useListsConfig()); - expect(result.current.needsConfiguration).toEqual(false); + expect(result.current.needsConfiguration).toBe(false); + }); + }); + + describe('create index privileges', () => { + it('canCreateIndex is true, when user can manage indices and cluster', () => { + useUserPrivilegesMock.mockReturnValue({ + detectionEnginePrivileges: { result: { cluster: { manage: true } } }, + }); + listsPrivilegesMock.canManageIndex = true; + + const { result } = renderHook(() => useListsConfig()); + expect(result.current.canCreateIndex).toBe(true); + }); + + it('canCreateIndex is false, when user can manage indices and can not manage cluster', () => { + useUserPrivilegesMock.mockReturnValue({ + detectionEnginePrivileges: { result: { cluster: { manage: false } } }, + }); + listsPrivilegesMock.canManageIndex = true; + + const { result } = renderHook(() => useListsConfig()); + expect(result.current.canCreateIndex).toBe(false); + }); + + it('canCreateIndex is false, when user can not manage indices and can manage cluster', () => { + useUserPrivilegesMock.mockReturnValue({ + detectionEnginePrivileges: { result: { cluster: { manage: true } } }, + }); + listsPrivilegesMock.canManageIndex = false; + + const { result } = renderHook(() => useListsConfig()); + expect(result.current.canCreateIndex).toBe(false); }); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/lists/use_lists_config.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/lists/use_lists_config.tsx index e07367367d153..f8db3528ba615 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/lists/use_lists_config.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/lists/use_lists_config.tsx @@ -10,6 +10,7 @@ import { useEffect } from 'react'; import { useKibana } from '../../../../common/lib/kibana'; import { useListsIndex } from './use_lists_index'; import { useListsPrivileges } from './use_lists_privileges'; +import { useUserPrivileges } from '../../../../common/components/user_privileges'; export interface UseListsConfigReturn { canManageIndex: boolean | null; @@ -17,13 +18,18 @@ export interface UseListsConfigReturn { enabled: boolean; loading: boolean; needsConfiguration: boolean; + needsIndex: boolean; + canCreateIndex: boolean | null; } export const useListsConfig = (): UseListsConfigReturn => { const { createIndex, indexExists, loading: indexLoading, error: indexError } = useListsIndex(); const { canManageIndex, canWriteIndex, loading: privilegesLoading } = useListsPrivileges(); + const { detectionEnginePrivileges } = useUserPrivileges(); + const { lists } = useKibana().services; + const canManageCluster = detectionEnginePrivileges.result?.cluster.manage ?? null; const enabled = lists != null; const loading = indexLoading || privilegesLoading; const needsIndex = indexExists === false; @@ -31,12 +37,23 @@ export const useListsConfig = (): UseListsConfigReturn => { const needsIndexConfiguration = needsIndex && (canManageIndex === false || (canManageIndex === true && hasIndexError)); const needsConfiguration = !enabled || needsIndexConfiguration; + // Index can be created only when manage cluster privilege assigned to user role. + // It's needed to create index templates + const canCreateIndex = canManageIndex && canManageCluster; useEffect(() => { - if (needsIndex && canManageIndex) { + if (needsIndex && canCreateIndex) { createIndex(); } - }, [canManageIndex, createIndex, needsIndex]); + }, [createIndex, needsIndex, canCreateIndex]); - return { canManageIndex, canWriteIndex, enabled, loading, needsConfiguration }; + return { + canManageIndex, + canWriteIndex, + enabled, + loading, + needsConfiguration, + needsIndex, + canCreateIndex, + }; }; diff --git a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_from_timeline.tsx b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_from_timeline.tsx index 4818a8c8f1acd..c5b2036efd701 100644 --- a/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_from_timeline.tsx +++ b/x-pack/plugins/security_solution/public/detections/containers/detection_engine/rules/use_rule_from_timeline.tsx @@ -11,7 +11,7 @@ import { useDispatch } from 'react-redux'; import { i18n } from '@kbn/i18n'; import type { EqlOptionsSelected } from '@kbn/timelines-plugin/common'; import { convertKueryToElasticSearchQuery } from '../../../../common/lib/kuery'; -import { updateIsLoading } from '../../../../timelines/store/timeline/actions'; +import { updateIsLoading } from '../../../../timelines/store/actions'; import { useAppToasts } from '../../../../common/hooks/use_app_toasts'; import { useSourcererDataView } from '../../../../common/containers/sourcerer'; import type { TimelineModel } from '../../../..'; diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/index.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/index.test.tsx index d5e956da14c0e..4d7798a4fcbe6 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/chart_select/index.test.tsx @@ -62,23 +62,33 @@ describe('ChartSelect', () => { ); expect(screen.getByTestId('chart-select-tabs')).toBeInTheDocument(); - expect(screen.getByTestId('trend')).toBeChecked(); + expect(screen.getByTitle('Trend')).toHaveAttribute('aria-pressed', 'true'); }); - test('changing selection render correctly when alertsPageChartsEnabled is true', async () => { + test('changing selection render correctly when alertsPageChartsEnabled is true', () => { mockUseIsExperimentalFeatureEnabled.mockReturnValue(true); const setAlertViewSelection = jest.fn(); - const { container } = render( + const { rerender } = render( ); - const button = container.querySelector('input[value="treemap"]'); - if (button) { - fireEvent.change(button, { target: { checked: true, type: 'radio' } }); - } - expect(screen.getByTestId('treemap')).toBeChecked(); - expect(screen.getByTestId('trend')).not.toBeChecked(); + expect(screen.getByTitle('Trend')).toHaveAttribute('aria-pressed', 'true'); + + const treemapButton = screen.getByTitle('Treemap'); + expect(treemapButton).toHaveAttribute('aria-pressed', 'false'); + + fireEvent.click(treemapButton); + expect(setAlertViewSelection).toHaveBeenCalledWith('treemap'); + + rerender( + + + + ); + + expect(screen.getByTitle('Treemap')).toHaveAttribute('aria-pressed', 'true'); + expect(screen.getByTitle('Trend')).toHaveAttribute('aria-pressed', 'false'); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.test.tsx b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.test.tsx index 07f07df47d888..feb8b0ead2be4 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.test.tsx +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/chart_panels/index.test.tsx @@ -245,22 +245,21 @@ describe('ChartPanels', () => { ); - const initialValue = screen.getAllByTestId('comboBoxInput')[0]; // EuiComboBox does NOT render the current selection via it's input; it uses this div - expect(initialValue).toHaveTextContent(defaultValue); + const initialInput = screen.getAllByTestId('comboBoxSearchInput')[0]; + expect(initialInput).toHaveValue(defaultValue); // update the EuiComboBox input to an invalid value: - const searchInput = screen.getAllByTestId('comboBoxSearchInput')[0]; // the actual controlled by EuiComboBox - fireEvent.change(searchInput, { target: { value: invalidValue } }); + fireEvent.change(initialInput, { target: { value: invalidValue } }); - const afterInvalidInput = screen.getAllByTestId('comboBoxInput')[0]; - expect(searchInput).toHaveValue(invalidValue); // the 'Group by' EuiComboBox is now in the "error state" - expect(afterInvalidInput).not.toHaveTextContent(invalidValue); // Value should not have been applied + const afterInvalidInput = screen.getAllByTestId('comboBoxSearchInput')[0]; + expect(afterInvalidInput).toHaveValue(invalidValue); // the 'Group by' EuiComboBox is now in the "error state" + expect(afterInvalidInput).toBeInvalid(); resetGroupByFields(); // invoke the `Reset group by fields` context menu action await waitFor(() => { - const afterReset = screen.getAllByTestId('comboBoxInput')[0]; - expect(afterReset).toHaveTextContent(defaultValue); // back to the default + const afterReset = screen.getAllByTestId('comboBoxSearchInput')[0]; + expect(afterReset).toHaveValue(defaultValue); // back to the default }); }); }); @@ -285,22 +284,21 @@ describe('ChartPanels', () => { ); - const initialValue = screen.getAllByTestId('comboBoxInput')[1]; // EuiComboBox does NOT render the current selection via it's input; it uses this div - expect(initialValue).toHaveTextContent(defaultValue); + const initialInput = screen.getAllByTestId('comboBoxSearchInput')[1]; + expect(initialInput).toHaveValue(defaultValue); // update the EuiComboBox input to an invalid value: - const searchInput = screen.getAllByTestId('comboBoxSearchInput')[1]; // the actual controlled by EuiComboBox - fireEvent.change(searchInput, { target: { value: invalidValue } }); + fireEvent.change(initialInput, { target: { value: invalidValue } }); - const afterInvalidInput = screen.getAllByTestId('comboBoxInput')[1]; - expect(searchInput).toHaveValue(invalidValue); // the 'Group by top' EuiComboBox is now in the "error state" - expect(afterInvalidInput).not.toHaveTextContent(invalidValue); // Value should not have been applied + const afterInvalidInput = screen.getAllByTestId('comboBoxSearchInput')[1]; + expect(afterInvalidInput).toHaveValue(invalidValue); // the 'Group by top' EuiComboBox is now in the "error state" + expect(afterInvalidInput).toBeInvalid(); resetGroupByFields(); // invoke the `Reset group by fields` context menu action await waitFor(() => { - const afterReset = screen.getAllByTestId('comboBoxInput')[1]; - expect(afterReset).toHaveTextContent(defaultValue); // back to the default + const afterReset = screen.getAllByTestId('comboBoxSearchInput')[1]; + expect(afterReset).toHaveValue(defaultValue); // back to the default }); }); }); diff --git a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts index b35c07e86f057..97f782c7dd95c 100644 --- a/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts +++ b/x-pack/plugins/security_solution/public/detections/pages/detection_engine/rules/translations.ts @@ -35,6 +35,14 @@ export const UPLOAD_VALUE_LISTS_TOOLTIP = i18n.translate( } ); +export const UPLOAD_VALUE_LISTS_PRIVILEGES_TOOLTIP = i18n.translate( + 'xpack.securitySolution.lists.detectionEngine.rules.uploadValueListsButtonPrivilegesTooltip', + { + defaultMessage: + 'A user with manage cluster privileges must visit the Rules page before you can import value lists.', + } +); + export const ADD_NEW_RULE = i18n.translate( 'xpack.securitySolution.detectionEngine.rules.addNewRuleTitle', { diff --git a/x-pack/plugins/security_solution/public/entity_analytics/api/api.ts b/x-pack/plugins/security_solution/public/entity_analytics/api/api.ts index b4e3734991467..73cb1cbd57ff0 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/api/api.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/api/api.ts @@ -12,88 +12,102 @@ import { RISK_ENGINE_DISABLE_URL, RISK_ENGINE_INIT_URL, RISK_ENGINE_PRIVILEGES_URL, + ASSET_CRITICALITY_PRIVILEGES_URL, } from '../../../common/constants'; -import { KibanaServices } from '../../common/lib/kibana'; import type { CalculateScoresResponse, EnableRiskEngineResponse, GetRiskEngineStatusResponse, InitRiskEngineResponse, DisableRiskEngineResponse, - RiskEnginePrivilegesResponse, } from '../../../server/lib/entity_analytics/types'; -import type { RiskScorePreviewRequestSchema } from '../../../common/risk_engine/risk_score_preview/request_schema'; +import type { RiskScorePreviewRequestSchema } from '../../../common/entity_analytics/risk_engine/risk_score_preview/request_schema'; +import type { EntityAnalyticsPrivileges } from '../../../common/api/entity_analytics/common'; +import { useKibana } from '../../common/lib/kibana/kibana_react'; -/** - * Fetches preview risks scores - */ -export const fetchRiskScorePreview = async ({ - signal, - params, -}: { - signal?: AbortSignal; - params: RiskScorePreviewRequestSchema; -}): Promise => { - return KibanaServices.get().http.fetch(RISK_SCORE_PREVIEW_URL, { - version: '1', - method: 'POST', - body: JSON.stringify(params), - signal, - }); -}; +export const useEntityAnalyticsRoutes = () => { + const http = useKibana().services.http; -/** - * Fetches risks engine status - */ -export const fetchRiskEngineStatus = async ({ - signal, -}: { - signal?: AbortSignal; -}): Promise => { - return KibanaServices.get().http.fetch(RISK_ENGINE_STATUS_URL, { - version: '1', - method: 'GET', + /** + * Fetches preview risks scores + */ + const fetchRiskScorePreview = ({ signal, - }); -}; + params, + }: { + signal?: AbortSignal; + params: RiskScorePreviewRequestSchema; + }) => + http.fetch(RISK_SCORE_PREVIEW_URL, { + version: '1', + method: 'POST', + body: JSON.stringify(params), + signal, + }); -/** - * Init risk score engine - */ -export const initRiskEngine = async (): Promise => { - return KibanaServices.get().http.fetch(RISK_ENGINE_INIT_URL, { - version: '1', - method: 'POST', - }); -}; + /** + * Fetches risks engine status + */ + const fetchRiskEngineStatus = ({ signal }: { signal?: AbortSignal }) => + http.fetch(RISK_ENGINE_STATUS_URL, { + version: '1', + method: 'GET', + signal, + }); -/** - * Enable risk score engine - */ -export const enableRiskEngine = async (): Promise => { - return KibanaServices.get().http.fetch(RISK_ENGINE_ENABLE_URL, { - version: '1', - method: 'POST', - }); -}; + /** + * Init risk score engine + */ + const initRiskEngine = () => + http.fetch(RISK_ENGINE_INIT_URL, { + version: '1', + method: 'POST', + }); -/** - * Disable risk score engine - */ -export const disableRiskEngine = async (): Promise => { - return KibanaServices.get().http.fetch(RISK_ENGINE_DISABLE_URL, { - version: '1', - method: 'POST', - }); -}; + /** + * Enable risk score engine + */ + const enableRiskEngine = () => + http.fetch(RISK_ENGINE_ENABLE_URL, { + version: '1', + method: 'POST', + }); -/** - * Get risk engine privileges - */ -export const fetchRiskEnginePrivileges = async (): Promise => { - return KibanaServices.get().http.fetch(RISK_ENGINE_PRIVILEGES_URL, { - version: '1', - method: 'GET', - }); + /** + * Disable risk score engine + */ + const disableRiskEngine = () => + http.fetch(RISK_ENGINE_DISABLE_URL, { + version: '1', + method: 'POST', + }); + + /** + * Get risk engine privileges + */ + const fetchRiskEnginePrivileges = () => + http.fetch(RISK_ENGINE_PRIVILEGES_URL, { + version: '1', + method: 'GET', + }); + + /** + * Get asset criticality privileges + */ + const fetchAssetCriticalityPrivileges = () => + http.fetch(ASSET_CRITICALITY_PRIVILEGES_URL, { + version: '1', + method: 'GET', + }); + + return { + fetchRiskScorePreview, + fetchRiskEngineStatus, + initRiskEngine, + enableRiskEngine, + disableRiskEngine, + fetchRiskEnginePrivileges, + fetchAssetCriticalityPrivileges, + }; }; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_asset_criticality_privileges.ts b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_asset_criticality_privileges.ts new file mode 100644 index 0000000000000..afed24ac49e63 --- /dev/null +++ b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_asset_criticality_privileges.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { useQuery } from '@tanstack/react-query'; +import { useEntityAnalyticsRoutes } from '../api'; + +export const useAssetCriticalityPrivileges = () => { + const { fetchAssetCriticalityPrivileges } = useEntityAnalyticsRoutes(); + return useQuery(['GET', 'FETCH_ASSET_CRITICALITY_PRIVILEGES'], fetchAssetCriticalityPrivileges); +}; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_disable_risk_engine_mutation.ts b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_disable_risk_engine_mutation.ts index 8b3ca2d0ac2ad..9f601d191d440 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_disable_risk_engine_mutation.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_disable_risk_engine_mutation.ts @@ -6,7 +6,7 @@ */ import type { UseMutationOptions } from '@tanstack/react-query'; import { useMutation } from '@tanstack/react-query'; -import { disableRiskEngine } from '../api'; +import { useEntityAnalyticsRoutes } from '../api'; import { useInvalidateRiskEngineStatusQuery } from './use_risk_engine_status'; import type { EnableRiskEngineResponse, @@ -17,6 +17,7 @@ export const DISABLE_RISK_ENGINE_MUTATION_KEY = ['POST', 'DISABLE_RISK_ENGINE']; export const useDisableRiskEngineMutation = (options?: UseMutationOptions<{}>) => { const invalidateRiskEngineStatusQuery = useInvalidateRiskEngineStatusQuery(); + const { disableRiskEngine } = useEntityAnalyticsRoutes(); return useMutation( () => disableRiskEngine(), diff --git a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_enable_risk_engine_mutation.ts b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_enable_risk_engine_mutation.ts index c4d5070014fc2..0f1a5995b7656 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_enable_risk_engine_mutation.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_enable_risk_engine_mutation.ts @@ -6,7 +6,7 @@ */ import type { UseMutationOptions } from '@tanstack/react-query'; import { useMutation } from '@tanstack/react-query'; -import { enableRiskEngine } from '../api'; +import { useEntityAnalyticsRoutes } from '../api'; import { useInvalidateRiskEngineStatusQuery } from './use_risk_engine_status'; import type { EnableRiskEngineResponse, @@ -16,9 +16,9 @@ export const ENABLE_RISK_ENGINE_MUTATION_KEY = ['POST', 'ENABLE_RISK_ENGINE']; export const useEnableRiskEngineMutation = (options?: UseMutationOptions<{}>) => { const invalidateRiskEngineStatusQuery = useInvalidateRiskEngineStatusQuery(); - + const { enableRiskEngine } = useEntityAnalyticsRoutes(); return useMutation( - () => enableRiskEngine(), + enableRiskEngine, { ...options, mutationKey: ENABLE_RISK_ENGINE_MUTATION_KEY, diff --git a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_init_risk_engine_mutation.ts b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_init_risk_engine_mutation.ts index 1e2a695652edf..aa8a19c4a7a6a 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_init_risk_engine_mutation.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_init_risk_engine_mutation.ts @@ -6,7 +6,7 @@ */ import type { UseMutationOptions } from '@tanstack/react-query'; import { useMutation } from '@tanstack/react-query'; -import { initRiskEngine } from '../api'; +import { useEntityAnalyticsRoutes } from '../api'; import { useInvalidateRiskEngineStatusQuery } from './use_risk_engine_status'; import type { InitRiskEngineResponse, @@ -17,7 +17,7 @@ export const INIT_RISK_ENGINE_STATUS_KEY = ['POST', 'INIT_RISK_ENGINE']; export const useInitRiskEngineMutation = (options?: UseMutationOptions<{}>) => { const invalidateRiskEngineStatusQuery = useInvalidateRiskEngineStatusQuery(); - + const { initRiskEngine } = useEntityAnalyticsRoutes(); return useMutation(() => initRiskEngine(), { ...options, mutationKey: INIT_RISK_ENGINE_STATUS_KEY, diff --git a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_preview_risk_scores.ts b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_preview_risk_scores.ts index 0d5667d7df813..01e9c6ac8dfea 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_preview_risk_scores.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_preview_risk_scores.ts @@ -6,17 +6,18 @@ */ import { useQuery } from '@tanstack/react-query'; import dateMath from '@kbn/datemath'; -import { fetchRiskScorePreview } from '../api'; -import type { RiskScorePreviewRequestSchema } from '../../../../common/risk_engine/risk_score_preview/request_schema'; +import { useEntityAnalyticsRoutes } from '../api'; +import type { RiskScorePreviewRequestSchema } from '../../../../common/entity_analytics/risk_engine/risk_score_preview/request_schema'; export const useRiskScorePreview = ({ data_view_id: dataViewId, range, filter, }: RiskScorePreviewRequestSchema) => { + const { fetchRiskScorePreview } = useEntityAnalyticsRoutes(); + return useQuery(['POST', 'FETCH_PREVIEW_RISK_SCORE', range, filter], async ({ signal }) => { const params: RiskScorePreviewRequestSchema = { data_view_id: dataViewId }; - if (range) { const startTime = dateMath.parse(range.start)?.utc().toISOString(); const endTime = dateMath diff --git a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_engine_privileges.ts b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_engine_privileges.ts index 2a3ffa40856cb..5278b537048ff 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_engine_privileges.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_engine_privileges.ts @@ -5,8 +5,9 @@ * 2.0. */ import { useQuery } from '@tanstack/react-query'; -import { fetchRiskEnginePrivileges } from '../api'; +import { useEntityAnalyticsRoutes } from '../api'; export const useRiskEnginePrivileges = () => { + const { fetchRiskEnginePrivileges } = useEntityAnalyticsRoutes(); return useQuery(['GET', 'FETCH_RISK_ENGINE_PRIVILEGES'], fetchRiskEnginePrivileges); }; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_engine_status.ts b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_engine_status.ts index 504ed59a40423..07ef32f0a3e41 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_engine_status.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/api/hooks/use_risk_engine_status.ts @@ -6,8 +6,8 @@ */ import { useQuery, useQueryClient } from '@tanstack/react-query'; import { useCallback } from 'react'; -import { fetchRiskEngineStatus } from '../api'; -import { RiskEngineStatus } from '../../../../common/risk_engine/types'; +import { useEntityAnalyticsRoutes } from '../api'; +import { RiskEngineStatus } from '../../../../common/entity_analytics/risk_engine/types'; import { useIsExperimentalFeatureEnabled } from '../../../common/hooks/use_experimental_features'; const FETCH_RISK_ENGINE_STATUS = ['GET', 'FETCH_RISK_ENGINE_STATUS']; @@ -29,7 +29,7 @@ export const useIsNewRiskScoreModuleInstalled = () => { export const useRiskEngineStatus = () => { const isNewRiskScoreModuleAvailable = useIsExperimentalFeatureEnabled('riskScoringRoutesEnabled'); - + const { fetchRiskEngineStatus } = useEntityAnalyticsRoutes(); return useQuery(FETCH_RISK_ENGINE_STATUS, async ({ signal }) => { if (!isNewRiskScoreModuleAvailable) { return { diff --git a/x-pack/plugins/security_solution/public/entity_analytics/common/index.ts b/x-pack/plugins/security_solution/public/entity_analytics/common/index.ts new file mode 100644 index 0000000000000..354e46ff63074 --- /dev/null +++ b/x-pack/plugins/security_solution/public/entity_analytics/common/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { userHasRiskEngineReadPermissions } from './user_has_risk_engine_read_permissions'; +export * from './utils'; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/common/user_has_risk_engine_read_permissions.test.ts b/x-pack/plugins/security_solution/public/entity_analytics/common/user_has_risk_engine_read_permissions.test.ts new file mode 100644 index 0000000000000..6425b68175935 --- /dev/null +++ b/x-pack/plugins/security_solution/public/entity_analytics/common/user_has_risk_engine_read_permissions.test.ts @@ -0,0 +1,57 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { userHasRiskEngineReadPermissions } from './user_has_risk_engine_read_permissions'; + +describe('userHasRiskEngineReadPermissions', () => { + it('returns false if isLoading is true', () => { + expect(userHasRiskEngineReadPermissions({ isLoading: true })).toEqual(false); + }); + it('returns true if hasAllRequiredPrivileges is true', () => { + expect( + userHasRiskEngineReadPermissions({ isLoading: false, hasAllRequiredPrivileges: true }) + ).toEqual(true); + }); + it('returns false if hasAllRequiredPrivileges is false and user is missing read permissions', () => { + expect( + userHasRiskEngineReadPermissions({ + isLoading: false, + hasAllRequiredPrivileges: false, + missingPrivileges: { + clusterPrivileges: [], + indexPrivileges: [['risk-score.risk-score-*', ['read']]], + }, + }) + ).toEqual(false); + }); + + it('returns true if hasAllRequiredPrivileges is false and user is missing read permissions for other index', () => { + expect( + userHasRiskEngineReadPermissions({ + isLoading: false, + hasAllRequiredPrivileges: false, + missingPrivileges: { + clusterPrivileges: [], + indexPrivileges: [['other-index.other-index-*', ['read']]], + }, + }) + ).toEqual(true); + }); + + it('returns true if hasAllRequiredPrivileges is false and user is not missing read permissions', () => { + expect( + userHasRiskEngineReadPermissions({ + isLoading: false, + hasAllRequiredPrivileges: false, + missingPrivileges: { + clusterPrivileges: [], + indexPrivileges: [['risk-score.risk-score-*', ['write']]], + }, + }) + ).toEqual(true); + }); +}); diff --git a/x-pack/plugins/security_solution/public/entity_analytics/common/user_has_risk_engine_read_permissions.ts b/x-pack/plugins/security_solution/public/entity_analytics/common/user_has_risk_engine_read_permissions.ts new file mode 100644 index 0000000000000..dac6f723da1dd --- /dev/null +++ b/x-pack/plugins/security_solution/public/entity_analytics/common/user_has_risk_engine_read_permissions.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { RISK_SCORE_INDEX_PATTERN } from '../../../common/entity_analytics/risk_engine'; +import type { RiskEngineMissingPrivilegesResponse } from '../hooks/use_missing_risk_engine_privileges'; + +export const userHasRiskEngineReadPermissions = ( + privileges: RiskEngineMissingPrivilegesResponse +): boolean => { + if (privileges.isLoading) { + return false; + } + + if (privileges.hasAllRequiredPrivileges) { + return true; + } + + const { indexPrivileges: missingIndexPrivileges } = privileges.missingPrivileges; + + const isMissingReadPrivilege = missingIndexPrivileges.find( + ([indexName, indexPrivileges]) => + indexName === RISK_SCORE_INDEX_PATTERN && indexPrivileges.includes('read') + ); + + return !isMissingReadPrivilege; +}; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_engine_privileges_callout/risk_engine_privileges_callout.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_engine_privileges_callout/risk_engine_privileges_callout.tsx index edb6bc11f7217..b2b4e0def3f54 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_engine_privileges_callout/risk_engine_privileges_callout.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_engine_privileges_callout/risk_engine_privileges_callout.tsx @@ -9,15 +9,14 @@ import React from 'react'; import type { CallOutMessage } from '../../../common/components/callouts'; import { CallOutSwitcher } from '../../../common/components/callouts'; import { MissingPrivilegesCallOutBody, MISSING_PRIVILEGES_CALLOUT_TITLE } from './translations'; -import { useMissingPrivileges } from './use_missing_risk_engine_privileges'; - -export const RiskEnginePrivilegesCallOut = () => { - const privileges = useMissingPrivileges(); +import type { RiskEngineMissingPrivilegesResponse } from '../../hooks/use_missing_risk_engine_privileges'; +export const RiskEnginePrivilegesCallOut: React.FC<{ + privileges: RiskEngineMissingPrivilegesResponse; +}> = ({ privileges }) => { if (privileges.isLoading || privileges.hasAllRequiredPrivileges) { return null; } - const message: CallOutMessage = { type: 'primary', id: `missing-risk-engine-privileges`, diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_engine_privileges_callout/translations.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_engine_privileges_callout/translations.tsx index ed58f50f28279..d7e45c0ff80f3 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_engine_privileges_callout/translations.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_engine_privileges_callout/translations.tsx @@ -9,9 +9,9 @@ import { EuiCode, EuiLink } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import React from 'react'; +import type { MissingPrivileges } from '../../../../common/entity_analytics/risk_engine'; import { useKibana } from '../../../common/lib/kibana'; import { CommaSeparatedValues } from '../../../detections/components/callouts/missing_privileges_callout/comma_separated_values'; -import type { MissingPrivileges } from './use_missing_risk_engine_privileges'; export const MISSING_PRIVILEGES_CALLOUT_TITLE = i18n.translate( 'xpack.securitySolution.riskEngine.missingPrivilegesCallOut.messageTitle', diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_engine_privileges_callout/use_missing_risk_engine_privileges.ts b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_engine_privileges_callout/use_missing_risk_engine_privileges.ts deleted file mode 100644 index adef9e3e4909e..0000000000000 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_engine_privileges_callout/use_missing_risk_engine_privileges.ts +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { useMemo } from 'react'; -import type { RiskEnginePrivilegesResponse } from '../../../../server/lib/entity_analytics/types'; -import { useRiskEnginePrivileges } from '../../api/hooks/use_risk_engine_privileges'; -import { - RISK_ENGINE_REQUIRED_ES_CLUSTER_PRIVILEGES, - RISK_ENGINE_REQUIRED_ES_INDEX_PRIVILEGES, -} from '../../../../common/risk_engine'; - -const getMissingIndexPrivileges = ( - privileges: RiskEnginePrivilegesResponse['privileges']['elasticsearch']['index'] -): MissingIndexPrivileges => { - const missingIndexPrivileges: MissingIndexPrivileges = []; - - for (const [indexName, requiredPrivileges] of Object.entries( - RISK_ENGINE_REQUIRED_ES_INDEX_PRIVILEGES - )) { - const missingPrivileges = requiredPrivileges.filter( - (privilege) => !privileges[indexName][privilege] - ); - - if (missingPrivileges.length) { - missingIndexPrivileges.push([indexName, missingPrivileges]); - } - } - - return missingIndexPrivileges; -}; - -export type MissingClusterPrivileges = string[]; -export type MissingIndexPrivileges = Array<[indexName: string, privileges: string[]]>; - -export interface MissingPrivileges { - clusterPrivileges: MissingClusterPrivileges; - indexPrivileges: MissingIndexPrivileges; -} - -export type MissingPrivilegesResponse = - | { isLoading: true } - | { isLoading: false; hasAllRequiredPrivileges: true } - | { isLoading: false; missingPrivileges: MissingPrivileges; hasAllRequiredPrivileges: false }; - -export const useMissingPrivileges = (): MissingPrivilegesResponse => { - const { data: privilegesResponse, isLoading } = useRiskEnginePrivileges(); - - return useMemo(() => { - if (isLoading || !privilegesResponse) { - return { - isLoading: true, - }; - } - - if (privilegesResponse.has_all_required) { - return { - isLoading: false, - hasAllRequiredPrivileges: true, - }; - } - - const { privileges } = privilegesResponse; - const missinIndexPrivileges = getMissingIndexPrivileges(privileges.elasticsearch.index); - const missingClusterPrivileges = RISK_ENGINE_REQUIRED_ES_CLUSTER_PRIVILEGES.filter( - (privilege) => !privileges.elasticsearch.cluster[privilege] - ); - - return { - isLoading: false, - hasAllRequiredPrivileges: false, - missingPrivileges: { - indexPrivileges: missinIndexPrivileges, - clusterPrivileges: missingClusterPrivileges, - }, - }; - }, [isLoading, privilegesResponse]); -}; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_enable_section.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_enable_section.tsx index b5ccc1b8daa63..fa61a6c15b9e8 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_enable_section.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_enable_section.tsx @@ -28,7 +28,6 @@ import { EuiCallOut, EuiAccordion, } from '@elastic/eui'; -import { FormattedMessage } from '@kbn/i18n-react'; import { LinkAnchor } from '@kbn/security-solution-navigation/links'; import { SecurityPageName } from '@kbn/security-solution-navigation'; import * as i18n from '../translations'; @@ -36,10 +35,11 @@ import { useRiskEngineStatus } from '../api/hooks/use_risk_engine_status'; import { useInitRiskEngineMutation } from '../api/hooks/use_init_risk_engine_mutation'; import { useEnableRiskEngineMutation } from '../api/hooks/use_enable_risk_engine_mutation'; import { useDisableRiskEngineMutation } from '../api/hooks/use_disable_risk_engine_mutation'; -import { RiskEngineStatus, MAX_SPACES_COUNT } from '../../../common/risk_engine'; +import { RiskEngineStatus, MAX_SPACES_COUNT } from '../../../common/entity_analytics/risk_engine'; import { RiskInformationFlyout } from '../../explore/components/risk_score/risk_information'; import { useOnOpenCloseHandler } from '../../helper_hooks'; +import type { RiskEngineMissingPrivilegesResponse } from '../hooks/use_missing_risk_engine_privileges'; const MIN_WIDTH_TO_PREVENT_LABEL_FROM_MOVING = '50px'; @@ -64,47 +64,6 @@ const RiskScoreErrorPanel = ({ errors }: { errors: string[] }) => ( ))} - - -

- {i18n.NEED_TO_HAVE} -

    -
  • - {'all'}, - index: {'risk-score.risk-score-*'}, - }} - /> -
  • -
  • - - {'manage_index_templates'} - {','} {'manage_transform'} - - ), - }} - /> -
  • -
  • - {'Saved Objects Management'}, - }} - /> -
  • -
-

-
); @@ -177,7 +136,59 @@ const RiskScoreUpdateModal = ({ ); }; -export const RiskScoreEnableSection = () => { +const RiskEngineHealth: React.FC<{ currentRiskEngineStatus?: RiskEngineStatus | null }> = ({ + currentRiskEngineStatus, +}) => { + if (!currentRiskEngineStatus) { + return {'-'}; + } + if (currentRiskEngineStatus === RiskEngineStatus.ENABLED) { + return {i18n.RISK_SCORE_MODULE_STATUS_ON}; + } + return {i18n.RISK_SCORE_MODULE_STATUS_OFF}; +}; + +const RiskEngineStatusRow: React.FC<{ + currentRiskEngineStatus?: RiskEngineStatus | null; + onSwitchClick: () => void; + isLoading: boolean; + privileges: RiskEngineMissingPrivilegesResponse; +}> = ({ currentRiskEngineStatus, onSwitchClick, isLoading, privileges }) => { + const userHasRequiredPrivileges = + 'hasAllRequiredPrivileges' in privileges && privileges.hasAllRequiredPrivileges; + const btnIsDisabled = !currentRiskEngineStatus || isLoading || !userHasRequiredPrivileges; + + return ( + + {isLoading && ( + + + + )} + + + + + + + + ); +}; + +export const RiskScoreEnableSection: React.FC<{ + privileges: RiskEngineMissingPrivilegesResponse; +}> = ({ privileges }) => { const [isModalVisible, setIsModalVisible] = useState(false); const { data: riskEngineStatus, isFetching: isStatusLoading } = useRiskEngineStatus(); const initRiskEngineMutation = useInitRiskEngineMutation({ @@ -200,13 +211,13 @@ export const RiskScoreEnableSection = () => { initRiskEngineMutation.isLoading || enableRiskEngineMutation.isLoading || disableRiskEngineMutation.isLoading || + privileges.isLoading || isStatusLoading; const isUpdateAvailable = riskEngineStatus?.isUpdateAvailable; - const btnIsDisabled = !currentRiskEngineStatus || isLoading; const onSwitchClick = () => { - if (btnIsDisabled) { + if (!currentRiskEngineStatus || isLoading) { return; } @@ -295,34 +306,12 @@ export const RiskScoreEnableSection = () => {
)} {!isUpdateAvailable && ( - - - {isLoading && ( - - )} - - - {currentRiskEngineStatus === RiskEngineStatus.ENABLED ? ( - {i18n.RISK_SCORE_MODULE_STATUS_ON} - ) : ( - {i18n.RISK_SCORE_MODULE_STATUS_OFF} - )} - - - - - + )} diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_preview_section.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_preview_section.tsx index 74c7aa4dcbdea..c927a12f48a64 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_preview_section.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_preview_section.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import React, { useState, useCallback } from 'react'; +import React, { useState, useCallback, useMemo } from 'react'; import type { DataView } from '@kbn/data-views-plugin/public'; import { EuiAccordion, @@ -17,10 +17,19 @@ import { EuiButton, EuiIcon, EuiText, + EuiLoadingSpinner, + EuiFlexGroup, + EuiFlexItem, + EuiCode, } from '@elastic/eui'; import type { BoolQuery, TimeRange, Query } from '@kbn/es-query'; import { buildEsQuery } from '@kbn/es-query'; -import { RiskScoreEntity, type RiskScore } from '../../../common/risk_engine'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { + RiskScoreEntity, + RISK_SCORE_INDEX_PATTERN, + type RiskScore, +} from '../../../common/entity_analytics/risk_engine'; import { RiskScorePreviewTable } from './risk_score_preview_table'; import * as i18n from '../translations'; import { useRiskScorePreview } from '../api/hooks/use_preview_risk_scores'; @@ -28,7 +37,8 @@ import { useKibana } from '../../common/lib/kibana'; import { SourcererScopeName } from '../../common/store/sourcerer/model'; import { useSourcererDataView } from '../../common/containers/sourcerer'; import { useAppToasts } from '../../common/hooks/use_app_toasts'; - +import type { RiskEngineMissingPrivilegesResponse } from '../hooks/use_missing_risk_engine_privileges'; +import { userHasRiskEngineReadPermissions } from '../common'; interface IRiskScorePreviewPanel { showMessage: string; hideMessage: string; @@ -43,6 +53,58 @@ const getRiskiestScores = (scores: RiskScore[] = [], field: string) => ?.sort((a, b) => b?.calculated_score_norm - a?.calculated_score_norm) ?.slice(0, 5) || []; +export const RiskScorePreviewSection: React.FC<{ + privileges: RiskEngineMissingPrivilegesResponse; +}> = ({ privileges }) => { + const sectionBody = useMemo(() => { + if (privileges.isLoading) { + return ( + + + + + + ); + } + if (userHasRiskEngineReadPermissions(privileges)) { + return ; + } + + return ; + }, [privileges]); + + return ( + <> + +

{i18n.PREVIEW}

+
+ + {sectionBody} + + ); +}; + +const MissingPermissionsCallout = () => { + return ( + + + {RISK_SCORE_INDEX_PATTERN}, + }} + /> + + + ); +}; + const RiskScorePreviewPanel = ({ items, showMessage, @@ -76,7 +138,7 @@ const RiskScorePreviewPanel = ({ ); }; -export const RiskScorePreviewSection = () => { +const RiskEnginePreview = () => { const [dateRange, setDateRange] = useState<{ from: string; to: string }>({ from: 'now-24h', to: 'now', @@ -150,10 +212,6 @@ export const RiskScorePreviewSection = () => { return ( <> - -

{i18n.PREVIEW}

-
- {i18n.PREVIEW_DESCRIPTION} diff --git a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_preview_table.tsx b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_preview_table.tsx index 12b162d467132..05fa3c18809a6 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_preview_table.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/components/risk_score_preview_table.tsx @@ -12,7 +12,10 @@ import type { RiskSeverity } from '../../../common/search_strategy'; import { RiskScoreLevel } from '../../explore/components/risk_score/severity/common'; import { HostDetailsLink, UserDetailsLink } from '../../common/components/links'; -import { RiskScoreEntity, type RiskScore as IRiskScore } from '../../../common/risk_engine'; +import { + RiskScoreEntity, + type RiskScore as IRiskScore, +} from '../../../common/entity_analytics/risk_engine'; type RiskScoreColumn = EuiBasicTableColumn & { field: keyof IRiskScore; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/hooks/use_missing_risk_engine_privileges.ts b/x-pack/plugins/security_solution/public/entity_analytics/hooks/use_missing_risk_engine_privileges.ts new file mode 100644 index 0000000000000..d51c2b2dfb49d --- /dev/null +++ b/x-pack/plugins/security_solution/public/entity_analytics/hooks/use_missing_risk_engine_privileges.ts @@ -0,0 +1,51 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { useMemo } from 'react'; +import { useRiskEnginePrivileges } from '../api/hooks/use_risk_engine_privileges'; +import { getMissingRiskEnginePrivileges } from '../../../common/entity_analytics/risk_engine'; +import type { MissingPrivileges } from '../../../common/entity_analytics/risk_engine'; +export type RiskEngineMissingPrivilegesResponse = + | { isLoading: true } + | { isLoading: false; hasAllRequiredPrivileges: true } + | { + isLoading: false; + missingPrivileges: MissingPrivileges; + hasAllRequiredPrivileges: false; + }; + +export const useMissingRiskEnginePrivileges = (): RiskEngineMissingPrivilegesResponse => { + const { data: privilegesResponse, isLoading } = useRiskEnginePrivileges(); + + return useMemo(() => { + if (isLoading || !privilegesResponse) { + return { + isLoading: true, + }; + } + + if (privilegesResponse.has_all_required) { + return { + isLoading: false, + hasAllRequiredPrivileges: true, + }; + } + + const { indexPrivileges, clusterPrivileges } = getMissingRiskEnginePrivileges( + privilegesResponse.privileges + ); + + return { + isLoading: false, + hasAllRequiredPrivileges: false, + missingPrivileges: { + indexPrivileges, + clusterPrivileges, + }, + }; + }, [isLoading, privilegesResponse]); +}; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/jest.config.js b/x-pack/plugins/security_solution/public/entity_analytics/jest.config.js new file mode 100644 index 0000000000000..ab3868717aa54 --- /dev/null +++ b/x-pack/plugins/security_solution/public/entity_analytics/jest.config.js @@ -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. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../../..', + roots: ['/x-pack/plugins/security_solution/public/entity_analytics'], + coverageDirectory: + '/target/kibana-coverage/jest/x-pack/plugins/security_solution/public/entity_analytics', + coverageReporters: ['text', 'html'], + collectCoverageFrom: [ + '/x-pack/plugins/security_solution/public/entity_analytics/**/*.{ts,tsx}', + ], + moduleNameMapper: require('../../server/__mocks__/module_name_map'), +}; diff --git a/x-pack/plugins/security_solution/public/entity_analytics/pages/entity_analytics_management_page.tsx b/x-pack/plugins/security_solution/public/entity_analytics/pages/entity_analytics_management_page.tsx index 7bc395d279751..71b7e676eaae6 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/pages/entity_analytics_management_page.tsx +++ b/x-pack/plugins/security_solution/public/entity_analytics/pages/entity_analytics_management_page.tsx @@ -13,15 +13,13 @@ import { RiskScoreEnableSection } from '../components/risk_score_enable_section' import { ENTITY_ANALYTICS_RISK_SCORE } from '../../app/translations'; import { BETA } from '../../common/translations'; import { RiskEnginePrivilegesCallOut } from '../components/risk_engine_privileges_callout'; -import { useIsExperimentalFeatureEnabled } from '../../common/hooks/use_experimental_features'; +import { useMissingRiskEnginePrivileges } from '../hooks/use_missing_risk_engine_privileges'; export const EntityAnalyticsManagementPage = () => { - const privilegesCalloutEnabled = useIsExperimentalFeatureEnabled( - 'riskEnginePrivilegesRouteEnabled' - ); + const privileges = useMissingRiskEnginePrivileges(); return ( <> - {privilegesCalloutEnabled && } + { - + - + diff --git a/x-pack/plugins/security_solution/public/entity_analytics/translations.ts b/x-pack/plugins/security_solution/public/entity_analytics/translations.ts index 06880dbdd4ae1..533f28f207de6 100644 --- a/x-pack/plugins/security_solution/public/entity_analytics/translations.ts +++ b/x-pack/plugins/security_solution/public/entity_analytics/translations.ts @@ -98,6 +98,13 @@ export const EA_DOCS_ENTITY_RISK_SCORE = i18n.translate( } ); +export const PREVIEW_MISSING_PERMISSIONS_TITLE = i18n.translate( + 'xpack.securitySolution.riskScore.riskScorePreview.missingPermissionsCallout.title', + { + defaultMessage: 'Insifficient index privileges to preview data', + } +); + export const PREVIEW = i18n.translate('xpack.securitySolution.riskScore.riskScorePreview.preview', { defaultMessage: 'Preview', }); diff --git a/x-pack/plugins/security_solution/public/explore/components/authentication/__snapshots__/authentications_host_table.test.tsx.snap b/x-pack/plugins/security_solution/public/explore/components/authentication/__snapshots__/authentications_host_table.test.tsx.snap index cebd8a483aafb..b0b2754c24d3b 100644 --- a/x-pack/plugins/security_solution/public/explore/components/authentication/__snapshots__/authentications_host_table.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/explore/components/authentication/__snapshots__/authentications_host_table.test.tsx.snap @@ -374,32 +374,28 @@ exports[`Authentication Host Table Component rendering it renders the host authe class="euiFlexItem emotion-euiFlexItem-grow-1" >
-
- -
+ + +

-
- -
+ + +
= ({ expanded = true }) => { - ); }; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_status.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_status.test.tsx deleted file mode 100644 index 31d9d686c4bd0..0000000000000 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_status.test.tsx +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { act, render } from '@testing-library/react'; -import { AlertStatus } from './alert_status'; -import { RightPanelContext } from '../context'; -import { WORKFLOW_STATUS_DETAILS_TEST_ID, WORKFLOW_STATUS_TITLE_TEST_ID } from './test_ids'; -import { mockSearchHit } from '../../shared/mocks/mock_search_hit'; -import { TestProviders } from '../../../../common/mock'; -import { useBulkGetUserProfiles } from '../../../../common/components/user_profiles/use_bulk_get_user_profiles'; - -jest.mock('../../../../common/components/user_profiles/use_bulk_get_user_profiles'); - -const renderAlertStatus = (contextValue: RightPanelContext) => - render( - - - - - - ); - -const mockUserProfiles = [ - { uid: 'user-id-1', enabled: true, user: { username: 'user1', full_name: 'User 1' }, data: {} }, -]; - -describe('', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - it('should render alert status history information', async () => { - (useBulkGetUserProfiles as jest.Mock).mockReturnValue({ - isLoading: false, - data: mockUserProfiles, - }); - const contextValue = { - searchHit: { - ...mockSearchHit, - fields: { - 'kibana.alert.workflow_user': 'user-id-1', - 'kibana.alert.workflow_status_updated_at': '2023-11-01T22:33:26.893Z', - }, - }, - } as unknown as RightPanelContext; - - const { getByTestId } = renderAlertStatus(contextValue); - - await act(async () => { - expect(getByTestId(WORKFLOW_STATUS_TITLE_TEST_ID)).toBeInTheDocument(); - expect(getByTestId(WORKFLOW_STATUS_DETAILS_TEST_ID)).toBeInTheDocument(); - }); - }); - - it('should render empty component if missing workflow_user value', async () => { - const contextValue = { - searchHit: { - some_field: 'some_value', - }, - } as unknown as RightPanelContext; - - const { container } = renderAlertStatus(contextValue); - - await act(async () => { - expect(container).toBeEmptyDOMElement(); - }); - }); -}); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_status.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_status.tsx deleted file mode 100644 index 07ce258b7dd7c..0000000000000 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/alert_status.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 { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiTitle } from '@elastic/eui'; -import { getUserDisplayName } from '@kbn/user-profile-components'; -import { FormattedMessage } from '@kbn/i18n-react'; -import type { FC } from 'react'; -import React from 'react'; -import { WORKFLOW_STATUS_DETAILS_TEST_ID, WORKFLOW_STATUS_TITLE_TEST_ID } from './test_ids'; -import { useRightPanelContext } from '../context'; -import { useBulkGetUserProfiles } from '../../../../common/components/user_profiles/use_bulk_get_user_profiles'; -import { PreferenceFormattedDate } from '../../../../common/components/formatted_date'; - -/** - * Displays info about who last updated the alert's workflow status and when. - */ -export const AlertStatus: FC = () => { - const { searchHit } = useRightPanelContext(); - const statusUpdatedBy = searchHit.fields?.['kibana.alert.workflow_user'] as string; - const statusUpdatedAt = searchHit.fields?.['kibana.alert.workflow_status_updated_at']; - - const result = useBulkGetUserProfiles({ uids: new Set(statusUpdatedBy) }); - const user = result.data?.[0]?.user; - - if (!statusUpdatedBy || !statusUpdatedAt || result.isLoading || user == null) { - return null; - } - - return ( - - - - -
- -
-
-
- - , - }} - /> - -
- ); -}; - -AlertStatus.displayName = 'AlertStatus'; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/analyzer_preview_container.test.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/analyzer_preview_container.test.tsx index b8c2672af1dc2..c43511ddae460 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/analyzer_preview_container.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/analyzer_preview_container.test.tsx @@ -11,7 +11,7 @@ import React from 'react'; import { RightPanelContext } from '../context'; import { mockContextValue } from '../mocks/mock_context'; import { AnalyzerPreviewContainer } from './analyzer_preview_container'; -import { isInvestigateInResolverActionEnabled } from '../../../../detections/components/alerts_table/timeline_actions/investigate_in_resolver'; +import { useIsInvestigateInResolverActionEnabled } from '../../../../detections/components/alerts_table/timeline_actions/investigate_in_resolver'; import { ANALYZER_PREVIEW_TEST_ID } from './test_ids'; import { useAlertPrevalenceFromProcessTree } from '../../../../common/containers/alerts/use_alert_prevalence_from_process_tree'; import * as mock from '../mocks/mock_analyzer_data'; @@ -68,7 +68,7 @@ describe('AnalyzerPreviewContainer', () => { }); it('should render component and link in header', () => { - (isInvestigateInResolverActionEnabled as jest.Mock).mockReturnValue(true); + (useIsInvestigateInResolverActionEnabled as jest.Mock).mockReturnValue(true); (useAlertPrevalenceFromProcessTree as jest.Mock).mockReturnValue({ loading: false, error: false, @@ -103,7 +103,7 @@ describe('AnalyzerPreviewContainer', () => { }); it('should render error message and text in header', () => { - (isInvestigateInResolverActionEnabled as jest.Mock).mockReturnValue(false); + (useIsInvestigateInResolverActionEnabled as jest.Mock).mockReturnValue(false); (useInvestigateInTimeline as jest.Mock).mockReturnValue({ investigateInTimelineAlertClick: jest.fn(), }); @@ -118,7 +118,7 @@ describe('AnalyzerPreviewContainer', () => { }); it('should navigate to analyzer in timeline when clicking on title', () => { - (isInvestigateInResolverActionEnabled as jest.Mock).mockReturnValue(true); + (useIsInvestigateInResolverActionEnabled as jest.Mock).mockReturnValue(true); (useAlertPrevalenceFromProcessTree as jest.Mock).mockReturnValue({ loading: false, error: false, @@ -138,7 +138,7 @@ describe('AnalyzerPreviewContainer', () => { }); it('should not navigate to analyzer when in preview and clicking on title', () => { - (isInvestigateInResolverActionEnabled as jest.Mock).mockReturnValue(true); + (useIsInvestigateInResolverActionEnabled as jest.Mock).mockReturnValue(true); (useAlertPrevalenceFromProcessTree as jest.Mock).mockReturnValue({ loading: false, error: false, diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/analyzer_preview_container.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/analyzer_preview_container.tsx index 843abeb7018e6..40c3b513114e7 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/analyzer_preview_container.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/analyzer_preview_container.tsx @@ -14,9 +14,9 @@ import { useStartTransaction } from '../../../../common/lib/apm/use_start_transa import { useInvestigateInTimeline } from '../../../../detections/components/alerts_table/timeline_actions/use_investigate_in_timeline'; import { ALERTS_ACTIONS } from '../../../../common/lib/apm/user_actions'; import { getScopedActions } from '../../../../helpers'; -import { setActiveTabTimeline } from '../../../../timelines/store/timeline/actions'; +import { setActiveTabTimeline } from '../../../../timelines/store/actions'; import { useRightPanelContext } from '../context'; -import { isInvestigateInResolverActionEnabled } from '../../../../detections/components/alerts_table/timeline_actions/investigate_in_resolver'; +import { useIsInvestigateInResolverActionEnabled } from '../../../../detections/components/alerts_table/timeline_actions/investigate_in_resolver'; import { AnalyzerPreview } from './analyzer_preview'; import { ANALYZER_PREVIEW_TEST_ID } from './test_ids'; import { ExpandablePanel } from '../../../shared/components/expandable_panel'; @@ -30,7 +30,7 @@ export const AnalyzerPreviewContainer: React.FC = () => { const { dataAsNestedObject, isPreview } = useRightPanelContext(); // decide whether to show the analyzer preview or not - const isEnabled = isInvestigateInResolverActionEnabled(dataAsNestedObject); + const isEnabled = useIsInvestigateInResolverActionEnabled(dataAsNestedObject); const dispatch = useDispatch(); const { startTransaction } = useStartTransaction(); diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields.tsx index 806a43f9f5497..5e6bcb2add441 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/highlighted_fields.tsx @@ -60,7 +60,7 @@ const columns: Array> = [ /> ), 'data-test-subj': 'fieldCell', - width: '50%', + width: '30%', }, { field: 'description', @@ -71,7 +71,7 @@ const columns: Array> = [ /> ), 'data-test-subj': 'valueCell', - width: '50%', + width: '70%', render: (description: { field: string; values: string[] | null | undefined; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/session_preview_container.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/session_preview_container.tsx index d0214b725a44e..5b5f964a8fdc0 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/session_preview_container.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/session_preview_container.tsx @@ -20,7 +20,7 @@ import { ALERTS_ACTIONS } from '../../../../common/lib/apm/user_actions'; import { ExpandablePanel } from '../../../shared/components/expandable_panel'; import { SESSION_PREVIEW_TEST_ID } from './test_ids'; import { useStartTransaction } from '../../../../common/lib/apm/use_start_transaction'; -import { setActiveTabTimeline } from '../../../../timelines/store/timeline/actions'; +import { setActiveTabTimeline } from '../../../../timelines/store/actions'; import { getScopedActions } from '../../../../helpers'; const timelineId = 'timeline-1'; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/test_ids.ts b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/test_ids.ts index fd9c15b2c079c..5b176a34014ab 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/components/test_ids.ts +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/components/test_ids.ts @@ -43,10 +43,6 @@ const MITRE_ATTACK_TEST_ID = `${PREFIX}MitreAttack` as const; export const MITRE_ATTACK_TITLE_TEST_ID = `${MITRE_ATTACK_TEST_ID}Title` as const; export const MITRE_ATTACK_DETAILS_TEST_ID = `${MITRE_ATTACK_TEST_ID}Details` as const; -export const WORKFLOW_STATUS_TEST_ID = `${PREFIX}WorkflowStatus` as const; -export const WORKFLOW_STATUS_TITLE_TEST_ID = `${WORKFLOW_STATUS_TEST_ID}Title` as const; -export const WORKFLOW_STATUS_DETAILS_TEST_ID = `${WORKFLOW_STATUS_TEST_ID}Details` as const; - /* Investigation section */ export const INVESTIGATION_SECTION_TEST_ID = `${PREFIX}InvestigationSection` as const; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/footer.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/footer.tsx index 995beea63a70f..b24de81e65f09 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/footer.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/footer.tsx @@ -8,12 +8,17 @@ import type { FC } from 'react'; import React, { useCallback } from 'react'; import { useExpandableFlyoutContext } from '@kbn/expandable-flyout'; -import { EuiPanel } from '@elastic/eui'; +import styled from 'styled-components'; +import { euiThemeVars } from '@kbn/ui-theme'; import { FlyoutFooter } from '../../../timelines/components/side_panel/event_details/flyout'; import { useRightPanelContext } from './context'; import { useHostIsolationTools } from '../../../timelines/components/side_panel/event_details/use_host_isolation_tools'; -import { DEFAULT_DARK_MODE } from '../../../../common/constants'; -import { useUiSetting } from '../../../common/lib/kibana'; + +const ContainerDiv = styled('div')` + .side-panel-flyout-footer { + padding: ${euiThemeVars.euiPanelPaddingModifiers.paddingMedium}; + } +`; interface PanelFooterProps { /** @@ -35,8 +40,6 @@ export const PanelFooter: FC = ({ isPreview }) => { refetchFlyoutData, scopeId, } = useRightPanelContext(); - const isDarkMode = useUiSetting(DEFAULT_DARK_MODE); - const { isHostIsolationPanelOpen, showHostIsolationPanel } = useHostIsolationTools(); const showHostIsolationPanelCallback = useCallback( @@ -56,13 +59,7 @@ export const PanelFooter: FC = ({ isPreview }) => { ); return !isPreview ? ( - + = ({ isPreview }) => { scopeId={scopeId} refetchFlyoutData={refetchFlyoutData} /> - + ) : null; }; diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/index.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/index.tsx index 08142a0ef08ba..fa209de15c175 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/index.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/index.tsx @@ -6,7 +6,7 @@ */ import type { FC } from 'react'; -import React, { memo, useMemo } from 'react'; +import React, { memo, useMemo, useEffect } from 'react'; import type { FlyoutPanelProps, PanelPath } from '@kbn/expandable-flyout'; import { useExpandableFlyoutContext } from '@kbn/expandable-flyout'; import { EventKind } from '../shared/constants/event_kinds'; @@ -36,7 +36,7 @@ export interface RightPanelProps extends FlyoutPanelProps { * Panel to be displayed in the document details expandable flyout right section */ export const RightPanel: FC> = memo(({ path }) => { - const { openRightPanel } = useExpandableFlyoutContext(); + const { openRightPanel, closeFlyout } = useExpandableFlyoutContext(); const { eventId, getFieldsData, indexName, scopeId, isPreview } = useRightPanelContext(); // for 8.10, we only render the flyout in its expandable mode if the document viewed is of type signal @@ -63,6 +63,19 @@ export const RightPanel: FC> = memo(({ path }) => { }); }; + // If flyout is open in preview mode, do not reload with stale information + useEffect(() => { + const beforeUnloadHandler = () => { + if (isPreview) { + closeFlyout(); + } + }; + window.addEventListener('beforeunload', beforeUnloadHandler); + return () => { + window.removeEventListener('beforeunload', beforeUnloadHandler); + }; + }, [isPreview, closeFlyout]); + return ( <> diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/right/tabs/table_tab.tsx b/x-pack/plugins/security_solution/public/flyout/document_details/right/tabs/table_tab.tsx index 8351d70216be7..c0083d8f80b2d 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/right/tabs/table_tab.tsx +++ b/x-pack/plugins/security_solution/public/flyout/document_details/right/tabs/table_tab.tsx @@ -47,6 +47,7 @@ export const getColumns: ColumnsProvider = ({ {i18n.FIELD} ), + width: '30%', render: (field, data) => { return ( @@ -60,6 +61,7 @@ export const getColumns: ColumnsProvider = ({ {i18n.VALUE} ), + width: '70%', render: (values, data) => { const fieldFromBrowserField = getFieldFromBrowserField( [data.category as string, 'fields', data.field], diff --git a/x-pack/plugins/security_solution/public/flyout/document_details/shared/hooks/use_show_related_alerts_by_ancestry.ts b/x-pack/plugins/security_solution/public/flyout/document_details/shared/hooks/use_show_related_alerts_by_ancestry.ts index 9d4434d943e12..53a1b336ff456 100644 --- a/x-pack/plugins/security_solution/public/flyout/document_details/shared/hooks/use_show_related_alerts_by_ancestry.ts +++ b/x-pack/plugins/security_solution/public/flyout/document_details/shared/hooks/use_show_related_alerts_by_ancestry.ts @@ -10,7 +10,7 @@ import { useMemo } from 'react'; import { find } from 'lodash/fp'; import type { TimelineEventsDetailsItem } from '@kbn/timelines-plugin/common'; import type { GetFieldsData } from '../../../../common/hooks/use_get_fields_data'; -import { isInvestigateInResolverActionEnabled } from '../../../../detections/components/alerts_table/timeline_actions/investigate_in_resolver'; +import { useIsInvestigateInResolverActionEnabled } from '../../../../detections/components/alerts_table/timeline_actions/investigate_in_resolver'; import { useIsExperimentalFeatureEnabled } from '../../../../common/hooks/use_experimental_features'; import { useLicense } from '../../../../common/hooks/use_license'; import { getField } from '../utils'; @@ -57,7 +57,7 @@ export const useShowRelatedAlertsByAncestry = ({ const isRelatedAlertsByProcessAncestryEnabled = useIsExperimentalFeatureEnabled( 'insightsRelatedAlertsByProcessAncestry' ); - const hasProcessEntityInfo = isInvestigateInResolverActionEnabled(dataAsNestedObject); + const hasProcessEntityInfo = useIsInvestigateInResolverActionEnabled(dataAsNestedObject); const originalDocumentId = getField(getFieldsData(ANCESTOR_ID)); diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/risk_inputs_left/content.test.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/risk_inputs_left/content.test.tsx index 93396e7dea03a..a5138dc035dc8 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/risk_inputs_left/content.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/risk_inputs_left/content.test.tsx @@ -5,8 +5,8 @@ * 2.0. */ -import type { SimpleRiskInput } from '../../../../common/risk_engine'; -import { RiskCategories } from '../../../../common/risk_engine'; +import type { SimpleRiskInput } from '../../../../common/entity_analytics/risk_engine'; +import { RiskCategories } from '../../../../common/entity_analytics/risk_engine'; import { fireEvent, render } from '@testing-library/react'; import React from 'react'; import { RiskInputsPanel } from '.'; diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/risk_inputs_left/content.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/risk_inputs_left/content.tsx index 544dca9df62a8..34b81d26e8888 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/risk_inputs_left/content.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/risk_inputs_left/content.tsx @@ -12,7 +12,7 @@ import { css } from '@emotion/react'; import { FormattedMessage } from '@kbn/i18n-react'; import { get } from 'lodash/fp'; import { ALERT_RULE_NAME } from '@kbn/rule-data-utils'; -import type { RiskInputs } from '../../../../common/risk_engine'; +import type { RiskInputs } from '../../../../common/entity_analytics/risk_engine'; import { ActionColumn } from './components/action_column'; import { PreferenceFormattedDate } from '../../../common/components/formatted_date'; import { RiskInputsUtilityBar } from './components/utility_bar'; diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/risk_inputs_left/index.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/risk_inputs_left/index.tsx index 386ec25bfeaf8..de926204236ba 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/risk_inputs_left/index.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/risk_inputs_left/index.tsx @@ -7,7 +7,7 @@ import React from 'react'; import type { FlyoutPanelProps } from '@kbn/expandable-flyout'; -import type { RiskInputs } from '../../../../common/risk_engine'; +import type { RiskInputs } from '../../../../common/entity_analytics/risk_engine'; import { RiskInputsPanelContent } from './content'; export interface RiskInputsPanelProps extends Record { diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/shared/components/risk_summary.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/shared/components/risk_summary.tsx index 763a6377a9d67..c4eaa36273166 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/shared/components/risk_summary.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/shared/components/risk_summary.tsx @@ -25,7 +25,7 @@ import { i18n } from '@kbn/i18n'; import { InspectButton, InspectButtonContainer } from '../../../../common/components/inspect'; import { ONE_WEEK_IN_HOURS } from '../../../../timelines/components/side_panel/new_user_detail/constants'; import { FormattedRelativePreferenceDate } from '../../../../common/components/formatted_date'; -import { RiskScoreEntity } from '../../../../../common/risk_engine'; +import { RiskScoreEntity } from '../../../../../common/entity_analytics/risk_engine'; import type { RiskScoreState } from '../../../../explore/containers/risk_score'; import { VisualizationEmbeddable } from '../../../../common/components/visualization_actions/visualization_embeddable'; import { getRiskScoreSummaryAttributes } from '../../../../common/components/visualization_actions/lens_attributes/common/risk_scores/risk_score_summary'; diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/header.test.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/header.test.tsx index d502e8e2d06dd..2f1ada7447dd6 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/header.test.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/header.test.tsx @@ -75,7 +75,6 @@ describe('UserDetailsContent', () => { '@timestamp': [futureDay], }, }, - [ManagedUserDatasetKey.OKTA]: undefined, }, }, }} @@ -126,10 +125,7 @@ describe('UserDetailsContent', () => { ...mockProps, managedUser: { ...mockManagedUserData, - data: { - [ManagedUserDatasetKey.ENTRA]: undefined, - [ManagedUserDatasetKey.OKTA]: undefined, - }, + data: {}, }, }} /> diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.tsx b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.tsx index bdac0653c5b7e..ff9add6011d05 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.tsx +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/index.tsx @@ -17,7 +17,7 @@ import { useGlobalTime } from '../../../common/containers/use_global_time'; import { AnomalyTableProvider } from '../../../common/components/ml/anomaly/anomaly_table_provider'; import { buildUserNamesFilter } from '../../../../common/search_strategy'; import { useRiskScore } from '../../../explore/containers/risk_score'; -import { RiskScoreEntity } from '../../../../common/risk_engine'; +import { RiskScoreEntity } from '../../../../common/entity_analytics/risk_engine'; import { FlyoutLoading } from '../../shared/components/flyout_loading'; import { RiskInputsPanelKey } from '../risk_inputs_left'; import { FlyoutNavigation } from '../../shared/components/flyout_navigation'; diff --git a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/mocks/index.ts b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/mocks/index.ts index bfd1a9a802199..d677f79ca4322 100644 --- a/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/mocks/index.ts +++ b/x-pack/plugins/security_solution/public/flyout/entity_details/user_right/mocks/index.ts @@ -8,7 +8,7 @@ import type { RiskScoreState } from '../../../../explore/containers/risk_score'; import type { RiskScoreEntity, UserRiskScore } from '../../../../../common/search_strategy'; import { RiskSeverity } from '../../../../../common/search_strategy'; -import { RiskCategories } from '../../../../../common/risk_engine'; +import { RiskCategories } from '../../../../../common/entity_analytics/risk_engine'; const userRiskScore: UserRiskScore = { '@timestamp': '626569200000', diff --git a/x-pack/plugins/security_solution/public/flyout/index.tsx b/x-pack/plugins/security_solution/public/flyout/index.tsx index be93654529595..71ea03d18572a 100644 --- a/x-pack/plugins/security_solution/public/flyout/index.tsx +++ b/x-pack/plugins/security_solution/public/flyout/index.tsx @@ -5,12 +5,8 @@ * 2.0. */ -import React, { memo, type FC } from 'react'; -import { - ExpandableFlyout, - type ExpandableFlyoutProps, - ExpandableFlyoutProvider, -} from '@kbn/expandable-flyout'; +import React, { memo } from 'react'; +import { ExpandableFlyout, type ExpandableFlyoutProps } from '@kbn/expandable-flyout'; import type { IsolateHostPanelProps } from './document_details/isolate_host'; import { IsolateHostPanel, @@ -93,13 +89,6 @@ const expandableFlyoutDocumentsPanels: ExpandableFlyoutProps['registeredPanels'] }, ]; -// NOTE: provider below accepts "storage" prop, please take a look into component's JSDoc. -export const SecuritySolutionFlyoutContextProvider: FC = ({ children }) => ( - {children} -); - -SecuritySolutionFlyoutContextProvider.displayName = 'SecuritySolutionFlyoutContextProvider'; - export const SecuritySolutionFlyout = memo(() => ( )); diff --git a/x-pack/plugins/security_solution/public/helpers.tsx b/x-pack/plugins/security_solution/public/helpers.tsx index b245a6ae2f6bc..09ad6612bdb06 100644 --- a/x-pack/plugins/security_solution/public/helpers.tsx +++ b/x-pack/plugins/security_solution/public/helpers.tsx @@ -36,7 +36,7 @@ import { NoPrivilegesPage } from './common/components/no_privileges'; import { SecurityPageName } from './app/types'; import type { InspectResponse, StartedSubPlugins, StartServices } from './types'; import { CASES_SUB_PLUGIN_KEY } from './types'; -import { timelineActions } from './timelines/store/timeline'; +import { timelineActions } from './timelines/store'; import { TimelineId } from '../common/types'; import { SourcererScopeName } from './common/store/sourcerer/model'; diff --git a/x-pack/plugins/security_solution/public/index.ts b/x-pack/plugins/security_solution/public/index.ts index 3af2b724b014e..92fcf14ace691 100644 --- a/x-pack/plugins/security_solution/public/index.ts +++ b/x-pack/plugins/security_solution/public/index.ts @@ -8,7 +8,7 @@ import type { PluginInitializerContext } from '@kbn/core/public'; import { Plugin } from './plugin'; import type { PluginSetup, PluginStart } from './types'; -export type { TimelineModel } from './timelines/store/timeline/model'; +export type { TimelineModel } from './timelines/store/model'; export type { LinkItem } from './common/links'; export type { FetchRulesResponse } from './detection_engine/rule_management/logic/types'; diff --git a/x-pack/plugins/security_solution/public/management/components/console_argument_selectors/file_selector.tsx b/x-pack/plugins/security_solution/public/management/components/console_argument_selectors/file_selector.tsx index 2373caf38ba39..58dada4f3b308 100644 --- a/x-pack/plugins/security_solution/public/management/components/console_argument_selectors/file_selector.tsx +++ b/x-pack/plugins/security_solution/public/management/components/console_argument_selectors/file_selector.tsx @@ -97,7 +97,6 @@ export const ArgumentFileSelector = memo< closePopover={handleClosePopover} anchorPosition="upCenter" initialFocus={`#${filePickerUUID}`} - anchorClassName="popoverAnchor" button={ diff --git a/x-pack/plugins/security_solution/public/management/components/effected_policy_select/effected_policy_select.test.tsx b/x-pack/plugins/security_solution/public/management/components/effected_policy_select/effected_policy_select.test.tsx index 174496f1f3d21..f3fdb0b8009be 100644 --- a/x-pack/plugins/security_solution/public/management/components/effected_policy_select/effected_policy_select.test.tsx +++ b/x-pack/plugins/security_solution/public/management/components/effected_policy_select/effected_policy_select.test.tsx @@ -74,13 +74,13 @@ describe('when using EffectedPolicySelect component', () => { const selectGlobalPolicy = () => { act(() => { - fireEvent.click(renderResult.getByTestId('globalPolicy')); + fireEvent.click(renderResult.getByTestId('test-global')); }); }; const selectPerPolicy = () => { act(() => { - fireEvent.click(renderResult.getByTestId('perPolicy')); + fireEvent.click(renderResult.getByTestId('test-perPolicy')); }); }; @@ -142,7 +142,8 @@ describe('when using EffectedPolicySelect component', () => { }); it('should maintain policies selection even if global was checked, and user switched back to per policy', () => { - render(); + const { debug } = render(); + debug(undefined, 999999); selectPerPolicy(); clickOnPolicy(); diff --git a/x-pack/plugins/security_solution/public/management/cypress/cypress_base.config.ts b/x-pack/plugins/security_solution/public/management/cypress/cypress_base.config.ts index 91b4751d745f1..1152caf76724a 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/cypress_base.config.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/cypress_base.config.ts @@ -66,6 +66,7 @@ export const getCypressBaseConfig = ( // grep related configs grepFilterSpecs: true, grepOmitFiltered: true, + IS_CI: process.env.CI, }, e2e: { diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifacts_mocked_data.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifacts_mocked_data.cy.ts index e7f32820c00d7..55a1035ca8969 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifacts_mocked_data.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/artifacts/artifacts_mocked_data.cy.ts @@ -31,7 +31,8 @@ const loginWithoutAccess = (url: string) => { loadPage(url); }; -describe('Artifacts pages', { tags: ['@ess', '@serverless'] }, () => { +// Flaky: https://github.com/elastic/kibana/issues/171168 +describe.skip('Artifacts pages', { tags: ['@ess', '@serverless'] }, () => { let endpointData: ReturnTypeFromChainable | undefined; before(() => { 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..305ce11d165ef 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 @@ -20,7 +20,9 @@ import { createEndpointHost } from '../../tasks/create_endpoint_host'; import { deleteAllLoadedEndpointData } from '../../tasks/delete_all_endpoint_data'; import { enableAllPolicyProtections } from '../../tasks/endpoint_policy'; -describe( +// FLAKY: https://github.com/elastic/kibana/issues/168340 +// FLAKY: https://github.com/elastic/kibana/issues/168427 +describe.skip( 'Automated Response Actions', { tags: [ diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/policy/policy_details.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/policy/policy_details.cy.ts index f927db6b0842d..78239fbca02d1 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/policy/policy_details.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/policy/policy_details.cy.ts @@ -44,7 +44,9 @@ describe( const testNote = 'test note'; const updatedTestNote = 'updated test note'; - describe('Renders and saves protection updates', () => { + // FLAKY: https://github.com/elastic/kibana/issues/169187 + // FLAKY: https://github.com/elastic/kibana/issues/169188 + describe.skip('Renders and saves protection updates', () => { let indexedPolicy: IndexedFleetEndpointPolicyResponse; let policy: PolicyData; const defaultDate = moment.utc().subtract(1, 'days'); diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/alerts_response_console.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/alerts_response_console.cy.ts index a736e05c33145..91557f3958f27 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/alerts_response_console.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/alerts_response_console.cy.ts @@ -26,7 +26,7 @@ import { enableAllPolicyProtections } from '../../tasks/endpoint_policy'; import { createEndpointHost } from '../../tasks/create_endpoint_host'; import { deleteAllLoadedEndpointData } from '../../tasks/delete_all_endpoint_data'; -describe('Response console', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => { +describe.skip('Response console', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => { let indexedPolicy: IndexedFleetEndpointPolicyResponse; let policy: PolicyData; let createdHost: CreateAndEnrollEndpointHostResponse; @@ -65,7 +65,9 @@ describe('Response console', { tags: ['@ess', '@serverless', '@brokenInServerles } }); - describe('From Alerts', () => { + // FLAKY: https://github.com/elastic/kibana/issues/169689 + // FLAKY: https://github.com/elastic/kibana/issues/173472 + describe.skip('From Alerts', () => { let ruleId: string; let ruleName: string; diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/cases_response_console.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/cases_response_console.cy.ts index a33d325d5443a..573d3749c7391 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/cases_response_console.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/cases_response_console.cy.ts @@ -28,7 +28,8 @@ import { createEndpointHost } from '../../tasks/create_endpoint_host'; import { deleteAllLoadedEndpointData } from '../../tasks/delete_all_endpoint_data'; import { APP_CASES_PATH } from '../../../../../common/constants'; -describe('Response console', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => { +// TIMING OUT: https://github.com/elastic/kibana/issues/172720 +describe.skip('Response console', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => { let indexedPolicy: IndexedFleetEndpointPolicyResponse; let policy: PolicyData; let createdHost: CreateAndEnrollEndpointHostResponse; diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/document_signing.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/document_signing.cy.ts index b806323726018..4093581366321 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/document_signing.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/document_signing.cy.ts @@ -22,7 +22,8 @@ import { enableAllPolicyProtections } from '../../tasks/endpoint_policy'; import { createEndpointHost } from '../../tasks/create_endpoint_host'; import { deleteAllLoadedEndpointData } from '../../tasks/delete_all_endpoint_data'; -describe('Document signing:', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => { +// FLAKY: https://github.com/elastic/kibana/issues/170674 +describe.skip('Document signing:', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => { let indexedPolicy: IndexedFleetEndpointPolicyResponse; let policy: PolicyData; let createdHost: CreateAndEnrollEndpointHostResponse; diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/endpoints_list_response_console.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/endpoints_list_response_console.cy.ts index 75074b0d3f94a..e932250dada6b 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/endpoints_list_response_console.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/endpoints_list_response_console.cy.ts @@ -20,7 +20,9 @@ import { enableAllPolicyProtections } from '../../tasks/endpoint_policy'; import { createEndpointHost } from '../../tasks/create_endpoint_host'; import { deleteAllLoadedEndpointData } from '../../tasks/delete_all_endpoint_data'; -describe('Response console', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => { +// FLAKY: https://github.com/elastic/kibana/issues/169821 +// FLAKY: https://github.com/elastic/kibana/issues/169822 +describe.skip('Response console', { tags: ['@ess', '@serverless', '@brokenInServerless'] }, () => { beforeEach(() => { login(); }); diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/execute.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/execute.cy.ts index dad573bb09c2b..8be29e75ddd9c 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/execute.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/execute.cy.ts @@ -26,7 +26,9 @@ describe('Response console', { tags: ['@ess', '@serverless'] }, () => { login(); }); - describe('Execute operations:', () => { + // FLAKY: https://github.com/elastic/kibana/issues/170373 + // FLAKY: https://github.com/elastic/kibana/issues/171444 + describe.skip('Execute operations:', () => { const homeFilePath = process.env.CI || true ? '/home/vagrant' : `/home/ubuntu`; let indexedPolicy: IndexedFleetEndpointPolicyResponse; diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/file_operations.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/file_operations.cy.ts index 9d378fde22f68..a470098b4f55f 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/file_operations.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/file_operations.cy.ts @@ -26,9 +26,10 @@ describe('Response console', { tags: ['@ess', '@serverless'] }, () => { login(); }); - // FLAKY: https://github.com/elastic/kibana/issues/170482 + // FLAKY: https://github.com/elastic/kibana/issues/170424 + // FLAKY: https://github.com/elastic/kibana/issues/173456 describe.skip('File operations:', () => { - const homeFilePath = process.env.CI || true ? '/home/vagrant' : `/home/ubuntu`; + const homeFilePath = Cypress.env('IS_CI') ? '/home/vagrant' : '/home/ubuntu'; const fileContent = 'This is a test file for the get-file command.'; const filePath = `${homeFilePath}/test_file.txt`; @@ -68,38 +69,52 @@ describe('Response console', { tags: ['@ess', '@serverless'] }, () => { }); it('"get-file --path" - should retrieve a file', () => { + const downloadsFolder = Cypress.config('downloadsFolder'); + waitForEndpointListPageToBeLoaded(createdHost.hostname); cy.task('createFileOnEndpoint', { hostname: createdHost.hostname, path: filePath, content: fileContent, }); + + // initiate get file action and wait for the API to complete + cy.intercept('api/endpoint/action/get_file').as('getFileAction'); openResponseConsoleFromEndpointList(); inputConsoleCommand(`get-file --path ${filePath}`); submitCommand(); - cy.getByTestSubj('getFileSuccess', { timeout: 60000 }).within(() => { + cy.wait('@getFileAction', { timeout: 60000 }); + + // verify that the file was retrieved + // and that the file download link is available + cy.getByTestSubj('getFileSuccess').within(() => { cy.contains('File retrieved from the host.'); cy.contains('(ZIP file passcode: elastic)'); cy.contains( 'Files are periodically deleted to clear storage space. Download and save file locally if needed.' ); - cy.contains('Click here to download').click(); - const downloadsFolder = Cypress.config('downloadsFolder'); - cy.readFile(`${downloadsFolder}/upload.zip`); - - cy.task('uploadFileToEndpoint', { - hostname: createdHost.hostname, - srcPath: `${downloadsFolder}/upload.zip`, - destPath: `${homeFilePath}/upload.zip`, - }); - - cy.task('readZippedFileContentOnEndpoint', { - hostname: createdHost.hostname, - path: `${homeFilePath}/upload.zip`, - password: 'elastic', - }).then((unzippedFileContent) => { - expect(unzippedFileContent).to.equal(fileContent); - }); + cy.contains('Click here to download').should('exist'); + }); + + cy.contains('Click here to download').click(); + + // wait for file to be downloaded + cy.readFile(`${downloadsFolder}/upload.zip`, { timeout: 120000 }).should('exist'); + + // move the zip file to VM + cy.task('uploadFileToEndpoint', { + hostname: createdHost.hostname, + srcPath: `${downloadsFolder}/upload.zip`, + destPath: `${homeFilePath}/upload.zip`, + }); + + // unzip the file and read its content + cy.task('readZippedFileContentOnEndpoint', { + hostname: createdHost.hostname, + path: `${homeFilePath}/upload.zip`, + password: 'elastic', + }).then((unzippedFileContent) => { + expect(unzippedFileContent).to.equal(fileContent); }); }); diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/isolate.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/isolate.cy.ts index 44fdf9d63fb68..fb5fcd0476c14 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/isolate.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/isolate.cy.ts @@ -26,7 +26,9 @@ import { enableAllPolicyProtections } from '../../../tasks/endpoint_policy'; import { createEndpointHost } from '../../../tasks/create_endpoint_host'; import { deleteAllLoadedEndpointData } from '../../../tasks/delete_all_endpoint_data'; -describe('Response console', { tags: ['@ess', '@serverless'] }, () => { +// FLAKY: https://github.com/elastic/kibana/issues/173464 +// FLAKY: https://github.com/elastic/kibana/issues/173465 +describe.skip('Response console', { tags: ['@ess', '@serverless'] }, () => { let indexedPolicy: IndexedFleetEndpointPolicyResponse; let policy: PolicyData; let createdHost: CreateAndEnrollEndpointHostResponse; diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/process_operations.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/process_operations.cy.ts index c7120ded692b9..ba2ebf1c1a88a 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/process_operations.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/response_actions/response_console/process_operations.cy.ts @@ -24,7 +24,9 @@ import { enableAllPolicyProtections } from '../../../tasks/endpoint_policy'; import { createEndpointHost } from '../../../tasks/create_endpoint_host'; import { deleteAllLoadedEndpointData } from '../../../tasks/delete_all_endpoint_data'; -describe('Response console', { tags: ['@ess', '@serverless'] }, () => { +// FLAKY: https://github.com/elastic/kibana/issues/173459 +// FLAKY: https://github.com/elastic/kibana/issues/170563 +describe.skip('Response console', { tags: ['@ess', '@serverless'] }, () => { beforeEach(() => { login(); }); diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/serverless/policy_details_with_security_essentials.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/serverless/policy_details_with_security_essentials.cy.ts index 36ebb060454c5..eb21b055a2cc1 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/serverless/policy_details_with_security_essentials.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/serverless/policy_details_with_security_essentials.cy.ts @@ -9,7 +9,8 @@ import { login } from '../../tasks/login'; import { visitPolicyDetailsPage } from '../../screens/policy_details'; import type { IndexedFleetEndpointPolicyResponse } from '../../../../../common/endpoint/data_loaders/index_fleet_endpoint_policy'; -describe( +// FLAKY: https://github.com/elastic/kibana/issues/170666 +describe.skip( 'When displaying the Policy Details in Security Essentials PLI', { tags: ['@serverless'], diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/disabled/unenroll_agent_from_fleet.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/disabled/unenroll_agent_from_fleet.cy.ts index e0b26bc2f77dd..046a185babda3 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/disabled/unenroll_agent_from_fleet.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/disabled/unenroll_agent_from_fleet.cy.ts @@ -20,7 +20,9 @@ import { enableAllPolicyProtections } from '../../../tasks/endpoint_policy'; import { createEndpointHost } from '../../../tasks/create_endpoint_host'; import { deleteAllLoadedEndpointData } from '../../../tasks/delete_all_endpoint_data'; -describe( +// FLAKY: https://github.com/elastic/kibana/issues/173463 +// FLAKY: https://github.com/elastic/kibana/issues/170814 +describe.skip( 'Unenroll agent from fleet with agent tamper protection is disabled', { tags: ['@ess'] }, () => { diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/disabled/uninstall_agent_from_host.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/disabled/uninstall_agent_from_host.cy.ts index ed47855ac894a..d5b018e9b03a4 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/disabled/uninstall_agent_from_host.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/disabled/uninstall_agent_from_host.cy.ts @@ -21,7 +21,8 @@ import { enableAllPolicyProtections } from '../../../tasks/endpoint_policy'; import { createEndpointHost } from '../../../tasks/create_endpoint_host'; import { deleteAllLoadedEndpointData } from '../../../tasks/delete_all_endpoint_data'; -describe( +// FLAKY: https://github.com/elastic/kibana/issues/173466 +describe.skip( 'Uninstall agent from host when agent tamper protection is disabled', { tags: ['@ess'] }, () => { diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/enabled/unenroll_agent_from_fleet.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/enabled/unenroll_agent_from_fleet.cy.ts index 17cb52c2cb042..cf37edfb0274b 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/enabled/unenroll_agent_from_fleet.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/enabled/unenroll_agent_from_fleet.cy.ts @@ -20,7 +20,9 @@ import { login } from '../../../tasks/login'; import { createEndpointHost } from '../../../tasks/create_endpoint_host'; import { deleteAllLoadedEndpointData } from '../../../tasks/delete_all_endpoint_data'; -describe( +// FLAKY: https://github.com/elastic/kibana/issues/173460 +// FLAKY: https://github.com/elastic/kibana/issues/170706 +describe.skip( 'Unenroll agent from fleet when agent tamper protection is enabled', { tags: ['@ess'] }, () => { diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/enabled/uninstall_agent_from_host.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/enabled/uninstall_agent_from_host.cy.ts index 527566bed608b..a7220e3bd7f1d 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/enabled/uninstall_agent_from_host.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/enabled/uninstall_agent_from_host.cy.ts @@ -22,7 +22,9 @@ import { login } from '../../../tasks/login'; import { createEndpointHost } from '../../../tasks/create_endpoint_host'; import { deleteAllLoadedEndpointData } from '../../../tasks/delete_all_endpoint_data'; -describe( +// FLAKY: https://github.com/elastic/kibana/issues/173471 +// FLAKY: https://github.com/elastic/kibana/issues/170601 +describe.skip( 'Uninstall agent from host when agent tamper protection is enabled', { tags: ['@ess'] }, () => { diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/switching_policies/unenroll_agent_from_fleet_changing_policy_from_disabled_to_enabled.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/switching_policies/unenroll_agent_from_fleet_changing_policy_from_disabled_to_enabled.cy.ts index 3d92528c2eee7..0bd503db67e99 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/switching_policies/unenroll_agent_from_fleet_changing_policy_from_disabled_to_enabled.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/switching_policies/unenroll_agent_from_fleet_changing_policy_from_disabled_to_enabled.cy.ts @@ -22,7 +22,9 @@ import { enableAllPolicyProtections } from '../../../tasks/endpoint_policy'; import { createEndpointHost } from '../../../tasks/create_endpoint_host'; import { deleteAllLoadedEndpointData } from '../../../tasks/delete_all_endpoint_data'; -describe( +// FLAKY: https://github.com/elastic/kibana/issues/173470 +// FLAKY: https://github.com/elastic/kibana/issues/170811 +describe.skip( 'Unenroll agent from fleet when agent tamper protection is disabled but then is switched to a policy with it enabled', { tags: ['@ess'] }, () => { diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/switching_policies/unenroll_agent_from_fleet_changing_policy_from_enabled_to_disabled.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/switching_policies/unenroll_agent_from_fleet_changing_policy_from_enabled_to_disabled.cy.ts index a9508a13f719b..d744abcb5b431 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/switching_policies/unenroll_agent_from_fleet_changing_policy_from_enabled_to_disabled.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/switching_policies/unenroll_agent_from_fleet_changing_policy_from_enabled_to_disabled.cy.ts @@ -22,7 +22,7 @@ import { enableAllPolicyProtections } from '../../../tasks/endpoint_policy'; import { createEndpointHost } from '../../../tasks/create_endpoint_host'; import { deleteAllLoadedEndpointData } from '../../../tasks/delete_all_endpoint_data'; -describe( +describe.skip( 'Unenroll agent from fleet changing when agent tamper protection is enabled but then is switched to a policy with it disabled', { tags: ['@ess'] }, () => { diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/switching_policies/unenroll_agent_from_fleet_changing_policy_from_enabled_to_enabled.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/switching_policies/unenroll_agent_from_fleet_changing_policy_from_enabled_to_enabled.cy.ts index a5654734c15e4..55058897c4f95 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/switching_policies/unenroll_agent_from_fleet_changing_policy_from_enabled_to_enabled.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/switching_policies/unenroll_agent_from_fleet_changing_policy_from_enabled_to_enabled.cy.ts @@ -21,7 +21,9 @@ import { login } from '../../../tasks/login'; import { createEndpointHost } from '../../../tasks/create_endpoint_host'; import { deleteAllLoadedEndpointData } from '../../../tasks/delete_all_endpoint_data'; -describe( +// FLAKY: https://github.com/elastic/kibana/issues/170816 +// FLAKY: https://github.com/elastic/kibana/issues/173458 +describe.skip( 'Unenroll agent from fleet changing agent policy when agent tamper protection is enabled but then is switched to a policy with it also enabled', { tags: ['@ess'] }, () => { diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/switching_policies/uninstall_agent_from_host_changing_policy_from_disabled_to_enabled.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/switching_policies/uninstall_agent_from_host_changing_policy_from_disabled_to_enabled.cy.ts index bbb675cf56d5e..ebd08694cd985 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/switching_policies/uninstall_agent_from_host_changing_policy_from_disabled_to_enabled.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/switching_policies/uninstall_agent_from_host_changing_policy_from_disabled_to_enabled.cy.ts @@ -24,7 +24,9 @@ import { enableAllPolicyProtections } from '../../../tasks/endpoint_policy'; import { createEndpointHost } from '../../../tasks/create_endpoint_host'; import { deleteAllLoadedEndpointData } from '../../../tasks/delete_all_endpoint_data'; -describe( +// FLAKY: https://github.com/elastic/kibana/issues/173475 +// FLAKY: https://github.com/elastic/kibana/issues/170794 +describe.skip( 'Uninstall agent from host changing agent policy when agent tamper protection is disabled but then is switched to a policy with it enabled', { tags: ['@ess'] }, () => { diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/switching_policies/uninstall_agent_from_host_changing_policy_from_enabled_to_disabled.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/switching_policies/uninstall_agent_from_host_changing_policy_from_enabled_to_disabled.cy.ts index 0768c4a49ca39..d01b5cbfdd086 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/switching_policies/uninstall_agent_from_host_changing_policy_from_enabled_to_disabled.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/switching_policies/uninstall_agent_from_host_changing_policy_from_enabled_to_disabled.cy.ts @@ -23,7 +23,9 @@ import { enableAllPolicyProtections } from '../../../tasks/endpoint_policy'; import { createEndpointHost } from '../../../tasks/create_endpoint_host'; import { deleteAllLoadedEndpointData } from '../../../tasks/delete_all_endpoint_data'; -describe( +// FLAKY: https://github.com/elastic/kibana/issues/173457 +// FLAKY: https://github.com/elastic/kibana/issues/170604 +describe.skip( 'Uninstall agent from host changing agent policy when agent tamper protection is enabled but then is switched to a policy with it disabled', { tags: ['@ess'] }, () => { diff --git a/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/switching_policies/uninstall_agent_from_host_changing_policy_from_enabled_to_enabled.cy.ts b/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/switching_policies/uninstall_agent_from_host_changing_policy_from_enabled_to_enabled.cy.ts index d8630a50a83b9..379afba46b7fd 100644 --- a/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/switching_policies/uninstall_agent_from_host_changing_policy_from_enabled_to_enabled.cy.ts +++ b/x-pack/plugins/security_solution/public/management/cypress/e2e/tamper_protection/switching_policies/uninstall_agent_from_host_changing_policy_from_enabled_to_enabled.cy.ts @@ -23,7 +23,8 @@ import { login } from '../../../tasks/login'; import { createEndpointHost } from '../../../tasks/create_endpoint_host'; import { deleteAllLoadedEndpointData } from '../../../tasks/delete_all_endpoint_data'; -describe( +// FLAKY: https://github.com/elastic/kibana/issues/173467 +describe.skip( 'Uninstall agent from host changing agent policy when agent tamper protection is enabled but then is switched to a policy with it also enabled', { tags: ['@ess'] }, () => { diff --git a/x-pack/plugins/security_solution/public/management/pages/blocklist/view/components/blocklist_form.test.tsx b/x-pack/plugins/security_solution/public/management/pages/blocklist/view/components/blocklist_form.test.tsx index e07dc6f2d081b..c7d1c3a46d442 100644 --- a/x-pack/plugins/security_solution/public/management/pages/blocklist/view/components/blocklist_form.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/blocklist/view/components/blocklist_form.test.tsx @@ -368,7 +368,7 @@ describe('blocklist form', () => { it('should default to global policy', () => { render(); - expect(screen.getByTestId('globalPolicy')).toBeEnabled(); + expect(screen.getByTestId('blocklist-form-effectedPolicies-global')).toBeEnabled(); }); it('should correctly edit policies', () => { @@ -383,7 +383,7 @@ describe('blocklist form', () => { }, ] as PolicyData[]; render(createProps({ policies })); - const byPolicyButton = screen.getByTestId('perPolicy'); + const byPolicyButton = screen.getByTestId('blocklist-form-effectedPolicies-perPolicy'); userEvent.click(byPolicyButton); expect(byPolicyButton).toBeEnabled(); @@ -408,9 +408,9 @@ describe('blocklist form', () => { }, ] as PolicyData[]; render(createProps({ policies, item: createItem({ tags: [`policy:${policies[1].id}`] }) })); - expect(screen.getByTestId('globalPolicy')).toBeEnabled(); + expect(screen.getByTestId('blocklist-form-effectedPolicies-global')).toBeEnabled(); - const byPolicyButton = screen.getByTestId('perPolicy'); + const byPolicyButton = screen.getByTestId('blocklist-form-effectedPolicies-perPolicy'); userEvent.click(byPolicyButton); expect(byPolicyButton).toBeEnabled(); userEvent.click(screen.getByText(policies[0].name)); diff --git a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form.test.tsx b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form.test.tsx index 2870a67e7042c..168ecc6b84a72 100644 --- a/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/event_filters/view/components/form.test.tsx @@ -281,7 +281,7 @@ describe('Event filter form', () => { it('should display the policy list when "per policy" is selected', async () => { render(); - userEvent.click(renderResult.getByTestId('perPolicy')); + userEvent.click(renderResult.getByTestId('eventFilters-form-effectedPolicies-perPolicy')); rerenderWithLatestProps(); // policy selector should show up expect( @@ -308,17 +308,21 @@ describe('Event filter form', () => { it('should have global policy by default', async () => { render(); - expect(renderResult.getByTestId('globalPolicy')).toBeChecked(); - expect(renderResult.getByTestId('perPolicy')).not.toBeChecked(); + expect(renderResult.getByTestId('eventFilters-form-effectedPolicies-global')).toHaveAttribute( + 'aria-pressed', + 'true' + ); + expect( + renderResult.getByTestId('eventFilters-form-effectedPolicies-perPolicy') + ).toHaveAttribute('aria-pressed', 'false'); }); it('should retain the previous policy selection when switching from per-policy to global', async () => { formProps.item.tags = [formProps.policies.map((p) => `policy:${p.id}`)[0]]; render(); const policyId = formProps.policies[0].id; - // move to per-policy and select the first - userEvent.click(renderResult.getByTestId('perPolicy')); + userEvent.click(renderResult.getByTestId('eventFilters-form-effectedPolicies-perPolicy')); userEvent.click(renderResult.getByTestId(`policy-${policyId}`)); formProps.item.tags = formProps.onChange.mock.calls[0][0].item.tags; rerender(); @@ -328,7 +332,7 @@ describe('Event filter form', () => { expect(formProps.item.tags).toEqual([`policy:${policyId}`]); // move back to global - userEvent.click(renderResult.getByTestId('globalPolicy')); + userEvent.click(renderResult.getByTestId('eventFilters-form-effectedPolicies-global')); formProps.item.tags = ['policy:all']; rerenderWithLatestProps(); expect(formProps.item.tags).toEqual(['policy:all']); @@ -337,7 +341,7 @@ describe('Event filter form', () => { ).toBeFalsy(); // move back to per-policy - userEvent.click(renderResult.getByTestId('perPolicy')); + userEvent.click(renderResult.getByTestId('eventFilters-form-effectedPolicies-perPolicy')); formProps.item.tags = [`policy:${policyId}`]; rerender(); // on change called with the previous policy @@ -379,13 +383,17 @@ describe('Event filter form', () => { formProps.item.tags = ['policy:id-0']; rerender(); - expect(renderResult.queryByTestId('perPolicy')).not.toBeNull(); + expect( + renderResult.queryByTestId('eventFilters-form-effectedPolicies-perPolicy') + ).not.toBeNull(); expect(renderResult.getByTestId('policy-id-0').getAttribute('aria-disabled')).toBe('true'); }); it("allows the user to set the event filter entry to 'Global' in the edit option", () => { render(); - const globalButtonInput = renderResult.getByTestId('globalPolicy') as HTMLButtonElement; + const globalButtonInput = renderResult.getByTestId( + 'eventFilters-form-effectedPolicies-global' + ) as HTMLButtonElement; userEvent.click(globalButtonInput); formProps.item.tags = ['policy:all']; rerender(); diff --git a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/integration_tests/form.test.tsx b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/integration_tests/form.test.tsx index 268fa8ac3c47a..04b94c0262fea 100644 --- a/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/integration_tests/form.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/host_isolation_exceptions/view/components/integration_tests/form.test.tsx @@ -146,7 +146,9 @@ describe('When on the host isolation exceptions entry form', () => { }); it('should show policy as selected when user clicks on it', async () => { - userEvent.click(renderResult.getByTestId('perPolicy')); + userEvent.click( + renderResult.getByTestId('hostIsolationExceptions-form-effectedPolicies-perPolicy') + ); await clickOnEffectedPolicy(renderResult, testIdPrefix); await expect(isEffectedPolicySelected(renderResult, testIdPrefix)).resolves.toBe(true); @@ -154,20 +156,26 @@ describe('When on the host isolation exceptions entry form', () => { it('should retain the previous policy selection when switching from per-policy to global', async () => { // move to per-policy and select the first - userEvent.click(renderResult.getByTestId('perPolicy')); + userEvent.click( + renderResult.getByTestId('hostIsolationExceptions-form-effectedPolicies-perPolicy') + ); await clickOnEffectedPolicy(renderResult, testIdPrefix); await expect(isEffectedPolicySelected(renderResult, testIdPrefix)).resolves.toBe(true); // move back to global - userEvent.click(renderResult.getByTestId('globalPolicy')); + userEvent.click( + renderResult.getByTestId('hostIsolationExceptions-form-effectedPolicies-global') + ); expect( renderResult.queryByTestId(`${testIdPrefix}-effectedPolicies-policiesSelectable`) ).toBeFalsy(); // move back to per-policy - userEvent.click(renderResult.getByTestId('perPolicy')); + userEvent.click( + renderResult.getByTestId('hostIsolationExceptions-form-effectedPolicies-perPolicy') + ); await expect(isEffectedPolicySelected(renderResult, testIdPrefix)).resolves.toBe(true); }); }); diff --git a/x-pack/plugins/security_solution/public/management/pages/policy/models/advanced_policy_schema.ts b/x-pack/plugins/security_solution/public/management/pages/policy/models/advanced_policy_schema.ts index 4fe0e6eb7b95a..449090d781330 100644 --- a/x-pack/plugins/security_solution/public/management/pages/policy/models/advanced_policy_schema.ts +++ b/x-pack/plugins/security_solution/public/management/pages/policy/models/advanced_policy_schema.ts @@ -1162,6 +1162,17 @@ export const AdvancedPolicySchema: AdvancedPolicySchemaType[] = [ } ), }, + { + key: 'windows.advanced.events.callstacks.timeout_microseconds', + first_supported_version: '8.12', + documentation: i18n.translate( + 'xpack.securitySolution.endpoint.policy.advanced.windows.advanced.events.callstacks.timeout_microseconds', + { + defaultMessage: + 'Maximum runtime of inline callstack collection/enrichment. Default: 100000', + } + ), + }, { key: 'windows.advanced.artifacts.global.proxy_url', first_supported_version: '8.8', diff --git a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/form.test.tsx b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/form.test.tsx index cc0111f23c416..df4b06ecc2b9c 100644 --- a/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/form.test.tsx +++ b/x-pack/plugins/security_solution/public/management/pages/trusted_apps/view/components/form.test.tsx @@ -430,7 +430,9 @@ describe('Trusted apps form', () => { expect(renderResult.getByTestId('policy-id-0-checkbox')).toBeChecked(); }); it("allows the user to set the trusted app entry to 'Global' in the edit option", () => { - const globalButtonInput = renderResult.getByTestId('globalPolicy') as HTMLButtonElement; + const globalButtonInput = renderResult.getByTestId( + 'trustedApps-form-effectedPolicies-global' + ) as HTMLButtonElement; act(() => { fireEvent.click(globalButtonInput); }); diff --git a/x-pack/plugins/security_solution/public/overview/components/common.tsx b/x-pack/plugins/security_solution/public/overview/components/common.tsx index 7ffcc7e5a95e3..f9068a4507b75 100644 --- a/x-pack/plugins/security_solution/public/overview/components/common.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/common.tsx @@ -9,7 +9,7 @@ import { EuiButtonIcon, EuiPopover, EuiPopoverTitle, EuiText } from '@elastic/eu import React, { useCallback, useState } from 'react'; import * as i18n from './translations'; import { RiskScoreDocLink } from '../../explore/components/risk_score/risk_score_onboarding/risk_score_doc_link'; -import type { RiskScoreEntity } from '../../../common/risk_engine'; +import type { RiskScoreEntity } from '../../../common/entity_analytics/risk_engine'; export const RiskScoreInfoTooltip: React.FC<{ toolTipContent: React.ReactNode; diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/hooks/use_navigate_to_timeline.test.ts b/x-pack/plugins/security_solution/public/overview/components/detection_response/hooks/use_navigate_to_timeline.test.ts index 5e451f6c44cc8..1257e1e480298 100644 --- a/x-pack/plugins/security_solution/public/overview/components/detection_response/hooks/use_navigate_to_timeline.test.ts +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/hooks/use_navigate_to_timeline.test.ts @@ -8,7 +8,7 @@ import { renderHook } from '@testing-library/react-hooks'; import { useDeepEqualSelector } from '../../../../common/hooks/use_selector'; -import { updateProviders } from '../../../../timelines/store/timeline/actions'; +import { updateProviders } from '../../../../timelines/store/actions'; import { useNavigateToTimeline } from './use_navigate_to_timeline'; import * as mock from './mock_data'; diff --git a/x-pack/plugins/security_solution/public/overview/components/detection_response/hooks/use_navigate_to_timeline.tsx b/x-pack/plugins/security_solution/public/overview/components/detection_response/hooks/use_navigate_to_timeline.tsx index 762286e2177fc..40ac8b91cfd84 100644 --- a/x-pack/plugins/security_solution/public/overview/components/detection_response/hooks/use_navigate_to_timeline.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/detection_response/hooks/use_navigate_to_timeline.tsx @@ -20,7 +20,7 @@ import type { DataProvider, QueryOperator } from '../../../../../common/types/ti import { TimelineId } from '../../../../../common/types/timeline'; import { TimelineType } from '../../../../../common/api/timeline'; import { useCreateTimeline } from '../../../../timelines/components/timeline/properties/use_create_timeline'; -import { updateProviders } from '../../../../timelines/store/timeline/actions'; +import { updateProviders } from '../../../../timelines/store/actions'; import { sourcererSelectors } from '../../../../common/store'; import type { TimeRange } from '../../../../common/store/inputs/model'; diff --git a/x-pack/plugins/security_solution/public/overview/components/recent_timelines/index.tsx b/x-pack/plugins/security_solution/public/overview/components/recent_timelines/index.tsx index e6c8068c0a865..0e5b4d6903068 100644 --- a/x-pack/plugins/security_solution/public/overview/components/recent_timelines/index.tsx +++ b/x-pack/plugins/security_solution/public/overview/components/recent_timelines/index.tsx @@ -16,7 +16,7 @@ import { dispatchUpdateTimeline, } from '../../../timelines/components/open_timeline/helpers'; import type { OnOpenTimeline } from '../../../timelines/components/open_timeline/types'; -import { updateIsLoading as dispatchUpdateIsLoading } from '../../../timelines/store/timeline/actions'; +import { updateIsLoading as dispatchUpdateIsLoading } from '../../../timelines/store/actions'; import { RecentTimelines } from './recent_timelines'; import * as i18n from './translations'; diff --git a/x-pack/plugins/security_solution/public/threat_intelligence/use_investigate_in_timeline.ts b/x-pack/plugins/security_solution/public/threat_intelligence/use_investigate_in_timeline.ts index 69d626f05de91..c73855cf7d115 100644 --- a/x-pack/plugins/security_solution/public/threat_intelligence/use_investigate_in_timeline.ts +++ b/x-pack/plugins/security_solution/public/threat_intelligence/use_investigate_in_timeline.ts @@ -7,7 +7,7 @@ import { useCallback, useMemo } from 'react'; import { useDispatch } from 'react-redux'; -import { timelineDefaults } from '../timelines/store/timeline/defaults'; +import { timelineDefaults } from '../timelines/store/defaults'; import { APP_UI_ID } from '../../common/constants'; import type { DataProvider } from '../../common/types'; import { TimelineId } from '../../common/types/timeline'; @@ -15,7 +15,7 @@ import { TimelineType } from '../../common/api/timeline'; import { useDeepEqualSelector } from '../common/hooks/use_selector'; import { useKibana } from '../common/lib/kibana'; import { useStartTransaction } from '../common/lib/apm/use_start_transaction'; -import { timelineActions, timelineSelectors } from '../timelines/store/timeline'; +import { timelineActions, timelineSelectors } from '../timelines/store'; import { useCreateTimeline } from '../timelines/components/timeline/properties/use_create_timeline'; import type { CreateTimelineProps } from '../detections/components/alerts_table/types'; import { dispatchUpdateTimeline } from '../timelines/components/open_timeline/helpers'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/edit_data_provider/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/edit_data_provider/index.test.tsx index 8fd5dce2da0d2..edcadfb2d391a 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/edit_data_provider/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/edit_data_provider/index.test.tsx @@ -5,7 +5,7 @@ * 2.0. */ -import { render, screen } from '@testing-library/react'; +import { render, screen, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; @@ -42,7 +42,8 @@ describe('StatefulEditDataProvider', () => { ); - expect(screen.getByText(field)).toBeInTheDocument(); + const fieldWrapper = screen.getByTestId('field'); + expect(within(fieldWrapper).getByTestId('comboBoxSearchInput')).toHaveValue(field); }); test('it renders the expected placeholder for the current field when field is empty', () => { @@ -82,7 +83,8 @@ describe('StatefulEditDataProvider', () => { ); - expect(screen.getByText('is')).toBeInTheDocument(); + const fieldWrapper = screen.getByTestId('operator'); + expect(within(fieldWrapper).getByTestId('comboBoxSearchInput')).toHaveValue('is'); }); test('it renders the negated "is" operator in a humanized format when isExcluded is true', () => { @@ -102,7 +104,8 @@ describe('StatefulEditDataProvider', () => { ); - expect(screen.getByText('is not')).toBeInTheDocument(); + const fieldWrapper = screen.getByTestId('operator'); + expect(within(fieldWrapper).getByTestId('comboBoxSearchInput')).toHaveValue('is not'); }); test('it renders the "exists" operator in human-readable format', () => { @@ -122,7 +125,8 @@ describe('StatefulEditDataProvider', () => { ); - expect(screen.getByText('exists')).toBeInTheDocument(); + const fieldWrapper = screen.getByTestId('operator'); + expect(within(fieldWrapper).getByTestId('comboBoxSearchInput')).toHaveValue('exists'); }); test('it renders the negated "exists" operator in a humanized format when isExcluded is true', () => { @@ -142,7 +146,8 @@ describe('StatefulEditDataProvider', () => { ); - expect(screen.getByText('does not exist')).toBeInTheDocument(); + const fieldWrapper = screen.getByTestId('operator'); + expect(within(fieldWrapper).getByTestId('comboBoxSearchInput')).toHaveValue('does not exist'); }); test('it renders the "is one of" operator in human-readable format', () => { @@ -162,7 +167,8 @@ describe('StatefulEditDataProvider', () => { ); - expect(screen.getByText('is one of')).toBeInTheDocument(); + const fieldWrapper = screen.getByTestId('operator'); + expect(within(fieldWrapper).getByTestId('comboBoxSearchInput')).toHaveValue('is one of'); }); test('it renders the negated "is one of" operator in a humanized format when isExcluded is true', () => { @@ -182,7 +188,8 @@ describe('StatefulEditDataProvider', () => { ); - expect(screen.getByText('is not one of')).toBeInTheDocument(); + const fieldWrapper = screen.getByTestId('operator'); + expect(within(fieldWrapper).getByTestId('comboBoxSearchInput')).toHaveValue('is not one of'); }); test('it renders the current value when the operator is "is"', () => { @@ -244,7 +251,9 @@ describe('StatefulEditDataProvider', () => { /> ); - expect(screen.getByText('enter one or more values')).toBeInTheDocument(); + + const wrapper = screen.getByTestId('is-one-of-combobox-input'); + expect(within(wrapper).getByPlaceholderText('enter one or more values')).toBeInTheDocument(); }); test('it renders selected values when the type of value is an array and the operator is "is one of"', () => { @@ -326,8 +335,8 @@ describe('StatefulEditDataProvider', () => { ); - // EuiCombobox does not render placeholder text with placeholder tag - expect(screen.getByText('enter one or more values')).toBeInTheDocument(); + const wrapper = screen.getByTestId('is-one-of-combobox-input'); + expect(within(wrapper).getByPlaceholderText('enter one or more values')).toBeInTheDocument(); }); test('it does NOT render value when the operator is "exists"', () => { diff --git a/x-pack/plugins/security_solution/public/timelines/components/field_renderers/__snapshots__/field_renderers.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/field_renderers/__snapshots__/field_renderers.test.tsx.snap index bf7c1251f5202..2f11cdf57c3a6 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/field_renderers/__snapshots__/field_renderers.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/field_renderers/__snapshots__/field_renderers.test.tsx.snap @@ -2,15 +2,11 @@ exports[`Field Renderers #autonomousSystemRenderer it renders correctly against snapshot 1`] = ` - .c0 .euiPopover__anchor { - width: 100%; -} - -.c1 > span.euiToolTipAnchor { + .c0 > span.euiToolTipAnchor { display: block; } -.c1 > span.euiToolTipAnchor.eui-textTruncate { +.c0 > span.euiToolTipAnchor.eui-textTruncate { display: inline-block; } @@ -24,29 +20,25 @@ exports[`Field Renderers #autonomousSystemRenderer it renders correctly against class="" >
-
- - Test Org - + Test Org -
+
@@ -64,29 +56,25 @@ exports[`Field Renderers #autonomousSystemRenderer it renders correctly against class="" >
-
- - 12345 - + 12345 -
+
@@ -108,15 +96,11 @@ exports[`Field Renderers #dateRenderer it renders correctly against snapshot 1`] exports[`Field Renderers #hostIdRenderer it renders correctly against snapshot 1`] = ` - .c0 .euiPopover__anchor { - width: 100%; -} - -.c1 > span.euiToolTipAnchor { + .c0 > span.euiToolTipAnchor { display: block; } -.c1 > span.euiToolTipAnchor.eui-textTruncate { +.c0 > span.euiToolTipAnchor.eui-textTruncate { display: inline-block; } @@ -124,36 +108,32 @@ exports[`Field Renderers #hostIdRenderer it renders correctly against snapshot 1 class="" >
-
- - - raspberrypi - - + raspberrypi + -
+
@@ -163,15 +143,11 @@ exports[`Field Renderers #hostIdRenderer it renders correctly against snapshot 1 exports[`Field Renderers #hostNameRenderer it renders correctly against snapshot 1`] = ` - .c0 .euiPopover__anchor { - width: 100%; -} - -.c1 > span.euiToolTipAnchor { + .c0 > span.euiToolTipAnchor { display: block; } -.c1 > span.euiToolTipAnchor.eui-textTruncate { +.c0 > span.euiToolTipAnchor.eui-textTruncate { display: inline-block; } @@ -179,36 +155,32 @@ exports[`Field Renderers #hostNameRenderer it renders correctly against snapshot class="" >
-
- - - raspberrypi - - + raspberrypi + -
+
@@ -218,15 +190,11 @@ exports[`Field Renderers #hostNameRenderer it renders correctly against snapshot exports[`Field Renderers #locationRenderer it renders correctly against snapshot 1`] = ` - .c0 .euiPopover__anchor { - width: 100%; -} - -.c1 > span.euiToolTipAnchor { + .c0 > span.euiToolTipAnchor { display: block; } -.c1 > span.euiToolTipAnchor.eui-textTruncate { +.c0 > span.euiToolTipAnchor.eui-textTruncate { display: inline-block; } @@ -240,29 +208,25 @@ exports[`Field Renderers #locationRenderer it renders correctly against snapshot class="" >
-
- - New York - + New York -
+
@@ -276,29 +240,25 @@ exports[`Field Renderers #locationRenderer it renders correctly against snapshot class="" >
-
- - New York - + New York -
+
diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/flyout/__snapshots__/index.test.tsx.snap index 88bf414235613..76ff4c26321be 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/__snapshots__/index.test.tsx.snap @@ -62,6 +62,52 @@ exports[`Flyout rendering it renders correctly against snapshot 1`] = `
+
+
+
+ +
+
+
+
+ +
@@ -109,31 +155,21 @@ exports[`Flyout rendering it renders correctly against snapshot 1`] = ` data-test-subj="timeline-status" > - Unsaved + + + Unsaved + +
-
- -
diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/index.tsx index b2e3230490ce7..2208b6ee706b8 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/index.tsx @@ -7,6 +7,7 @@ import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; import React from 'react'; +import styled from 'styled-components'; import { useKibana } from '../../../../common/lib/kibana/kibana_react'; import { APP_ID } from '../../../../../common'; import type { TimelineTabs } from '../../../../../common/types'; @@ -25,6 +26,12 @@ interface TimelineActionMenuProps { activeTab: TimelineTabs; } +const VerticalDivider = styled.span` + width: 0px; + height: 20px; + border-left: 1px solid ${({ theme }) => theme.eui.euiColorLightShade}; +`; + const TimelineActionMenuComponent = ({ mode = 'normal', timelineId, @@ -49,11 +56,6 @@ const TimelineActionMenuComponent = ({ - {userCasesPermissions.create && userCasesPermissions.read ? ( - - - - ) : null} + {userCasesPermissions.create && userCasesPermissions.read ? ( + <> + + + + + + + + ) : null} diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/save_timeline_modal.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/save_timeline_modal.tsx index 10154901f2db7..8028eb92eac7b 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/save_timeline_modal.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/action_menu/save_timeline_modal.tsx @@ -27,7 +27,7 @@ import { getUseField, Field, Form, useForm } from '../../../../shared_imports'; import { TimelineId } from '../../../../../common/types/timeline'; import { TimelineStatus, TimelineType } from '../../../../../common/api/timeline'; import { useDeepEqualSelector } from '../../../../common/hooks/use_selector'; -import { timelineActions, timelineSelectors } from '../../../store/timeline'; +import { timelineActions, timelineSelectors } from '../../../store'; import * as commonI18n from '../../timeline/properties/translations'; import * as i18n from './translations'; import { useStartTransaction } from '../../../../common/lib/apm/use_start_transaction'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/add_timeline_button/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/add_timeline_button/index.test.tsx index 80bd77bb81096..efae77571138d 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/add_timeline_button/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/add_timeline_button/index.test.tsx @@ -52,11 +52,11 @@ jest.mock('../../../containers/all', () => { }); jest.mock('../../timeline/properties/new_template_timeline', () => ({ - NewTemplateTimeline: jest.fn(() =>
{'Create new timeline template'}
), + NewTemplateTimeline: jest.fn(() =>
{'Create new Timeline template'}
), })); jest.mock('../../timeline/properties/helpers', () => ({ - NewTimeline: jest.fn().mockReturnValue(
{'Create new timeline'}
), + NewTimeline: jest.fn().mockReturnValue(
{'Create new Timeline'}
), })); jest.mock('../../../../common/containers/source', () => ({ diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/add_timeline_button/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/add_timeline_button/index.tsx index 69b71adb9fb6e..2c9e537ce4b06 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/add_timeline_button/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/add_timeline_button/index.tsx @@ -39,10 +39,10 @@ const AddTimelineButtonComponent: React.FC = ({ () => ( diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/add_to_case_button/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/add_to_case_button/index.tsx index 81e520368da6c..4eb701c5a8d98 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/add_to_case_button/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/add_to_case_button/index.tsx @@ -12,15 +12,15 @@ import { useDispatch } from 'react-redux'; import type { CaseUI } from '@kbn/cases-plugin/common'; import { APP_ID, APP_UI_ID } from '../../../../../common/constants'; -import { timelineSelectors } from '../../../store/timeline'; -import { setInsertTimeline, showTimeline } from '../../../store/timeline/actions'; +import { timelineSelectors } from '../../../store'; +import { setInsertTimeline, showTimeline } from '../../../store/actions'; import { useDeepEqualSelector } from '../../../../common/hooks/use_selector'; import { useKibana } from '../../../../common/lib/kibana'; import { TimelineId } from '../../../../../common/types/timeline'; import { TimelineStatus, TimelineType } from '../../../../../common/api/timeline'; import { getCreateCaseUrl, getCaseDetailsUrl } from '../../../../common/components/link_to'; import { SecurityPageName } from '../../../../app/types'; -import { timelineDefaults } from '../../../store/timeline/defaults'; +import { timelineDefaults } from '../../../store/defaults'; import * as i18n from '../../timeline/properties/translations'; interface Props { diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/active_timelines.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/active_timelines.tsx index cbc38f4ccecb9..9a863a5fe8184 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/active_timelines.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/active_timelines.tsx @@ -18,7 +18,7 @@ import { focusActiveTimelineButton, } from '../../timeline/helpers'; import { UNTITLED_TIMELINE, UNTITLED_TEMPLATE } from '../../timeline/properties/translations'; -import { timelineActions } from '../../../store/timeline'; +import { timelineActions } from '../../../store'; import * as i18n from './translations'; export interface ActiveTimelinesProps { diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx index 14158e884e1bb..cba27263d8dfe 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/index.tsx @@ -15,7 +15,7 @@ import { getEsQueryConfig } from '@kbn/data-plugin/common'; import { euiStyled } from '@kbn/kibana-react-plugin/common'; import { createHistoryEntry } from '../../../../common/utils/global_query_string/helpers'; import { useDeepEqualSelector } from '../../../../common/hooks/use_selector'; -import { timelineActions, timelineSelectors } from '../../../store/timeline'; +import { timelineActions, timelineSelectors } from '../../../store'; import type { State } from '../../../../common/store'; import { useKibana } from '../../../../common/lib/kibana'; import { useSourcererDataView } from '../../../../common/containers/sourcerer'; @@ -27,7 +27,8 @@ import * as i18n from './translations'; import { TimelineActionMenu } from '../action_menu'; import { AddToFavoritesButton } from '../../timeline/properties/helpers'; import { TimelineStatusInfo } from './timeline_status_info'; -import { timelineDefaults } from '../../../store/timeline/defaults'; +import { timelineDefaults } from '../../../store/defaults'; +import { AddTimelineButton } from '../add_timeline_button'; interface FlyoutHeaderPanelProps { timelineId: string; @@ -141,6 +142,14 @@ const FlyoutHeaderPanelComponent: React.FC = ({ timeline > + {!show ? ( + + + + ) : null} + + + = ({ timeline - - - {show && ( diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/selectors.ts b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/selectors.ts index 494489a1c7bfe..441ae5fed1a7e 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/selectors.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/selectors.ts @@ -8,7 +8,7 @@ import { createSelector } from 'reselect'; import { TimelineStatus } from '../../../../../common/api/timeline'; -import { timelineSelectors } from '../../../store/timeline'; +import { timelineSelectors } from '../../../store'; export const getTimelineStatusByIdSelector = () => createSelector(timelineSelectors.selectTimeline, (timeline) => ({ diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/timeline_status_info.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/timeline_status_info.test.tsx index f7fda862f793f..55285e18638cf 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/timeline_status_info.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/timeline_status_info.test.tsx @@ -28,18 +28,6 @@ describe('TestComponent', () => { it('should render the status correctly when timeline has unsaved changes', () => { render(); - expect(screen.getByText('Has unsaved changes')).toBeVisible(); - }); - - it('should render the status correctly when timeline is saved', () => { - const updatedTime = Date.now(); - render(); - expect(screen.getByText('Saved')).toBeVisible(); - }); - - it('should render the status correctly when timeline is saved some time ago', () => { - const updatedTime = Date.now() - 10000; - render(); - expect(screen.getByTestId('timeline-status')).toHaveTextContent(/Saved10 seconds ago/); + expect(screen.getByText('Unsaved changes')).toBeVisible(); }); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/timeline_status_info.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/timeline_status_info.tsx index ed164ddab47fc..ebd2f5bede310 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/timeline_status_info.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/timeline_status_info.tsx @@ -6,8 +6,7 @@ */ import React from 'react'; -import { EuiTextColor, EuiText } from '@elastic/eui'; -import { FormattedRelative } from '@kbn/i18n-react'; +import { EuiText, EuiBadge } from '@elastic/eui'; import styled from 'styled-components'; import { TimelineStatus } from '../../../../../common/api/timeline'; @@ -29,21 +28,13 @@ export const TimelineStatusInfo = React.memo( let statusContent: React.ReactNode = null; if (isUnsaved || !updated) { - statusContent = {i18n.UNSAVED}; + statusContent = {i18n.UNSAVED}; } else if (changed) { - statusContent = {i18n.UNSAVED_CHANGES}; - } else { - statusContent = ( - <> - {i18n.SAVED} - - - ); + statusContent = {i18n.UNSAVED_CHANGES}; } + + if (!statusContent) return null; + return ( {statusContent} diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/translations.ts b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/translations.ts index 56036f899e61f..f3e7306eae315 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/header/translations.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/header/translations.ts @@ -26,7 +26,7 @@ export const SAVED = i18n.translate('xpack.securitySolution.timeline.properties. export const UNSAVED_CHANGES = i18n.translate( 'xpack.securitySolution.timeline.properties.hasChangesLabel', { - defaultMessage: 'Has unsaved changes', + defaultMessage: 'Unsaved changes', } ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/index.test.tsx index 5d0e7eebc5eef..73454f6b54b6b 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/index.test.tsx @@ -12,7 +12,7 @@ import '../../../common/mock/react_beautiful_dnd'; import { TestProviders } from '../../../common/mock'; import { TimelineId } from '../../../../common/types/timeline'; -import * as timelineActions from '../../store/timeline/actions'; +import * as timelineActions from '../../store/actions'; import { Flyout } from '.'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/flyout/index.tsx index 2f9ff64c20970..13514d82cbe7f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/index.tsx @@ -16,7 +16,7 @@ import { FlyoutBottomBar } from './bottom_bar'; import { Pane } from './pane'; import { getTimelineShowStatusByIdSelector } from './selectors'; import { useTimelineSavePrompt } from '../../../common/hooks/timeline/use_timeline_save_prompt'; -import { timelineActions } from '../../store/timeline'; +import { timelineActions } from '../../store'; import { focusActiveTimelineButton } from '../timeline/helpers'; interface OwnProps { diff --git a/x-pack/plugins/security_solution/public/timelines/components/flyout/selectors.ts b/x-pack/plugins/security_solution/public/timelines/components/flyout/selectors.ts index df631c75a97d1..b02284aec6555 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/flyout/selectors.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/flyout/selectors.ts @@ -9,7 +9,7 @@ import { createSelector } from 'reselect'; import { TimelineTabs } from '../../../../common/types/timeline'; import { TimelineStatus } from '../../../../common/api/timeline'; -import { timelineSelectors } from '../../store/timeline'; +import { timelineSelectors } from '../../store'; export const getTimelineShowStatusByIdSelector = () => createSelector(timelineSelectors.selectTimeline, (timeline) => ({ diff --git a/x-pack/plugins/security_solution/public/timelines/components/formatted_ip/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/formatted_ip/index.test.tsx index b44fb4afc04ee..b7e0c782a9e6e 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/formatted_ip/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/formatted_ip/index.test.tsx @@ -11,7 +11,7 @@ import userEvent from '@testing-library/user-event'; import { FormattedIp } from '.'; import { TestProviders } from '../../../common/mock'; import { TimelineId, TimelineTabs } from '../../../../common/types/timeline'; -import { timelineActions } from '../../store/timeline'; +import { timelineActions } from '../../store'; import { activeTimeline } from '../../containers/active_timeline_context'; import { StatefulEventContext } from '../../../common/components/events_viewer/stateful_event_context'; @@ -44,8 +44,8 @@ jest.mock('../../../common/components/drag_and_drop/draggable_wrapper', () => { }; }); -jest.mock('../../store/timeline', () => { - const original = jest.requireActual('../../store/timeline'); +jest.mock('../../store', () => { + const original = jest.requireActual('../../store'); return { ...original, timelineActions: { diff --git a/x-pack/plugins/security_solution/public/timelines/components/graph_overlay/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/graph_overlay/index.tsx index 7b686f1bbc113..75c074c517758 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/graph_overlay/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/graph_overlay/index.tsx @@ -33,8 +33,8 @@ import { isFullScreen } from '../timeline/body/column_headers'; import { inputsActions } from '../../../common/store/actions'; import { Resolver } from '../../../resolver/view'; import { useTimelineDataFilters } from '../../containers/use_timeline_data_filters'; -import { timelineSelectors } from '../../store/timeline'; -import { timelineDefaults } from '../../store/timeline/defaults'; +import { timelineSelectors } from '../../store'; +import { timelineDefaults } from '../../store/defaults'; const SESSION_VIEW_FULL_SCREEN = 'sessionViewFullScreen'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/netflow/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/netflow/__snapshots__/index.test.tsx.snap index 8ad225711b773..919d237027815 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/netflow/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/netflow/__snapshots__/index.test.tsx.snap @@ -2,13 +2,13 @@ exports[`Netflow renders correctly against snapshot 1`] = ` - .c13 svg { + .c12 svg { position: relative; top: -1px; } -.c11, -.c11 * { +.c10, +.c10 * { display: inline-block; max-width: 100%; overflow: hidden; @@ -17,18 +17,14 @@ exports[`Netflow renders correctly against snapshot 1`] = ` white-space: nowrap; } -.c1 .euiPopover__anchor { - width: 100%; -} - -.c3 { +.c2 { border-radius: 2px; padding: 0 4px 0 8px; position: relative; z-index: 0 !important; } -.c3::before { +.c2::before { background-image: linear-gradient( 135deg, #535966 25%, transparent 25% ), linear-gradient( -135deg, #535966 25%, transparent 25% ), linear-gradient( 135deg, transparent 75%, #535966 75% ), linear-gradient( -135deg, transparent 75%, #535966 75% ); background-position: 0 0,1px 0,1px -1px,0px 1px; background-size: 2px 2px; @@ -41,147 +37,147 @@ exports[`Netflow renders correctly against snapshot 1`] = ` width: 4px; } -.c3:hover, -.c3:hover .euiBadge, -.c3:hover .euiBadge__text { +.c2:hover, +.c2:hover .euiBadge, +.c2:hover .euiBadge__text { cursor: move; cursor: -webkit-grab; cursor: -moz-grab; cursor: grab; } -.event-column-view:hover .c3::before, -tr:hover .c3::before { +.event-column-view:hover .c2::before, +tr:hover .c2::before { background-image: linear-gradient( 135deg, #98a2b3 25%, transparent 25% ), linear-gradient( -135deg, #98a2b3 25%, transparent 25% ), linear-gradient( 135deg, transparent 75%, #98a2b3 75% ), linear-gradient( -135deg, transparent 75%, #98a2b3 75% ); } -.c3:hover, -.c3:focus, -.event-column-view:hover .c3:hover, -.event-column-view:focus .c3:focus, -tr:hover .c3:hover, -tr:hover .c3:focus { +.c2:hover, +.c2:focus, +.event-column-view:hover .c2:hover, +.event-column-view:focus .c2:focus, +tr:hover .c2:hover, +tr:hover .c2:focus { background-color: #36a2ef; } -.c3:hover, -.c3:focus, -.event-column-view:hover .c3:hover, -.event-column-view:focus .c3:focus, -tr:hover .c3:hover, -tr:hover .c3:focus, -.c3:hover a, -.c3:focus a, -.event-column-view:hover .c3:hover a, -.event-column-view:focus .c3:focus a, -tr:hover .c3:hover a, -tr:hover .c3:focus a, -.c3:hover a:hover, -.c3:focus a:hover, -.event-column-view:hover .c3:hover a:hover, -.event-column-view:focus .c3:focus a:hover, -tr:hover .c3:hover a:hover, -tr:hover .c3:focus a:hover { +.c2:hover, +.c2:focus, +.event-column-view:hover .c2:hover, +.event-column-view:focus .c2:focus, +tr:hover .c2:hover, +tr:hover .c2:focus, +.c2:hover a, +.c2:focus a, +.event-column-view:hover .c2:hover a, +.event-column-view:focus .c2:focus a, +tr:hover .c2:hover a, +tr:hover .c2:focus a, +.c2:hover a:hover, +.c2:focus a:hover, +.event-column-view:hover .c2:hover a:hover, +.event-column-view:focus .c2:focus a:hover, +tr:hover .c2:hover a:hover, +tr:hover .c2:focus a:hover { color: #1d1e24; } -.c3:hover::before, -.c3:focus::before, -.event-column-view:hover .c3:hover::before, -.event-column-view:focus .c3:focus::before, -tr:hover .c3:hover::before, -tr:hover .c3:focus::before { +.c2:hover::before, +.c2:focus::before, +.event-column-view:hover .c2:hover::before, +.event-column-view:focus .c2:focus::before, +tr:hover .c2:hover::before, +tr:hover .c2:focus::before { background-image: linear-gradient( 135deg, #1d1e24 25%, transparent 25% ), linear-gradient( -135deg, #1d1e24 25%, transparent 25% ), linear-gradient( 135deg, transparent 75%, #1d1e24 75% ), linear-gradient( -135deg, transparent 75%, #1d1e24 75% ); } -.c2 { +.c1 { display: inline-block; max-width: 100%; } -.c2 [data-rbd-placeholder-context-id] { +.c1 [data-rbd-placeholder-context-id] { display: none !important; } -.c4 > span.euiToolTipAnchor { +.c3 > span.euiToolTipAnchor { display: block; } -.c4 > span.euiToolTipAnchor.eui-textTruncate { +.c3 > span.euiToolTipAnchor.eui-textTruncate { display: inline-block; } -.c5 { +.c4 { vertical-align: top; } -.c22 { +.c21 { margin-right: 5px; } -.c21 { +.c20 { margin-right: 5px; } -.c15 { +.c14 { position: relative; top: 1px; } -.c14 { +.c13 { margin-right: 5px; } -.c17 { +.c16 { background-color: #343741; height: 2.8px; width: 25px; } -.c20 { +.c19 { background-color: #343741; height: 2.2px; width: 25px; } -.c19 { +.c18 { margin-right: 5px; } -.c16 { +.c15 { margin: 0 2px; } -.c16 .euiToolTipAnchor { +.c15 .euiToolTipAnchor { white-space: nowrap; } -.c18 { +.c17 { margin: 0 5px; } -.c7 { +.c6 { margin-right: 3px; } -.c8 { +.c7 { margin: 0 5px; } -.c12 { +.c11 { margin: 0 3px; } -.c10 { +.c9 { font-weight: bold; margin-top: 2px; } -.c9 { +.c8 { margin-top: 3px; } -.c6 { +.c5 { margin-right: 3px; position: relative; top: -1px; @@ -216,69 +212,65 @@ tr:hover .c3:focus::before { class="" >
-
+ - - - first.last - + first.last -
+
@@ -294,69 +286,65 @@ tr:hover .c3:focus::before { class="" >
-
+ - - - rat - + rat -
+
@@ -381,63 +369,59 @@ tr:hover .c3:focus::before { class="" >
-
- -
+ - - -
- 1ms -
-
-
-
-
+ 1ms +
+ +
+
@@ -453,59 +437,55 @@ tr:hover .c3:focus::before { class="" >
-
- -
+ - - - Nov 12, 2018 @ 19:03:25.836 - -
-
-
+ Nov 12, 2018 @ 19:03:25.836 + +
+
@@ -521,59 +501,55 @@ tr:hover .c3:focus::before { class="" >
-
- -
+ - - - Nov 12, 2018 @ 19:03:25.936 - -
-
-
+ Nov 12, 2018 @ 19:03:25.936 + +
+
@@ -597,75 +573,71 @@ tr:hover .c3:focus::before { class="euiFlexGroup emotion-euiFlexGroup-responsive-none-center-center-row" >
-
+ - - - outgoing - + outgoing -
+
@@ -675,70 +647,66 @@ tr:hover .c3:focus::before {
-
- - http - + http -
+
@@ -748,63 +716,59 @@ tr:hover .c3:focus::before {
-
- -
- - 100B - -
-
+ + 100B + +
-
+
@@ -814,63 +778,59 @@ tr:hover .c3:focus::before {
-
- -
- - 3 pkts - -
-
+ + 3 pkts + +
-
+
@@ -880,70 +840,66 @@ tr:hover .c3:focus::before {
-
- - tcp - + tcp -
+
@@ -959,64 +915,60 @@ tr:hover .c3:focus::before { class="" >
-
- - we.live.in.a - + we.live.in.a -
+
@@ -1028,7 +980,7 @@ tr:hover .c3:focus::before {
Source
@@ -1079,58 +1031,54 @@ tr:hover .c3:focus::before { class="" >
-
- - - 192.168.1.2 - - + 192.168.1.2 + -
+
@@ -1149,7 +1097,7 @@ tr:hover .c3:focus::before { class="euiFlexItem emotion-euiFlexItem-growZero" > : @@ -1159,7 +1107,7 @@ tr:hover .c3:focus::before { class="euiFlexItem emotion-euiFlexItem-growZero" >
-
- - North America - + North America -
+
@@ -1264,7 +1208,7 @@ tr:hover .c3:focus::before {
-
- - United States - + United States -
+
@@ -1332,7 +1272,7 @@ tr:hover .c3:focus::before {
🇺🇸 @@ -1354,51 +1294,47 @@ tr:hover .c3:focus::before { class="" >
-
- - US - + US -
+
@@ -1410,7 +1346,7 @@ tr:hover .c3:focus::before {
-
- - Georgia - + Georgia -
+
@@ -1478,7 +1410,7 @@ tr:hover .c3:focus::before {
-
- - Atlanta - + Atlanta -
+
@@ -1553,7 +1481,7 @@ tr:hover .c3:focus::before {
@@ -1576,62 +1504,58 @@ tr:hover .c3:focus::before { class="" >
-
- -
- - (60.00%) - - - 60B - -
-
+ (60.00%) +
+ + 60B + +
-
+
@@ -1644,7 +1568,7 @@ tr:hover .c3:focus::before { class="euiFlexItem emotion-euiFlexItem-growZero" >
@@ -1655,57 +1579,53 @@ tr:hover .c3:focus::before { class="" >
-
- -
- - 2 pkts - -
-
+ + 2 pkts + +
-
+
@@ -1718,7 +1638,7 @@ tr:hover .c3:focus::before { class="euiFlexItem emotion-euiFlexItem-growZero" >
@@ -1752,7 +1672,7 @@ tr:hover .c3:focus::before { class="euiFlexItem emotion-euiFlexItem-growZero" >
@@ -1763,62 +1683,58 @@ tr:hover .c3:focus::before { class="" >
-
- -
- - (40.00%) - - - 40B - -
-
+ (40.00%) +
+ + 40B + +
-
+
@@ -1831,7 +1747,7 @@ tr:hover .c3:focus::before { class="euiFlexItem emotion-euiFlexItem-growZero" >
@@ -1842,57 +1758,53 @@ tr:hover .c3:focus::before { class="" >
-
- -
- - 1 pkts - -
-
+ + 1 pkts + +
-
+
@@ -1905,7 +1817,7 @@ tr:hover .c3:focus::before { class="euiFlexItem emotion-euiFlexItem-growZero" >
@@ -1934,7 +1846,7 @@ tr:hover .c3:focus::before { class="euiFlexItem emotion-euiFlexItem-growZero" >
Destination
@@ -1958,58 +1870,54 @@ tr:hover .c3:focus::before { class="" >
-
- - - 10.1.2.3 - - + 10.1.2.3 + -
+
@@ -2028,7 +1936,7 @@ tr:hover .c3:focus::before { class="euiFlexItem emotion-euiFlexItem-growZero" > : @@ -2038,7 +1946,7 @@ tr:hover .c3:focus::before { class="euiFlexItem emotion-euiFlexItem-growZero" >
-
- - North America - + North America -
+
@@ -2143,7 +2047,7 @@ tr:hover .c3:focus::before {
-
- - United States - + United States -
+
@@ -2211,7 +2111,7 @@ tr:hover .c3:focus::before {
🇺🇸 @@ -2233,51 +2133,47 @@ tr:hover .c3:focus::before { class="" >
-
- - US - + US -
+
@@ -2289,7 +2185,7 @@ tr:hover .c3:focus::before {
-
- - New York - + New York -
+
@@ -2357,7 +2249,7 @@ tr:hover .c3:focus::before {
-
- - New York - + New York -
+
@@ -2451,93 +2339,89 @@ tr:hover .c3:focus::before { class="" >
@@ -2553,93 +2437,89 @@ tr:hover .c3:focus::before { class="" >
@@ -2655,93 +2535,89 @@ tr:hover .c3:focus::before { class="" >
diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts index 6bd865e2750a2..7d789dbc46623 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts @@ -13,13 +13,13 @@ import { mockTimelineModel, mockGetOneTimelineResult, } from '../../../common/mock'; -import { timelineDefaults } from '../../store/timeline/defaults'; +import { timelineDefaults } from '../../store/defaults'; import { setTimelineRangeDatePicker as dispatchSetTimelineRangeDatePicker } from '../../../common/store/inputs/actions'; import { applyKqlFilterQuery as dispatchApplyKqlFilterQuery, addTimeline as dispatchAddTimeline, addNote as dispatchAddGlobalTimelineNote, -} from '../../store/timeline/actions'; +} from '../../store/actions'; import { addNotes as dispatchAddNotes, updateNote as dispatchUpdateNote, @@ -50,7 +50,7 @@ import { resolveTimeline } from '../../containers/api'; jest.mock('../../../common/store/inputs/actions'); jest.mock('../../../common/utils/normalize_time_range'); -jest.mock('../../store/timeline/actions'); +jest.mock('../../store/actions'); jest.mock('../../../common/store/app/actions'); jest.mock('uuid', () => { return { diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts index e4f5f8746cc0c..f7468d4958f04 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts @@ -38,9 +38,9 @@ import { applyKqlFilterQuery as dispatchApplyKqlFilterQuery, addTimeline as dispatchAddTimeline, addNote as dispatchAddGlobalTimelineNote, -} from '../../store/timeline/actions'; -import type { TimelineModel } from '../../store/timeline/model'; -import { timelineDefaults } from '../../store/timeline/defaults'; +} from '../../store/actions'; +import type { TimelineModel } from '../../store/model'; +import { timelineDefaults } from '../../store/defaults'; import { defaultColumnHeaderType, diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.tsx index 60f4dab89a2c1..dc2cca5104497 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/index.tsx @@ -18,12 +18,12 @@ import { SecurityPageName } from '../../../../common/constants'; import { useShallowEqualSelector } from '../../../common/hooks/use_selector'; import type { SortFieldTimeline } from '../../../../common/api/timeline'; import { TimelineId } from '../../../../common/types/timeline'; -import type { TimelineModel } from '../../store/timeline/model'; -import { timelineSelectors } from '../../store/timeline'; +import type { TimelineModel } from '../../store/model'; +import { timelineSelectors } from '../../store'; import { createTimeline as dispatchCreateNewTimeline, updateIsLoading as dispatchUpdateIsLoading, -} from '../../store/timeline/actions'; +} from '../../store/actions'; import { useGetAllTimeline } from '../../containers/all'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/note_previews/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/note_previews/index.tsx index 92011428edf0d..caff8fee65b2f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/note_previews/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/note_previews/index.tsx @@ -24,7 +24,7 @@ import { useMutation } from '@tanstack/react-query'; import type { TimelineResultNote } from '../types'; import { getEmptyValue, defaultToEmptyTag } from '../../../../common/components/empty_value'; import { MarkdownRenderer } from '../../../../common/components/markdown_editor'; -import { timelineActions, timelineSelectors } from '../../../store/timeline'; +import { timelineActions, timelineSelectors } from '../../../store'; import { appActions } from '../../../../common/store/app'; import { NOTE_CONTENT_CLASS_NAME } from '../../timeline/body/helpers'; import * as i18n from './translations'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline_modal/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline_modal/index.tsx index 9d803357058a0..75990ce7792d0 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline_modal/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/open_timeline_modal/index.tsx @@ -8,7 +8,7 @@ import { EuiModal } from '@elastic/eui'; import React from 'react'; -import type { TimelineModel } from '../../../store/timeline/model'; +import type { TimelineModel } from '../../../store/model'; import * as i18n from '../translations'; import type { ActionTimelineToShow } from '../types'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/types.ts b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/types.ts index 4a300c2c02ca1..e5a8abc612b8e 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/types.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/types.ts @@ -7,7 +7,7 @@ import type React from 'react'; import type { AllTimelinesVariables } from '../../containers/all'; -import type { TimelineModel } from '../../store/timeline/model'; +import type { TimelineModel } from '../../store/model'; import type { RowRendererId, SingleTimelineResolveResponse, diff --git a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/index.tsx index 05b1e7254b9dd..be33b84ca5f0c 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/row_renderers_browser/index.tsx @@ -25,9 +25,9 @@ import styled from 'styled-components'; import type { State } from '../../../common/store'; import { RowRendererId } from '../../../../common/api/timeline'; import { useDeepEqualSelector } from '../../../common/hooks/use_selector'; -import { setExcludedRowRendererIds as dispatchSetExcludedRowRendererIds } from '../../store/timeline/actions'; -import { timelineSelectors } from '../../store/timeline'; -import { timelineDefaults } from '../../store/timeline/defaults'; +import { setExcludedRowRendererIds as dispatchSetExcludedRowRendererIds } from '../../store/actions'; +import { timelineSelectors } from '../../store'; +import { timelineDefaults } from '../../store/defaults'; import { RowRenderersBrowser } from './row_renderers_browser'; import * as i18n from './translations'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/__snapshots__/index.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/side_panel/__snapshots__/index.test.tsx.snap index cfcbd3b1be2b2..8475b548dd795 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/__snapshots__/index.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/__snapshots__/index.test.tsx.snap @@ -75,63 +75,6 @@ Array [ class="euiFlexGroup emotion-euiFlexGroup-responsive-none-flexStart-center-row" />
-
-
-
-

- Assignees: -

-
-
-
-
-
-
-
- - - -
-
-
-
-
, @@ -265,63 +208,6 @@ exports[`Details Panel Component DetailsPanel:EventDetails: rendering it should class="euiFlexGroup emotion-euiFlexGroup-responsive-none-flexStart-center-row" />
-
-
-
-

- Assignees: -

-
-
-
-
-
-
-
- - - -
-
-
-
-
diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/expandable_event.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/expandable_event.tsx index 18cef99f0cb20..2b195eb11fe3c 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/expandable_event.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/event_details/expandable_event.tsx @@ -175,7 +175,7 @@ export const ExpandableEventTitle = React.memo( )} - {scopeId !== TableId.rulePreview && ( + {isAlert && scopeId !== TableId.rulePreview && ( { const original = jest.requireActual('react-redux'); return { diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/hooks/use_detail_panel.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/hooks/use_detail_panel.tsx index 5cde89c82e796..45f85294bfa37 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/hooks/use_detail_panel.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/hooks/use_detail_panel.tsx @@ -12,12 +12,12 @@ import { dataTableSelectors } from '@kbn/securitysolution-data-table'; import type { ExpandedDetailType } from '../../../../../common/types'; import { getScopedActions, isInTableScope, isTimelineScope } from '../../../../helpers'; import type { FlowTargetSourceDest } from '../../../../../common/search_strategy'; -import { timelineSelectors } from '../../../store/timeline'; +import { timelineSelectors } from '../../../store'; import { useSourcererDataView } from '../../../../common/containers/sourcerer'; import type { SourcererScopeName } from '../../../../common/store/sourcerer/model'; import { activeTimeline } from '../../../containers/active_timeline_context'; import { TimelineTabs, TimelineId } from '../../../../../common/types/timeline'; -import { timelineDefaults } from '../../../store/timeline/defaults'; +import { timelineDefaults } from '../../../store/defaults'; import { useDeepEqualSelector } from '../../../../common/hooks/use_selector'; import { DetailsPanel as DetailsPanelComponent } from '..'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/index.tsx index ad33fdfe1b73e..ae2cd149ef69d 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/index.tsx @@ -13,8 +13,8 @@ import { EuiFlyout } from '@elastic/eui'; import type { EntityType } from '@kbn/timelines-plugin/common'; import { dataTableActions, dataTableSelectors } from '@kbn/securitysolution-data-table'; import { getScopedActions, isInTableScope, isTimelineScope } from '../../../helpers'; -import { timelineSelectors } from '../../store/timeline'; -import { timelineDefaults } from '../../store/timeline/defaults'; +import { timelineSelectors } from '../../store'; +import { timelineDefaults } from '../../store/defaults'; import type { BrowserFields } from '../../../common/containers/source'; import type { RunTimeMappings } from '../../../common/store/sourcerer/model'; import { TimelineId, TimelineTabs } from '../../../../common/types/timeline'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/__mocks__/index.ts b/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/__mocks__/index.ts index 2bd47cd19f9ea..65c6bd974b83a 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/__mocks__/index.ts @@ -113,7 +113,6 @@ export const managedUserDetails: ManagedUserHits = { _index: 'test-index', _id: '123-test', }, - [ManagedUserDatasetKey.OKTA]: undefined, }; export const mockManagedUserData: ManagedUserData = { diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/columns.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/columns.tsx index 6fd22361c55cc..8c4f31ea12141 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/columns.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/columns.tsx @@ -10,9 +10,7 @@ import React, { useCallback } from 'react'; import { head } from 'lodash/fp'; import { euiLightVars } from '@kbn/ui-theme'; import type { EuiBasicTableColumn } from '@elastic/eui'; -import { EuiToolTip } from '@elastic/eui'; import { useDispatch } from 'react-redux'; -import { EcsFlat } from '@kbn/ecs'; import { SourcererScopeName } from '../../../../common/store/sourcerer/model'; import { DefaultFieldRenderer } from '../../field_renderers/field_renderers'; import type { @@ -36,16 +34,14 @@ const fieldColumn: EuiBasicTableColumn = { name: i18n.FIELD_COLUMN_TITLE, field: 'label', render: (label: string, { field }) => ( - - - {label ?? field} - - + + {label ?? field} + ), }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/hooks/use_managed_user.ts b/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/hooks/use_managed_user.ts index f8bfd378464c8..61e8b7e7e961c 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/hooks/use_managed_user.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/hooks/use_managed_user.ts @@ -7,7 +7,6 @@ import { useEffect, useMemo } from 'react'; import { useInstalledIntegrations } from '../../../../../detections/components/rules/related_integrations/use_installed_integrations'; -import { ManagedUserDatasetKey } from '../../../../../../common/search_strategy/security_solution/users/managed_details'; import { UsersQueries } from '../../../../../../common/search_strategy'; import { useSpaceId } from '../../../../../common/hooks/use_space_id'; import { useSearchStrategy } from '../../../../../common/containers/use_search_strategy'; @@ -37,10 +36,7 @@ export const useManagedUser = (userName: string, observedUser: ObserverUser) => } = useSearchStrategy({ factoryQueryType: UsersQueries.managedDetails, initialResult: { - users: { - [ManagedUserDatasetKey.ENTRA]: undefined, - [ManagedUserDatasetKey.OKTA]: undefined, - }, + users: {}, }, errorMessage: i18n.FAIL_MANAGED_USER, }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/managed_user.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/managed_user.test.tsx index 02a7721d22246..70363853b9101 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/managed_user.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/managed_user.test.tsx @@ -108,7 +108,6 @@ describe('ManagedUser', () => { managedUser: { ...mockManagedUserData, data: { - [ManagedUserDatasetKey.ENTRA]: undefined, [ManagedUserDatasetKey.OKTA]: { fields: mockOktaUserFields, _index: '123', diff --git a/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/managed_user.tsx b/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/managed_user.tsx index 42d059356081e..35a9a78011982 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/managed_user.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/side_panel/new_user_detail/managed_user.tsx @@ -136,7 +136,7 @@ export const ManagedUser = ({ managedUser={oktaManagedUser.fields} indexName={oktaManagedUser._index} eventId={oktaManagedUser._id} - tableType={UserAssetTableType.assetEntra} + tableType={UserAssetTableType.assetOkta} > ` - & .euiPopover__anchor { + & .euiPopover { padding-right: 8px; width: ${({ $width }) => $width}px; } diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header/index.test.tsx index 3f92df6827681..d80831d49abcc 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header/index.test.tsx @@ -8,7 +8,7 @@ import { mount, shallow } from 'enzyme'; import React from 'react'; -import { timelineActions } from '../../../../../store/timeline'; +import { timelineActions } from '../../../../../store'; import { TestProviders } from '../../../../../../common/mock'; import type { Sort } from '../../sort'; import { CloseButton } from '../actions'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header/index.tsx index 142d6f7894c22..99c11571f0c2d 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header/index.tsx @@ -15,7 +15,7 @@ import { useDeepEqualSelector, useShallowEqualSelector, } from '../../../../../../common/hooks/use_selector'; -import { timelineActions, timelineSelectors } from '../../../../../store/timeline'; +import { timelineActions, timelineSelectors } from '../../../../../store'; import type { OnFilterChange } from '../../../events'; import type { Sort } from '../../sort'; import { Actions } from '../actions'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header/selectors.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header/selectors.tsx index 52aae6ff69a59..cac232fd6ea34 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header/selectors.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/header/selectors.tsx @@ -7,7 +7,7 @@ import { createSelector } from 'reselect'; import { TimelineTabs } from '../../../../../../../common/types/timeline'; -import { selectTimeline } from '../../../../../store/timeline/selectors'; +import { selectTimeline } from '../../../../../store/selectors'; export const isEqlOnSelector = () => createSelector(selectTimeline, (timeline) => timeline?.activeTab === TimelineTabs.eql); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.test.tsx index b1f7ded5d5c3d..4042619404d27 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.test.tsx @@ -18,7 +18,7 @@ import { useMountAppended } from '../../../../../common/utils/use_mount_appended import type { ColumnHeadersComponentProps } from '.'; import { ColumnHeadersComponent } from '.'; import { cloneDeep } from 'lodash/fp'; -import { timelineActions } from '../../../../store/timeline'; +import { timelineActions } from '../../../../store'; import { TimelineId, TimelineTabs } from '../../../../../../common/types/timeline'; import { Direction } from '../../../../../../common/search_strategy'; import { getDefaultControlColumn } from '../control_columns'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.tsx index 37ac268073bcc..dbf5cf8e94ff6 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/column_headers/index.tsx @@ -11,7 +11,7 @@ import { Droppable } from '@hello-pangea/dnd'; import { useDispatch } from 'react-redux'; import type { ControlColumnProps, HeaderActionProps } from '../../../../../../common/types'; -import { removeColumn, upsertColumn } from '../../../../store/timeline/actions'; +import { removeColumn, upsertColumn } from '../../../../store/actions'; import { DragEffects } from '../../../../../common/components/drag_and_drop/draggable_wrapper'; import { DraggableFieldBadge } from '../../../../../common/components/draggables/field_badge'; import type { BrowserFields } from '../../../../../common/containers/source'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx index 42c34a5f06e0e..8faab92f5747f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/events/stateful_event.tsx @@ -29,13 +29,13 @@ import { useEventDetailsWidthContext } from '../../../../../common/components/ev import { EventColumnView } from './event_column_view'; import type { inputsModel } from '../../../../../common/store'; import { appSelectors } from '../../../../../common/store'; -import { timelineActions, timelineSelectors } from '../../../../store/timeline'; +import { timelineActions, timelineSelectors } from '../../../../store'; import { activeTimeline } from '../../../../containers/active_timeline_context'; import type { TimelineResultNote } from '../../../open_timeline/types'; import { getRowRenderer } from '../renderers/get_row_renderer'; import { StatefulRowRenderer } from './stateful_row_renderer'; import { NOTES_BUTTON_CLASS_NAME } from '../../properties/helpers'; -import { timelineDefaults } from '../../../../store/timeline/defaults'; +import { timelineDefaults } from '../../../../store/defaults'; import { useGetMappedNonEcsValue } from '../data_driven_columns'; import { StatefulEventContext } from '../../../../../common/components/events_viewer/stateful_event_context'; import type { diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx index 4ab99faba09c4..2cd9587a63de5 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.test.tsx @@ -31,7 +31,7 @@ import type { Props } from '.'; import { StatefulBody } from '.'; import type { Sort } from './sort'; import { getDefaultControlColumn } from './control_columns'; -import { timelineActions } from '../../../store/timeline'; +import { timelineActions } from '../../../store'; import { TimelineId, TimelineTabs } from '../../../../../common/types/timeline'; import { defaultRowRenderers } from './renderers'; import type { State } from '../../../../common/store'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.tsx index ac5d73fb7e085..0478b1cf90ad5 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/index.tsx @@ -24,8 +24,8 @@ import { RowRendererId } from '../../../../../common/api/timeline'; import type { BrowserFields } from '../../../../common/containers/source'; import type { TimelineItem } from '../../../../../common/search_strategy/timeline'; import type { inputsModel, State } from '../../../../common/store'; -import { timelineDefaults } from '../../../store/timeline/defaults'; -import { timelineActions } from '../../../store/timeline'; +import { timelineDefaults } from '../../../store/defaults'; +import { timelineActions } from '../../../store'; import type { OnRowSelected, OnSelectAll } from '../events'; import { getColumnHeaders } from './column_headers/helpers'; import { getEventIdToDataMapping } from './helpers'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/host_name.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/host_name.test.tsx index 63a254f4d2d6f..1535b05a97a4f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/host_name.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/host_name.test.tsx @@ -11,7 +11,7 @@ import { waitFor } from '@testing-library/react'; import { HostName } from './host_name'; import { TestProviders } from '../../../../../common/mock'; import { TimelineId, TimelineTabs } from '../../../../../../common/types/timeline'; -import { timelineActions } from '../../../../store/timeline'; +import { timelineActions } from '../../../../store'; import { activeTimeline } from '../../../../containers/active_timeline_context'; import { StatefulEventContext } from '../../../../../common/components/events_viewer/stateful_event_context'; import { createTelemetryServiceMock } from '../../../../../common/lib/telemetry/telemetry_service.mock'; @@ -44,8 +44,8 @@ jest.mock('../../../../../common/components/draggables', () => ({ DefaultDraggable: () =>
, })); -jest.mock('../../../../store/timeline', () => { - const original = jest.requireActual('../../../../store/timeline'); +jest.mock('../../../../store', () => { + const original = jest.requireActual('../../../../store'); return { ...original, timelineActions: { diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/netflow/__snapshots__/netflow_row_renderer.test.tsx.snap b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/netflow/__snapshots__/netflow_row_renderer.test.tsx.snap index e9f09d689f0cd..803a4f84552f2 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/netflow/__snapshots__/netflow_row_renderer.test.tsx.snap +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/netflow/__snapshots__/netflow_row_renderer.test.tsx.snap @@ -16,13 +16,13 @@ exports[`netflowRowRenderer renders correctly against snapshot 1`] = ` border-radius: 4px; } -.c15 svg { +.c14 svg { position: relative; top: -1px; } -.c13, -.c13 * { +.c12, +.c12 * { display: inline-block; max-width: 100%; overflow: hidden; @@ -31,18 +31,14 @@ exports[`netflowRowRenderer renders correctly against snapshot 1`] = ` white-space: nowrap; } -.c3 .euiPopover__anchor { - width: 100%; -} - -.c5 { +.c4 { border-radius: 2px; padding: 0 4px 0 8px; position: relative; z-index: 0 !important; } -.c5::before { +.c4::before { background-image: linear-gradient( 135deg, #535966 25%, transparent 25% ), linear-gradient( -135deg, #535966 25%, transparent 25% ), linear-gradient( 135deg, transparent 75%, #535966 75% ), linear-gradient( -135deg, transparent 75%, #535966 75% ); background-position: 0 0,1px 0,1px -1px,0px 1px; background-size: 2px 2px; @@ -55,147 +51,147 @@ exports[`netflowRowRenderer renders correctly against snapshot 1`] = ` width: 4px; } -.c5:hover, -.c5:hover .euiBadge, -.c5:hover .euiBadge__text { +.c4:hover, +.c4:hover .euiBadge, +.c4:hover .euiBadge__text { cursor: move; cursor: -webkit-grab; cursor: -moz-grab; cursor: grab; } -.event-column-view:hover .c5::before, -tr:hover .c5::before { +.event-column-view:hover .c4::before, +tr:hover .c4::before { background-image: linear-gradient( 135deg, #98a2b3 25%, transparent 25% ), linear-gradient( -135deg, #98a2b3 25%, transparent 25% ), linear-gradient( 135deg, transparent 75%, #98a2b3 75% ), linear-gradient( -135deg, transparent 75%, #98a2b3 75% ); } -.c5:hover, -.c5:focus, -.event-column-view:hover .c5:hover, -.event-column-view:focus .c5:focus, -tr:hover .c5:hover, -tr:hover .c5:focus { +.c4:hover, +.c4:focus, +.event-column-view:hover .c4:hover, +.event-column-view:focus .c4:focus, +tr:hover .c4:hover, +tr:hover .c4:focus { background-color: #36a2ef; } -.c5:hover, -.c5:focus, -.event-column-view:hover .c5:hover, -.event-column-view:focus .c5:focus, -tr:hover .c5:hover, -tr:hover .c5:focus, -.c5:hover a, -.c5:focus a, -.event-column-view:hover .c5:hover a, -.event-column-view:focus .c5:focus a, -tr:hover .c5:hover a, -tr:hover .c5:focus a, -.c5:hover a:hover, -.c5:focus a:hover, -.event-column-view:hover .c5:hover a:hover, -.event-column-view:focus .c5:focus a:hover, -tr:hover .c5:hover a:hover, -tr:hover .c5:focus a:hover { +.c4:hover, +.c4:focus, +.event-column-view:hover .c4:hover, +.event-column-view:focus .c4:focus, +tr:hover .c4:hover, +tr:hover .c4:focus, +.c4:hover a, +.c4:focus a, +.event-column-view:hover .c4:hover a, +.event-column-view:focus .c4:focus a, +tr:hover .c4:hover a, +tr:hover .c4:focus a, +.c4:hover a:hover, +.c4:focus a:hover, +.event-column-view:hover .c4:hover a:hover, +.event-column-view:focus .c4:focus a:hover, +tr:hover .c4:hover a:hover, +tr:hover .c4:focus a:hover { color: #1d1e24; } -.c5:hover::before, -.c5:focus::before, -.event-column-view:hover .c5:hover::before, -.event-column-view:focus .c5:focus::before, -tr:hover .c5:hover::before, -tr:hover .c5:focus::before { +.c4:hover::before, +.c4:focus::before, +.event-column-view:hover .c4:hover::before, +.event-column-view:focus .c4:focus::before, +tr:hover .c4:hover::before, +tr:hover .c4:focus::before { background-image: linear-gradient( 135deg, #1d1e24 25%, transparent 25% ), linear-gradient( -135deg, #1d1e24 25%, transparent 25% ), linear-gradient( 135deg, transparent 75%, #1d1e24 75% ), linear-gradient( -135deg, transparent 75%, #1d1e24 75% ); } -.c4 { +.c3 { display: inline-block; max-width: 100%; } -.c4 [data-rbd-placeholder-context-id] { +.c3 [data-rbd-placeholder-context-id] { display: none !important; } -.c6 > span.euiToolTipAnchor { +.c5 > span.euiToolTipAnchor { display: block; } -.c6 > span.euiToolTipAnchor.eui-textTruncate { +.c5 > span.euiToolTipAnchor.eui-textTruncate { display: inline-block; } -.c7 { +.c6 { vertical-align: top; } -.c24 { +.c23 { margin-right: 5px; } -.c23 { +.c22 { margin-right: 5px; } -.c9 { +.c8 { margin-right: 3px; } -.c10 { +.c9 { margin: 0 5px; } -.c19 { +.c18 { background-color: #343741; height: 2.8px; width: 25px; } -.c22 { +.c21 { background-color: #343741; height: 2.2px; width: 25px; } -.c21 { +.c20 { margin-right: 5px; } -.c18 { +.c17 { margin: 0 2px; } -.c18 .euiToolTipAnchor { +.c17 .euiToolTipAnchor { white-space: nowrap; } -.c20 { +.c19 { margin: 0 5px; } -.c17 { +.c16 { position: relative; top: 1px; } -.c16 { +.c15 { margin-right: 5px; } -.c14 { +.c13 { margin: 0 3px; } -.c12 { +.c11 { font-weight: bold; margin-top: 2px; } -.c11 { +.c10 { margin-top: 3px; } -.c8 { +.c7 { margin-right: 3px; position: relative; top: -1px; @@ -240,79 +236,75 @@ tr:hover .c5:focus::before { class="" >
-
+ user.name +

+ -

- user.name -

+ - - - first.last - + first.last -

- Press enter for options, or press space to begin dragging. -

-
+ +

+ Press enter for options, or press space to begin dragging. +

@@ -328,79 +320,75 @@ tr:hover .c5:focus::before { class="" >
-
+ process.name +

+ -

- process.name -

+ - - - rat - + rat -

- Press enter for options, or press space to begin dragging. -

-
+ +

+ Press enter for options, or press space to begin dragging. +

@@ -425,73 +413,69 @@ tr:hover .c5:focus::before { class="" >
-
+ event.duration +

+ -

- event.duration -

- -
+ - - -
- 1ms -
-
-
-
-

- Press enter for options, or press space to begin dragging. -

-
+ 1ms +
+ +
+ +

+ Press enter for options, or press space to begin dragging. +

@@ -507,69 +491,65 @@ tr:hover .c5:focus::before { class="" >
-
+ event.start +

+ -

- event.start -

- -
+ - - - Nov 12, 2018 @ 19:03:25.836 - -
-
-

- Press enter for options, or press space to begin dragging. -

-
+ Nov 12, 2018 @ 19:03:25.836 + +
+ +

+ Press enter for options, or press space to begin dragging. +

@@ -585,69 +565,65 @@ tr:hover .c5:focus::before { class="" >
-
+ event.end +

+ -

- event.end -

- -
+ - - - Nov 12, 2018 @ 19:03:25.936 - -
-
-

- Press enter for options, or press space to begin dragging. -

-
+ Nov 12, 2018 @ 19:03:25.936 + +
+ +

+ Press enter for options, or press space to begin dragging. +

@@ -671,85 +647,81 @@ tr:hover .c5:focus::before { class="euiFlexGroup emotion-euiFlexGroup-responsive-none-center-center-row" >
-
+ network.direction +

+ -

- network.direction -

+ - - - outgoing - + outgoing -

- Press enter for options, or press space to begin dragging. -

-
+ +

+ Press enter for options, or press space to begin dragging. +

@@ -759,80 +731,76 @@ tr:hover .c5:focus::before {
-
+ network.protocol +

+ -

- network.protocol -

- - http - + http -

- Press enter for options, or press space to begin dragging. -

-
+ +

+ Press enter for options, or press space to begin dragging. +

@@ -842,73 +810,69 @@ tr:hover .c5:focus::before {
-
+ network.bytes +

+ -

- network.bytes -

- -
- - 100B - -
-
+ + 100B + +
-

- Press enter for options, or press space to begin dragging. -

-
+ +

+ Press enter for options, or press space to begin dragging. +

@@ -918,73 +882,69 @@ tr:hover .c5:focus::before {
-
+ network.packets +

+ -

- network.packets -

- -
- - 3 pkts - -
-
+ + 3 pkts + +
-

- Press enter for options, or press space to begin dragging. -

-
+ +

+ Press enter for options, or press space to begin dragging. +

@@ -994,80 +954,76 @@ tr:hover .c5:focus::before {
-
+ network.transport +

+ -

- network.transport -

- - tcp - + tcp -

- Press enter for options, or press space to begin dragging. -

-
+ +

+ Press enter for options, or press space to begin dragging. +

@@ -1083,74 +1039,70 @@ tr:hover .c5:focus::before { class="" >
-
+ network.community_id +

+ -

- network.community_id -

- - we.live.in.a - + we.live.in.a -

- Press enter for options, or press space to begin dragging. -

-
+ +

+ Press enter for options, or press space to begin dragging. +

@@ -1162,7 +1114,7 @@ tr:hover .c5:focus::before {
Source
@@ -1213,68 +1165,64 @@ tr:hover .c5:focus::before { class="" >
-
+ source.ip +

+ -

- source.ip -

- - - 192.168.1.2 - - + 192.168.1.2 + -

- Press enter for options, or press space to begin dragging. -

-
+ +

+ Press enter for options, or press space to begin dragging. +

@@ -1293,7 +1241,7 @@ tr:hover .c5:focus::before { class="euiFlexItem emotion-euiFlexItem-growZero" > : @@ -1306,84 +1254,80 @@ tr:hover .c5:focus::before { class="" >
-
+ source.port +

+ -

- source.port -

- - - + External link + + + (opens in a new tab or window) + + +
-

- Press enter for options, or press space to begin dragging. -

-
+ +

+ Press enter for options, or press space to begin dragging. +

@@ -1405,7 +1349,7 @@ tr:hover .c5:focus::before { class="euiFlexGroup emotion-euiFlexGroup-responsive-none-flexStart-center-row" >
-
+ source.geo.continent_name +

+ -

- source.geo.continent_name -

- - North America - + North America -

- Press enter for options, or press space to begin dragging. -

-
+ +

+ Press enter for options, or press space to begin dragging. +

@@ -1483,7 +1423,7 @@ tr:hover .c5:focus::before {
-
+ source.geo.country_name +

+ -

- source.geo.country_name -

- - United States - + United States -

- Press enter for options, or press space to begin dragging. -

-
+ +

+ Press enter for options, or press space to begin dragging. +

@@ -1561,7 +1497,7 @@ tr:hover .c5:focus::before {
🇺🇸 @@ -1583,61 +1519,57 @@ tr:hover .c5:focus::before { class="" >
-
+ source.geo.country_iso_code +

+ -

- source.geo.country_iso_code -

- - US - + US -

- Press enter for options, or press space to begin dragging. -

-
+ +

+ Press enter for options, or press space to begin dragging. +

@@ -1649,7 +1581,7 @@ tr:hover .c5:focus::before {
-
+ source.geo.region_name +

+ -

- source.geo.region_name -

- - Georgia - + Georgia -

- Press enter for options, or press space to begin dragging. -

-
+ +

+ Press enter for options, or press space to begin dragging. +

@@ -1727,7 +1655,7 @@ tr:hover .c5:focus::before {
-
+ source.geo.city_name +

+ -

- source.geo.city_name -

- - Atlanta - + Atlanta -

- Press enter for options, or press space to begin dragging. -

-
+ +

+ Press enter for options, or press space to begin dragging. +

@@ -1812,7 +1736,7 @@ tr:hover .c5:focus::before {
@@ -1835,72 +1759,68 @@ tr:hover .c5:focus::before { class="" >
-
+ source.bytes +

+ -

- source.bytes -

- -
- - (60.00%) - - - 60B - -
-
+ (60.00%) +
+ + 60B + +
-

- Press enter for options, or press space to begin dragging. -

-
+ +

+ Press enter for options, or press space to begin dragging. +

@@ -1913,7 +1833,7 @@ tr:hover .c5:focus::before { class="euiFlexItem emotion-euiFlexItem-growZero" >
@@ -1924,67 +1844,63 @@ tr:hover .c5:focus::before { class="" >
-
+ source.packets +

+ -

- source.packets -

- -
- - 2 pkts - -
-
+ + 2 pkts + +
-

- Press enter for options, or press space to begin dragging. -

-
+ +

+ Press enter for options, or press space to begin dragging. +

@@ -1997,7 +1913,7 @@ tr:hover .c5:focus::before { class="euiFlexItem emotion-euiFlexItem-growZero" >
@@ -2031,7 +1947,7 @@ tr:hover .c5:focus::before { class="euiFlexItem emotion-euiFlexItem-growZero" >
@@ -2042,72 +1958,68 @@ tr:hover .c5:focus::before { class="" >
-
+ destination.bytes +

+ -

- destination.bytes -

- -
- - (40.00%) - - - 40B - -
-
+ (40.00%) +
+ + 40B + +
-

- Press enter for options, or press space to begin dragging. -

-
+ +

+ Press enter for options, or press space to begin dragging. +

@@ -2120,7 +2032,7 @@ tr:hover .c5:focus::before { class="euiFlexItem emotion-euiFlexItem-growZero" >
@@ -2131,67 +2043,63 @@ tr:hover .c5:focus::before { class="" >
-
+ destination.packets +

+ -

- destination.packets -

- -
- - 1 pkts - -
-
+ + 1 pkts + +
-

- Press enter for options, or press space to begin dragging. -

-
+ +

+ Press enter for options, or press space to begin dragging. +

@@ -2204,7 +2112,7 @@ tr:hover .c5:focus::before { class="euiFlexItem emotion-euiFlexItem-growZero" >
@@ -2233,7 +2141,7 @@ tr:hover .c5:focus::before { class="euiFlexItem emotion-euiFlexItem-growZero" >
Destination
@@ -2257,68 +2165,64 @@ tr:hover .c5:focus::before { class="" >
-
+ destination.ip +

+ -

- destination.ip -

- - - 10.1.2.3 - - + 10.1.2.3 + -

- Press enter for options, or press space to begin dragging. -

-
+ +

+ Press enter for options, or press space to begin dragging. +

@@ -2337,7 +2241,7 @@ tr:hover .c5:focus::before { class="euiFlexItem emotion-euiFlexItem-growZero" > : @@ -2350,84 +2254,80 @@ tr:hover .c5:focus::before { class="" >
-
+ destination.port +

+ -

- destination.port -

- - - + External link + + + (opens in a new tab or window) + + +
-

- Press enter for options, or press space to begin dragging. -

-
+ +

+ Press enter for options, or press space to begin dragging. +

@@ -2449,7 +2349,7 @@ tr:hover .c5:focus::before { class="euiFlexGroup emotion-euiFlexGroup-responsive-none-flexStart-center-row" >
-
+ destination.geo.continent_name +

+ -

- destination.geo.continent_name -

- - North America - + North America -

- Press enter for options, or press space to begin dragging. -

-
+ +

+ Press enter for options, or press space to begin dragging. +

@@ -2527,7 +2423,7 @@ tr:hover .c5:focus::before {
-
+ destination.geo.country_name +

+ -

- destination.geo.country_name -

- - United States - + United States -

- Press enter for options, or press space to begin dragging. -

-
+ +

+ Press enter for options, or press space to begin dragging. +

@@ -2605,7 +2497,7 @@ tr:hover .c5:focus::before {
🇺🇸 @@ -2627,61 +2519,57 @@ tr:hover .c5:focus::before { class="" >
-
+ destination.geo.country_iso_code +

+ -

- destination.geo.country_iso_code -

- - US - + US -

- Press enter for options, or press space to begin dragging. -

-
+ +

+ Press enter for options, or press space to begin dragging. +

@@ -2693,7 +2581,7 @@ tr:hover .c5:focus::before {
-
+ destination.geo.region_name +

+ -

- destination.geo.region_name -

- - New York - + New York -

- Press enter for options, or press space to begin dragging. -

-
+ +

+ Press enter for options, or press space to begin dragging. +

@@ -2771,7 +2655,7 @@ tr:hover .c5:focus::before {
-
+ destination.geo.city_name +

+ -

- destination.geo.city_name -

- - New York - + New York -

- Press enter for options, or press space to begin dragging. -

-
+ +

+ Press enter for options, or press space to begin dragging. +

@@ -2875,103 +2755,99 @@ tr:hover .c5:focus::before { class="" >
-
+ tls.fingerprints.ja3.hash +

+ -

- tls.fingerprints.ja3.hash -

+ - + ja3 + + + tls.fingerprints.ja3.hash-value - ja3 + External link - - tls.fingerprints.ja3.hash-value - - External link - - - (opens in a new tab or window) - - - + (opens in a new tab or window) + + -

- Press enter for options, or press space to begin dragging. -

-
+ +

+ Press enter for options, or press space to begin dragging. +

@@ -2987,103 +2863,99 @@ tr:hover .c5:focus::before { class="" >
@@ -3099,103 +2971,99 @@ tr:hover .c5:focus::before { class="" >
-
+ tls.server_certificate.fingerprint.sha1 +

+ -

- tls.server_certificate.fingerprint.sha1 -

+ - + server cert + + + tls.server_certificate.fingerprint.sha1-value - server cert + External link - - tls.server_certificate.fingerprint.sha1-value - - External link - - - (opens in a new tab or window) - - - + (opens in a new tab or window) + + -

- Press enter for options, or press space to begin dragging. -

-
+ +

+ Press enter for options, or press space to begin dragging. +

diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/user_name.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/user_name.test.tsx index a2183ec19c939..81ab0e35ee213 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/user_name.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/renderers/user_name.test.tsx @@ -10,7 +10,7 @@ import { waitFor } from '@testing-library/react'; import { TestProviders } from '../../../../../common/mock'; import { TimelineId, TimelineTabs } from '../../../../../../common/types/timeline'; -import { timelineActions } from '../../../../store/timeline'; +import { timelineActions } from '../../../../store'; import { activeTimeline } from '../../../../containers/active_timeline_context'; import { UserName } from './user_name'; import { StatefulEventContext } from '../../../../../common/components/events_viewer/stateful_event_context'; @@ -44,8 +44,8 @@ jest.mock('../../../../../common/components/draggables', () => ({ DefaultDraggable: () =>
, })); -jest.mock('../../../../store/timeline', () => { - const original = jest.requireActual('../../../../store/timeline'); +jest.mock('../../../../store', () => { + const original = jest.requireActual('../../../../store'); return { ...original, timelineActions: { diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/selectors/index.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/selectors/index.ts index e4c1f422886fa..26e2b11bd2b4b 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/body/selectors/index.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/body/selectors/index.ts @@ -7,7 +7,7 @@ import { createSelector } from 'reselect'; -import { getTimelineByIdSelector } from '../../../../store/timeline/selectors'; +import { getTimelineByIdSelector } from '../../../../store/selectors'; /** * This selector combines all the selectors used by the Timeline `StatefulBody`, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/add_data_provider_popover.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/add_data_provider_popover.tsx index bff85fdf28e46..5903d4bfe3022 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/add_data_provider_popover.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/add_data_provider_popover.tsx @@ -26,7 +26,7 @@ import { useDeepEqualSelector } from '../../../../common/hooks/use_selector'; import { StatefulEditDataProvider } from '../../edit_data_provider'; import { addContentToTimeline, getDisplayValue } from './helpers'; import { DataProviderType } from './data_provider'; -import { timelineSelectors } from '../../../store/timeline'; +import { timelineSelectors } from '../../../store'; import { ADD_FIELD_LABEL, ADD_TEMPLATE_FIELD_LABEL } from './translations'; interface AddDataProviderPopoverProps { diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/helpers.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/helpers.tsx index d96e98212f38d..53ac22d384a04 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/helpers.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/helpers.tsx @@ -9,7 +9,7 @@ import { omit } from 'lodash/fp'; import type { DraggableLocation } from '@hello-pangea/dnd'; import type { Dispatch } from 'redux'; -import { updateProviders } from '../../../store/timeline/actions'; +import { updateProviders } from '../../../store/actions'; import type { PrimitiveOrArrayOfPrimitives } from '../../../../common/lib/kuery'; import { isPrimitiveArray } from '../helpers'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/index.tsx index c712c1b5c8df5..175283e418246 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/index.tsx @@ -21,13 +21,13 @@ import { droppableTimelineProvidersPrefix } from '../../../../common/components/ import { Empty } from './empty'; import { Providers } from './providers'; -import { timelineSelectors } from '../../../store/timeline'; -import { timelineDefaults } from '../../../store/timeline/defaults'; +import { timelineSelectors } from '../../../store'; +import { timelineDefaults } from '../../../store/defaults'; import * as i18n from './translations'; import { options } from '../search_or_filter/helpers'; -import type { KqlMode } from '../../../store/timeline/model'; -import { updateKqlMode } from '../../../store/timeline/actions'; +import type { KqlMode } from '../../../store/model'; +import { updateKqlMode } from '../../../store/actions'; interface Props { timelineId: string; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_item_badge.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_item_badge.tsx index b40b15a331fa0..871a82a9beb29 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_item_badge.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/provider_item_badge.tsx @@ -15,7 +15,7 @@ import { useDeepEqualSelector, useShallowEqualSelector, } from '../../../../common/hooks/use_selector'; -import { timelineSelectors } from '../../../store/timeline'; +import { timelineSelectors } from '../../../store'; import type { PrimitiveOrArrayOfPrimitives } from '../../../../common/lib/kuery'; import type { OnDataProviderEdited } from '../events'; @@ -24,7 +24,7 @@ import { ProviderItemActions } from './provider_item_actions'; import type { DataProvidersAnd, QueryOperator } from './data_provider'; import { DataProviderType } from './data_provider'; import { dragAndDropActions } from '../../../../common/store/drag_and_drop'; -import { timelineDefaults } from '../../../store/timeline/defaults'; +import { timelineDefaults } from '../../../store/defaults'; interface ProviderItemBadgeProps { andProviderId?: string; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.test.tsx index a0be505921850..d6492c0d864b0 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.test.tsx @@ -11,7 +11,7 @@ import React from 'react'; import { TestProviders } from '../../../../common/mock/test_providers'; import { DroppableWrapper } from '../../../../common/components/drag_and_drop/droppable_wrapper'; -import { timelineActions } from '../../../store/timeline'; +import { timelineActions } from '../../../store'; import { mockDataProviders } from './mock/mock_data_providers'; import { Providers } from './providers'; import { DELETE_CLASS_NAME, ENABLE_CLASS_NAME, EXCLUDE_CLASS_NAME } from './provider_item_actions'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.tsx index 5d581a505742f..dd7eb5cf11051 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/data_providers/providers.tsx @@ -18,7 +18,7 @@ import { IS_DRAGGING_CLASS_NAME, } from '@kbn/securitysolution-t-grid'; import { useDraggableKeyboardWrapper } from '../../../../common/components/drag_and_drop/draggable_keyboard_wrapper_hook'; -import { timelineActions } from '../../../store/timeline'; +import { timelineActions } from '../../../store'; import { AndOrBadge } from '../../../../common/components/and_or_badge'; import { AddDataProviderPopover } from './add_data_provider_popover'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/eql_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/eql_tab_content/index.tsx index 63d20993174d6..f6d5465ab492c 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/eql_tab_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/eql_tab_content/index.tsx @@ -24,7 +24,7 @@ import { InPortal } from 'react-reverse-portal'; import type { ControlColumnProps } from '../../../../../common/types'; import { InputsModelId } from '../../../../common/store/inputs/constants'; -import { timelineActions, timelineSelectors } from '../../../store/timeline'; +import { timelineActions, timelineSelectors } from '../../../store'; import type { CellValueElementProps } from '../cell_rendering'; import type { TimelineItem } from '../../../../../common/search_strategy'; import { useTimelineEvents } from '../../../containers'; @@ -42,10 +42,10 @@ import { EventDetailsWidthProvider } from '../../../../common/components/events_ import type { inputsModel, State } from '../../../../common/store'; import { inputsSelectors } from '../../../../common/store'; import { SourcererScopeName } from '../../../../common/store/sourcerer/model'; -import { timelineDefaults } from '../../../store/timeline/defaults'; +import { timelineDefaults } from '../../../store/defaults'; import { useSourcererDataView } from '../../../../common/containers/sourcerer'; import { useEqlEventsCountPortal } from '../../../../common/hooks/use_timeline_events_count'; -import type { TimelineModel } from '../../../store/timeline/model'; +import type { TimelineModel } from '../../../store/model'; import { TimelineDatePickerLock } from '../date_picker_lock'; import { useTimelineFullScreen } from '../../../../common/containers/use_full_screen'; import { activeTimeline } from '../../../containers/active_timeline_context'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/esql_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/esql_tab_content/index.tsx index c5762e0f0a45d..e8a2a2cbc9efb 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/esql_tab_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/esql_tab_content/index.tsx @@ -25,11 +25,11 @@ import { useDiscoverState } from './use_discover_state'; import { SourcererScopeName } from '../../../../common/store/sourcerer/model'; import { useSetDiscoverCustomizationCallbacks } from './customizations/use_set_discover_customizations'; import { EmbeddedDiscoverContainer } from './styles'; -import { timelineSelectors } from '../../../store/timeline'; +import { timelineSelectors } from '../../../store'; import { useShallowEqualSelector } from '../../../../common/hooks/use_selector'; -import { timelineDefaults } from '../../../store/timeline/defaults'; +import { timelineDefaults } from '../../../store/defaults'; import { savedSearchComparator } from './utils'; -import { setIsDiscoverSavedSearchLoaded } from '../../../store/timeline/actions'; +import { setIsDiscoverSavedSearchLoaded } from '../../../store/actions'; import { GET_TIMELINE_DISCOVER_SAVED_SEARCH_TITLE } from './translations'; const HideSearchSessionIndicatorBreadcrumbIcon = createGlobalStyle` diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/index.tsx index b37139f6b8406..019a4a1ce4132 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/footer/index.tsx @@ -28,7 +28,7 @@ import { EVENTS_COUNT_BUTTON_CLASS_NAME } from '../helpers'; import * as i18n from './translations'; import { useEventDetailsWidthContext } from '../../../../common/components/events_viewer/event_details_width_context'; -import { timelineActions, timelineSelectors } from '../../../store/timeline'; +import { timelineActions, timelineSelectors } from '../../../store'; import { useDeepEqualSelector } from '../../../../common/hooks/use_selector'; import { useKibana } from '../../../../common/lib/kibana'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/graph_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/graph_tab_content/index.tsx index 85c68e823da1b..844dbc3712e59 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/graph_tab_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/graph_tab_content/index.tsx @@ -8,7 +8,7 @@ import React, { useMemo } from 'react'; import { EuiFlexItem } from '@elastic/eui'; import styled from 'styled-components'; -import { timelineSelectors } from '../../../store/timeline'; +import { timelineSelectors } from '../../../store'; import { useShallowEqualSelector } from '../../../../common/hooks/use_selector'; import type { TimelineId } from '../../../../../common/types/timeline'; import { GraphOverlay } from '../../graph_overlay'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx index 5ec2a07af5bb0..f1e8b55b7233b 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx @@ -13,13 +13,13 @@ import styled from 'styled-components'; import { isTab } from '@kbn/timelines-plugin/public'; import { useUserPrivileges } from '../../../common/components/user_privileges'; -import { timelineActions, timelineSelectors } from '../../store/timeline'; -import { timelineDefaults } from '../../store/timeline/defaults'; +import { timelineActions, timelineSelectors } from '../../store'; +import { timelineDefaults } from '../../store/defaults'; import { defaultHeaders } from './body/column_headers/default_headers'; import type { CellValueElementProps } from './cell_rendering'; import { SourcererScopeName } from '../../../common/store/sourcerer/model'; import { FlyoutHeaderPanel } from '../flyout/header'; -import type { TimelineId, RowRenderer } from '../../../../common/types/timeline'; +import type { TimelineId, RowRenderer, TimelineTabs } from '../../../../common/types/timeline'; import { TimelineType } from '../../../../common/api/timeline'; import { useDeepEqualSelector, useShallowEqualSelector } from '../../../common/hooks/use_selector'; import { activeTimeline } from '../../containers/active_timeline_context'; @@ -82,6 +82,7 @@ const StatefulTimelineComponent: React.FC = ({ initialized, show: isOpen, isLoading, + activeTab, } = useDeepEqualSelector((state) => pick( [ @@ -95,6 +96,7 @@ const StatefulTimelineComponent: React.FC = ({ 'initialized', 'show', 'isLoading', + 'activeTab', ], getTimeline(state, timelineId) ?? timelineDefaults ) @@ -195,6 +197,18 @@ const StatefulTimelineComponent: React.FC = ({ const showTimelineTour = isOpen && !isLoading && canEditTimeline; + const handleSwitchToTab = useCallback( + (tab: TimelineTabs) => { + dispatch( + timelineActions.setActiveTabTimeline({ + id: timelineId, + activeTab: tab, + }) + ); + }, + [timelineId, dispatch] + ); + return ( = ({ />
- {showTimelineTour ? : null} + {showTimelineTour ? ( + + ) : null} ); }; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/kpi/kpi_container.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/kpi/kpi_container.tsx index 31eb9ad5e5e53..c4f49c245e6d3 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/kpi/kpi_container.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/kpi/kpi_container.tsx @@ -19,8 +19,8 @@ import { SourcererScopeName } from '../../../../common/store/sourcerer/model'; import { TimelineKPIs } from './kpis'; import { useTimelineKpis } from '../../../containers/kpis'; import { useKibana } from '../../../../common/lib/kibana'; -import { timelineSelectors } from '../../../store/timeline'; -import { timelineDefaults } from '../../../store/timeline/defaults'; +import { timelineSelectors } from '../../../store'; +import { timelineDefaults } from '../../../store/defaults'; import { combineQueries } from '../../../../common/lib/kuery'; import { endSelector, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/notes_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/notes_tab_content/index.tsx index 963713a2317a6..0d794769b0887 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/notes_tab_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/notes_tab_content/index.tsx @@ -24,7 +24,7 @@ import styled from 'styled-components'; import type { EuiTheme } from '@kbn/react-kibana-context-styled'; import { useSourcererDataView } from '../../../../common/containers/sourcerer'; import { SourcererScopeName } from '../../../../common/store/sourcerer/model'; -import { timelineActions } from '../../../store/timeline'; +import { timelineActions } from '../../../store'; import { useDeepEqualSelector, useShallowEqualSelector, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/notes_tab_content/selectors.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/notes_tab_content/selectors.ts index bc0317f4c4282..57e727818d5f4 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/notes_tab_content/selectors.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/notes_tab_content/selectors.ts @@ -7,7 +7,7 @@ import { createSelector } from 'reselect'; -import { timelineSelectors } from '../../../store/timeline'; +import { timelineSelectors } from '../../../store'; export const getTimelineNoteSelector = () => createSelector(timelineSelectors.selectTimeline, (timeline) => { diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/pin/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/pin/index.tsx index 6e359920701f3..369fadfb82457 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/pin/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/pin/index.tsx @@ -54,7 +54,7 @@ export const Pin = React.memo( isTemplate, isPinned: pinned, }); - const pinAriaLabel = ariaLabel != null ? ariaLabel : defaultAriaLabel; + const pinAriaLabel = ariaLabel != null && !isTemplate ? ariaLabel : defaultAriaLabel; return ( = ({ return compact ? ( = ({ /> ) : ( createSelector( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.tsx index e1e732b32e479..73d83469413c4 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_bar/index.tsx @@ -20,13 +20,13 @@ import { useSourcererDataView } from '../../../../common/containers/sourcerer'; import { SourcererScopeName } from '../../../../common/store/sourcerer/model'; import { convertKueryToElasticSearchQuery } from '../../../../common/lib/kuery'; -import type { KqlMode } from '../../../store/timeline/model'; +import type { KqlMode } from '../../../store/model'; import { useSavedQueryServices } from '../../../../common/utils/saved_query_services'; import type { DispatchUpdateReduxTime } from '../../../../common/components/super_date_picker'; import { QueryBar } from '../../../../common/components/query_bar'; import type { DataProvider } from '../data_providers/data_provider'; import { buildGlobalQuery } from '../helpers'; -import { timelineActions } from '../../../store/timeline'; +import { timelineActions } from '../../../store'; import type { KueryFilterQuery, KueryFilterQueryKind } from '../../../../../common/types/timeline'; export interface QueryBarTimelineComponentProps { diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/header/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/header/index.tsx index 8d70c3e4d44c3..553b71705884b 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/header/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/header/index.tsx @@ -15,9 +15,9 @@ import { euiThemeVars } from '@kbn/ui-theme'; import type { TimelineStatusLiteralWithNull } from '../../../../../../common/api/timeline'; import { TimelineStatus, TimelineType } from '../../../../../../common/api/timeline'; -import { timelineSelectors } from '../../../../store/timeline'; +import { timelineSelectors } from '../../../../store'; import { useDeepEqualSelector } from '../../../../../common/hooks/use_selector'; -import { timelineDefaults } from '../../../../store/timeline/defaults'; +import { timelineDefaults } from '../../../../store/defaults'; import * as i18n from './translations'; import { StatefulSearchOrFilter } from '../../search_or_filter'; import { DataProviders } from '../../data_providers'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/header/selectors.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/header/selectors.ts index 02668c18b308c..62c0134b96612 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/header/selectors.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/header/selectors.ts @@ -7,7 +7,7 @@ import { createSelector } from 'reselect'; -import { timelineSelectors } from '../../../../store/timeline'; +import { timelineSelectors } from '../../../../store'; export const getTimelineSaveModalByIdSelector = () => createSelector(timelineSelectors.selectTimeline, (timeline) => ({ diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx index 390369eaf89ed..be05ff81f53c2 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/query_tab_content/index.tsx @@ -27,7 +27,7 @@ import { getEsQueryConfig } from '@kbn/data-plugin/common'; import type { ControlColumnProps } from '../../../../../common/types'; import { InputsModelId } from '../../../../common/store/inputs/constants'; import { useInvalidFilterQuery } from '../../../../common/hooks/use_invalid_filter_query'; -import { timelineActions, timelineSelectors } from '../../../store/timeline'; +import { timelineActions, timelineSelectors } from '../../../store'; import type { CellValueElementProps } from '../cell_rendering'; import type { Direction, TimelineItem } from '../../../../../common/search_strategy'; import { useTimelineEvents } from '../../../containers'; @@ -50,10 +50,10 @@ import { EventDetailsWidthProvider } from '../../../../common/components/events_ import type { inputsModel, State } from '../../../../common/store'; import { inputsSelectors } from '../../../../common/store'; import { SourcererScopeName } from '../../../../common/store/sourcerer/model'; -import { timelineDefaults } from '../../../store/timeline/defaults'; +import { timelineDefaults } from '../../../store/defaults'; import { useSourcererDataView } from '../../../../common/containers/sourcerer'; import { useTimelineEventsCountPortal } from '../../../../common/hooks/use_timeline_events_count'; -import type { TimelineModel } from '../../../store/timeline/model'; +import type { TimelineModel } from '../../../store/model'; import { useTimelineFullScreen } from '../../../../common/containers/use_full_screen'; import { activeTimeline } from '../../../containers/active_timeline_context'; import { DetailsPanel } from '../../side_panel'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/helpers.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/helpers.tsx index e8db433c0a117..282d94b873e83 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/helpers.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/helpers.tsx @@ -12,7 +12,7 @@ import styled from 'styled-components'; import { AndOrBadge } from '../../../../common/components/and_or_badge'; import * as i18n from './translations'; -import type { KqlMode } from '../../../store/timeline/model'; +import type { KqlMode } from '../../../store/model'; const AndOrContainer = styled.div` position: relative; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/index.tsx index 7dc5e7909093d..cdd6fc793082f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/index.tsx @@ -25,12 +25,12 @@ import { SourcererScopeName } from '../../../../common/store/sourcerer/model'; import { useSourcererDataView } from '../../../../common/containers/sourcerer'; import type { State, inputsModel } from '../../../../common/store'; import { inputsSelectors } from '../../../../common/store'; -import { timelineActions, timelineSelectors } from '../../../store/timeline'; -import type { KqlMode, TimelineModel } from '../../../store/timeline/model'; -import { timelineDefaults } from '../../../store/timeline/defaults'; +import { timelineActions, timelineSelectors } from '../../../store'; +import type { KqlMode, TimelineModel } from '../../../store/model'; +import { timelineDefaults } from '../../../store/defaults'; import { dispatchUpdateReduxTime } from '../../../../common/components/super_date_picker'; import { SearchOrFilter } from './search_or_filter'; -import { setDataProviderVisibility } from '../../../store/timeline/actions'; +import { setDataProviderVisibility } from '../../../store/actions'; import * as i18n from './translations'; const FilterItemsContainer = styled(EuiFlexGroup)``; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/search_or_filter.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/search_or_filter.tsx index e822dfb7e0dcf..16f053b4abb75 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/search_or_filter.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_or_filter/search_or_filter.tsx @@ -13,7 +13,7 @@ import type { Filter } from '@kbn/es-query'; import type { FilterManager } from '@kbn/data-plugin/public'; import { TimelineType } from '../../../../../common/api/timeline'; import { InputsModelId } from '../../../../common/store/inputs/constants'; -import type { KqlMode } from '../../../store/timeline/model'; +import type { KqlMode } from '../../../store/model'; import type { DispatchUpdateReduxTime } from '../../../../common/components/super_date_picker'; import { SuperDatePicker } from '../../../../common/components/super_date_picker'; import type { KueryFilterQuery } from '../../../../../common/types/timeline'; diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_super_select/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_super_select/index.tsx index a18176e523f1b..d7e8ae909c75f 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/search_super_select/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/search_super_select/index.tsx @@ -133,7 +133,7 @@ const SearchTimelineSuperSelectComponent: React.FC ( @@ -182,9 +182,9 @@ const ActiveTimelineTab = memo( timelineId={timelineId} /> - {isEsqlSettingEnabled && ( + {showTimeline && isEsqlSettingEnabled && activeTimelineTab === TimelineTabs.esql && ( diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/selectors.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/selectors.ts index 04045e94aee25..1d82a1e228032 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/selectors.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/selectors.ts @@ -8,7 +8,7 @@ import { createSelector } from 'reselect'; import { TimelineTabs } from '../../../../../common/types/timeline'; import { selectNotesById } from '../../../../common/store/app/selectors'; -import { selectTimeline } from '../../../store/timeline/selectors'; +import { selectTimeline } from '../../../store/selectors'; export const getActiveTabSelector = () => createSelector(selectTimeline, (timeline) => timeline?.activeTab ?? TimelineTabs.query); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tour/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tour/index.test.tsx index 04a753d76a68c..8f902d47d6ed1 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tour/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tour/index.test.tsx @@ -6,20 +6,44 @@ */ import React from 'react'; +import type { TimelineTourProps } from '.'; import { TimelineTour } from '.'; import { TIMELINE_TOUR_CONFIG_ANCHORS } from './step_config'; import { useIsElementMounted } from '../../../../detection_engine/rule_management_ui/components/rules_table/rules_table/guided_onboarding/use_is_element_mounted'; import { render, screen, waitFor, fireEvent } from '@testing-library/react'; -import { TestProviders } from '../../../../common/mock'; +import { + createSecuritySolutionStorageMock, + kibanaObservable, + mockGlobalState, + SUB_PLUGINS_REDUCER, + TestProviders, +} from '../../../../common/mock'; +import { TimelineTabs } from '../../../../../common/types'; +import { TimelineType } from '../../../../../common/api/timeline'; +import { createStore } from '../../../../common/store'; +import { useKibana as mockUseKibana } from '../../../../common/lib/kibana/__mocks__'; +import { useKibana } from '../../../../common/lib/kibana'; jest.mock( '../../../../detection_engine/rule_management_ui/components/rules_table/rules_table/guided_onboarding/use_is_element_mounted' ); +jest.mock('../../../../common/lib/kibana'); -const TestComponent = () => { +const mockedUseKibana = mockUseKibana(); + +const switchTabMock = jest.fn(); +const { storage: storageMock } = createSecuritySolutionStorageMock(); +const mockStore = createStore(mockGlobalState, SUB_PLUGINS_REDUCER, kibanaObservable, storageMock); + +const TestComponent = (props: Partial = {}) => { return ( - - + + {Object.values(TIMELINE_TOUR_CONFIG_ANCHORS).map((anchor) => { return
; })} @@ -32,6 +56,18 @@ describe('Timeline Tour', () => { (useIsElementMounted as jest.Mock).mockReturnValue(true); }); + beforeEach(() => { + (useKibana as jest.Mock).mockReturnValue({ + ...mockedUseKibana, + services: { + ...mockedUseKibana.services, + storage: storageMock, + }, + }); + + storageMock.clear(); + }); + it('should not render tour steps when element are not mounted', () => { (useIsElementMounted as jest.Mock).mockReturnValueOnce(false); render(); @@ -58,6 +94,37 @@ describe('Timeline Tour', () => { fireEvent.click(screen.getByText('Next')); + await waitFor(() => { + expect(screen.getByTestId('timeline-tour-step-4')).toBeVisible(); + }); + + fireEvent.click(screen.getByText('Next')); + + await waitFor(() => { + expect(screen.queryByText('Finish tour')).toBeVisible(); + }); + }); + + it('should render different tour steps when timeline type is template', async () => { + render(); + + await waitFor(() => { + expect(screen.getByTestId('timeline-tour-step-1')).toBeVisible(); + }); + + fireEvent.click(screen.getByText('Next')); + await waitFor(() => { + expect(screen.getByTestId('timeline-tour-step-2')).toBeVisible(); + }); + + fireEvent.click(screen.getByText('Next')); + + await waitFor(() => { + expect(screen.getByTestId('timeline-tour-step-3')).toBeVisible(); + }); + + fireEvent.click(screen.getByText('Next')); + await waitFor(() => { expect(screen.queryByText('Finish tour')).toBeVisible(); }); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tour/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tour/index.tsx index 96c703fb1b841..f959e6fe3a47a 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tour/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tour/index.tsx @@ -10,8 +10,10 @@ * * */ -import React, { useEffect, useCallback, useState } from 'react'; +import React, { useEffect, useCallback, useState, useMemo } from 'react'; import { EuiButton, EuiButtonEmpty, EuiTourStep } from '@elastic/eui'; +import type { TimelineType } from '../../../../../common/api/timeline'; +import type { TimelineTabs } from '../../../../../common/types'; import { useIsElementMounted } from '../../../../detection_engine/rule_management_ui/components/rules_table/rules_table/guided_onboarding/use_is_element_mounted'; import { NEW_FEATURES_TOUR_STORAGE_KEYS } from '../../../../../common/constants'; import { useKibana } from '../../../../common/lib/kibana'; @@ -25,14 +27,26 @@ interface TourState { tourSubtitle: string; } -const TimelineTourComp = () => { +export interface TimelineTourProps { + activeTab: TimelineTabs; + timelineType: TimelineType; + switchToTab: (tab: TimelineTabs) => void; +} + +const TimelineTourComp = (props: TimelineTourProps) => { + const { activeTab, switchToTab, timelineType } = props; const { services: { storage }, } = useKibana(); + const updatedTourSteps = useMemo( + () => + timelineTourSteps.filter((step) => !step.timelineType || step.timelineType === timelineType), + [timelineType] + ); + const [tourState, setTourState] = useState(() => { const restoredTourState = storage.get(NEW_FEATURES_TOUR_STORAGE_KEYS.TIMELINE); - if (restoredTourState != null) { return restoredTourState; } @@ -64,7 +78,7 @@ const TimelineTourComp = () => { const getFooterAction = useCallback( (step: number) => { // if it's the last step, we don't want to show the next button - return step === timelineTourSteps.length ? ( + return step === updatedTourSteps.length ? ( {i18n.TIMELINE_TOUR_FINISH} @@ -79,37 +93,45 @@ const TimelineTourComp = () => { ] ); }, - [finishTour, nextStep] + [finishTour, nextStep, updatedTourSteps.length] ); - const nextEl = timelineTourSteps[tourState.currentTourStep - 1]?.anchor; + const nextEl = updatedTourSteps[tourState.currentTourStep - 1]?.anchor; const isElementAtCurrentStepMounted = useIsElementMounted(nextEl); + const currentStepConfig = updatedTourSteps[tourState.currentTourStep - 1]; + + if (currentStepConfig?.timelineTab && currentStepConfig.timelineTab !== activeTab) { + switchToTab(currentStepConfig.timelineTab); + } + if (!tourState.isTourActive || !isElementAtCurrentStepMounted) { return null; } return ( <> - {timelineTourSteps.map((steps, idx) => { - if (tourState.currentTourStep !== idx + 1) return null; + {updatedTourSteps.map((steps, idx) => { + const stepCount = idx + 1; + if (tourState.currentTourStep !== stepCount) return null; + const panelProps = { + 'data-test-subj': `timeline-tour-step-${idx + 1}`, + }; return ( ); })} diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tour/step_config.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tour/step_config.tsx index fe668d4e5d9ec..265f87a61d0bc 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tour/step_config.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tour/step_config.tsx @@ -8,6 +8,8 @@ import { EuiText, EuiCode } from '@elastic/eui'; import React from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; +import { TimelineType } from '../../../../../common/api/timeline'; +import { TimelineTabs } from '../../../../../common/types'; import * as i18n from './translations'; export const TIMELINE_TOUR_CONFIG_ANCHORS = { @@ -15,17 +17,17 @@ export const TIMELINE_TOUR_CONFIG_ANCHORS = { DATA_VIEW: 'timeline-data-view', DATA_PROVIDER: 'toggle-data-provider', SAVE_TIMELINE: 'save-timeline-action', + ADD_TO_FAVORITES: 'add-to-favorites', }; export const timelineTourSteps = [ { - step: 1, title: i18n.TIMELINE_TOUR_TIMELINE_ACTIONS_STEP_TITLE, content: ( {i18n.TIMELINE_TOUR_NEW}, openButton: {i18n.TIMELINE_TOUR_OPEN}, @@ -36,13 +38,25 @@ export const timelineTourSteps = [ anchor: TIMELINE_TOUR_CONFIG_ANCHORS.ACTION_MENU, }, { - step: 2, + title: i18n.TIMELINE_TOUR_ADD_TO_FAVORITES_STEP_TITLE, + content: ( + + + + ), + anchor: TIMELINE_TOUR_CONFIG_ANCHORS.ADD_TO_FAVORITES, + }, + { + timelineTab: TimelineTabs.query, title: i18n.TIMELINE_TOUR_CHANGE_DATA_VIEW_TITLE, content: ( {i18n.TIMELINE_TOUR_DATA_VIEW}, }} @@ -52,19 +66,19 @@ export const timelineTourSteps = [ anchor: TIMELINE_TOUR_CONFIG_ANCHORS.DATA_VIEW, }, { - step: 3, + timelineType: TimelineType.default, + timelineTab: TimelineTabs.query, title: i18n.TIMELINE_TOUR_DATA_PROVIDER_VISIBILITY_TITLE, content: {i18n.TIMELINE_TOUR_DATA_PROVIDER_VISIBILITY_DESCRIPTION}, anchor: TIMELINE_TOUR_CONFIG_ANCHORS.DATA_PROVIDER, }, { - step: 4, title: i18n.TIMELINE_TOUR_SAVE_TIMELINE_STEP_TITLE, content: ( {i18n.TIMELINE_TOUR_SAVE}, editButton: {i18n.TIMELINE_TOUR_EDIT}, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tour/translations.ts b/x-pack/plugins/security_solution/public/timelines/components/timeline/tour/translations.ts index 3b51ad38a6763..3bb3b0e7eac00 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tour/translations.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tour/translations.ts @@ -31,7 +31,7 @@ export const TIMELINE_TOUR_DATA_PROVIDER_VISIBILITY_TITLE = i18n.translate( export const TIMELINE_TOUR_DATA_PROVIDER_VISIBILITY_DESCRIPTION = i18n.translate( 'xpack.securitySolution.timeline.tour.dataProviderToggle.description', { - defaultMessage: 'Click to expand or collapse the query builder', + defaultMessage: 'Click to expand or collapse the query builder.', } ); @@ -49,6 +49,13 @@ export const TIMELINE_TOUR_CHANGE_DATA_VIEW_TITLE = i18n.translate( } ); +export const TIMELINE_TOUR_ADD_TO_FAVORITES_STEP_TITLE = i18n.translate( + 'xpack.securitySolution.timeline.tour.addToFavorites.title', + { + defaultMessage: 'A new and intuitive way to favorite your Timeline', + } +); + export const TIMELINE_TOUR_NEXT = i18n.translate('xpack.securitySolution.timeline.tour.next', { defaultMessage: 'Next', }); diff --git a/x-pack/plugins/security_solution/public/timelines/containers/index.tsx b/x-pack/plugins/security_solution/public/timelines/containers/index.tsx index f7902dacf5e68..81252216c529d 100644 --- a/x-pack/plugins/security_solution/public/timelines/containers/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/containers/index.tsx @@ -23,7 +23,7 @@ import type { inputsModel } from '../../common/store'; import type { RunTimeMappings } from '../../common/store/sourcerer/model'; import { useKibana } from '../../common/lib/kibana'; import { createFilter } from '../../common/containers/helpers'; -import { timelineActions } from '../store/timeline'; +import { timelineActions } from '../store'; import { detectionsTimelineIds } from './helpers'; import { getInspectResponse } from '../../helpers'; import type { diff --git a/x-pack/plugins/security_solution/public/timelines/index.ts b/x-pack/plugins/security_solution/public/timelines/index.ts index 71c15088d1acf..91078fa0420b1 100644 --- a/x-pack/plugins/security_solution/public/timelines/index.ts +++ b/x-pack/plugins/security_solution/public/timelines/index.ts @@ -7,8 +7,8 @@ import type { SecuritySubPluginWithStore } from '../app/types'; import { routes } from './routes'; -import { initialTimelineState, timelineReducer } from './store/timeline/reducer'; -import type { TimelineState } from './store/timeline/types'; +import { initialTimelineState, timelineReducer } from './store/reducer'; +import type { TimelineState } from './store/types'; export class Timelines { public setup() {} diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts b/x-pack/plugins/security_solution/public/timelines/store/actions.ts similarity index 95% rename from x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts rename to x-pack/plugins/security_solution/public/timelines/store/actions.ts index 8440a534ad45d..8de7d6670e28e 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/actions.ts @@ -9,19 +9,19 @@ import actionCreatorFactory from 'typescript-fsa'; import type { Filter } from '@kbn/es-query'; import type { SavedSearch } from '@kbn/saved-search-plugin/common'; -import type { SessionViewConfig } from '../../../../common/types'; +import type { SessionViewConfig } from '../../../common/types'; import type { DataProvider, DataProviderType, QueryOperator, -} from '../../components/timeline/data_providers/data_provider'; +} from '../components/timeline/data_providers/data_provider'; import type { KqlMode, TimelineModel } from './model'; import type { InitialyzeTimelineSettings, InsertTimeline } from './types'; import type { FieldsEqlOptions, TimelineNonEcsData, -} from '../../../../common/search_strategy/timeline'; +} from '../../../common/search_strategy/timeline'; import type { TimelineTabs, TimelinePersistInput, @@ -29,9 +29,9 @@ import type { ToggleDetailPanel, ColumnHeaderOptions, SortColumnTimeline, -} from '../../../../common/types/timeline'; -import type { RowRendererId } from '../../../../common/api/timeline'; -import type { ResolveTimelineConfig } from '../../components/open_timeline/types'; +} from '../../../common/types/timeline'; +import type { RowRendererId } from '../../../common/api/timeline'; +import type { ResolveTimelineConfig } from '../components/open_timeline/types'; const actionCreator = actionCreatorFactory('x-pack/security_solution/local/timeline'); diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/defaults.ts b/x-pack/plugins/security_solution/public/timelines/store/defaults.ts similarity index 87% rename from x-pack/plugins/security_solution/public/timelines/store/timeline/defaults.ts rename to x-pack/plugins/security_solution/public/timelines/store/defaults.ts index 1df0a997a2d8e..9fb94d9f5b1b9 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/defaults.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/defaults.ts @@ -5,11 +5,11 @@ * 2.0. */ -import { TimelineTabs } from '../../../../common/types/timeline'; -import { TimelineType, TimelineStatus } from '../../../../common/api/timeline'; +import { TimelineTabs } from '../../../common/types/timeline'; +import { TimelineType, TimelineStatus } from '../../../common/api/timeline'; -import { defaultHeaders } from '../../components/timeline/body/column_headers/default_headers'; -import { normalizeTimeRange } from '../../../common/utils/normalize_time_range'; +import { defaultHeaders } from '../components/timeline/body/column_headers/default_headers'; +import { normalizeTimeRange } from '../../common/utils/normalize_time_range'; import type { SubsetTimelineModel, TimelineModel } from './model'; // normalizeTimeRange uses getTimeRangeSettings which cannot be used outside Kibana context if the uiSettings is not false diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.test.ts b/x-pack/plugins/security_solution/public/timelines/store/epic.test.ts similarity index 97% rename from x-pack/plugins/security_solution/public/timelines/store/timeline/epic.test.ts rename to x-pack/plugins/security_solution/public/timelines/store/epic.test.ts index 9a273bc6ca14a..bbcc9363a1072 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/epic.test.ts @@ -7,9 +7,9 @@ import type { Filter } from '@kbn/es-query'; import { FilterStateStore } from '@kbn/es-query'; -import { Direction } from '../../../../common/search_strategy'; -import { TimelineTabs } from '../../../../common/types/timeline'; -import { TimelineType, TimelineStatus } from '../../../../common/api/timeline'; +import { Direction } from '../../../common/search_strategy'; +import { TimelineTabs } from '../../../common/types/timeline'; +import { TimelineType, TimelineStatus } from '../../../common/api/timeline'; import { convertTimelineAsInput } from './epic'; import type { TimelineModel } from './model'; diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts b/x-pack/plugins/security_solution/public/timelines/store/epic.ts similarity index 96% rename from x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts rename to x-pack/plugins/security_solution/public/timelines/store/epic.ts index 7c072abce73e0..d3c14c3a1e2c5 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/epic.ts @@ -29,15 +29,15 @@ import { takeUntil, } from 'rxjs/operators'; -import type { TimelineErrorResponse, TimelineResponse } from '../../../../common/api/timeline'; -import type { ColumnHeaderOptions } from '../../../../common/types/timeline'; -import { TimelineStatus, TimelineType } from '../../../../common/api/timeline'; -import type { inputsModel } from '../../../common/store/inputs'; -import { addError } from '../../../common/store/app/actions'; +import type { TimelineErrorResponse, TimelineResponse } from '../../../common/api/timeline'; +import type { ColumnHeaderOptions } from '../../../common/types/timeline'; +import { TimelineStatus, TimelineType } from '../../../common/api/timeline'; +import type { inputsModel } from '../../common/store/inputs'; +import { addError } from '../../common/store/app/actions'; -import { copyTimeline, persistTimeline } from '../../containers/api'; -import { ALL_TIMELINE_QUERY_ID } from '../../containers/all'; -import * as i18n from '../../pages/translations'; +import { copyTimeline, persistTimeline } from '../containers/api'; +import { ALL_TIMELINE_QUERY_ID } from '../containers/all'; +import * as i18n from '../pages/translations'; import { updateTimeline, @@ -57,7 +57,7 @@ import { isNotNull } from './helpers'; import { dispatcherTimelinePersistQueue } from './epic_dispatcher_timeline_persistence_queue'; import { myEpicTimelineId } from './my_epic_timeline_id'; import type { TimelineEpicDependencies } from './types'; -import type { TimelineInput } from '../../../../common/search_strategy'; +import type { TimelineInput } from '../../../common/search_strategy'; const isItAtimelineAction = (timelineId: string | undefined) => timelineId && timelineId.toLowerCase().startsWith('timeline'); diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_changed.ts b/x-pack/plugins/security_solution/public/timelines/store/epic_changed.ts similarity index 100% rename from x-pack/plugins/security_solution/public/timelines/store/timeline/epic_changed.ts rename to x-pack/plugins/security_solution/public/timelines/store/epic_changed.ts diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_dispatcher_timeline_persistence_queue.ts b/x-pack/plugins/security_solution/public/timelines/store/epic_dispatcher_timeline_persistence_queue.ts similarity index 100% rename from x-pack/plugins/security_solution/public/timelines/store/timeline/epic_dispatcher_timeline_persistence_queue.ts rename to x-pack/plugins/security_solution/public/timelines/store/epic_dispatcher_timeline_persistence_queue.ts diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_favorite.ts b/x-pack/plugins/security_solution/public/timelines/store/epic_favorite.ts similarity index 92% rename from x-pack/plugins/security_solution/public/timelines/store/timeline/epic_favorite.ts rename to x-pack/plugins/security_solution/public/timelines/store/epic_favorite.ts index ff501fb4761de..c6bd77d222a6c 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_favorite.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/epic_favorite.ts @@ -12,7 +12,7 @@ import type { Observable } from 'rxjs'; import { from, EMPTY } from 'rxjs'; import { filter, mergeMap, withLatestFrom, startWith, takeUntil } from 'rxjs/operators'; -import { addError } from '../../../common/store/app/actions'; +import { addError } from '../../common/store/app/actions'; import { endTimelineSaving, updateIsFavorite, @@ -23,10 +23,10 @@ import { import { dispatcherTimelinePersistQueue } from './epic_dispatcher_timeline_persistence_queue'; import { myEpicTimelineId } from './my_epic_timeline_id'; import type { TimelineById } from './types'; -import type { inputsModel } from '../../../common/store/inputs'; -import type { ResponseFavoriteTimeline } from '../../../../common/api/timeline'; -import { TimelineType } from '../../../../common/api/timeline'; -import { persistFavorite } from '../../containers/api'; +import type { inputsModel } from '../../common/store/inputs'; +import type { ResponseFavoriteTimeline } from '../../../common/api/timeline'; +import { TimelineType } from '../../../common/api/timeline'; +import { persistFavorite } from '../containers/api'; type FavoriteTimelineAction = ReturnType; diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_note.ts b/x-pack/plugins/security_solution/public/timelines/store/epic_note.ts similarity index 93% rename from x-pack/plugins/security_solution/public/timelines/store/timeline/epic_note.ts rename to x-pack/plugins/security_solution/public/timelines/store/epic_note.ts index 01e612302ca31..98c4f90bfa1dd 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_note.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/epic_note.ts @@ -12,9 +12,9 @@ import type { Observable } from 'rxjs'; import { from, EMPTY } from 'rxjs'; import { filter, mergeMap, switchMap, withLatestFrom, startWith, takeUntil } from 'rxjs/operators'; -import { updateNote, addError } from '../../../common/store/app/actions'; -import type { NotesById } from '../../../common/store/app/model'; -import type { inputsModel } from '../../../common/store/inputs'; +import { updateNote, addError } from '../../common/store/app/actions'; +import type { NotesById } from '../../common/store/app/model'; +import type { inputsModel } from '../../common/store/inputs'; import { addNote, @@ -27,8 +27,8 @@ import { import { myEpicTimelineId } from './my_epic_timeline_id'; import { dispatcherTimelinePersistQueue } from './epic_dispatcher_timeline_persistence_queue'; import type { TimelineById } from './types'; -import { persistNote } from '../../containers/notes/api'; -import type { ResponseNote } from '../../../../common/api/timeline'; +import { persistNote } from '../containers/notes/api'; +import type { ResponseNote } from '../../../common/api/timeline'; type NoteAction = ReturnType; diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_pinned_event.ts b/x-pack/plugins/security_solution/public/timelines/store/epic_pinned_event.ts similarity index 94% rename from x-pack/plugins/security_solution/public/timelines/store/timeline/epic_pinned_event.ts rename to x-pack/plugins/security_solution/public/timelines/store/epic_pinned_event.ts index b99de117e785e..0808479bd5f24 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/epic_pinned_event.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/epic_pinned_event.ts @@ -12,9 +12,9 @@ import type { Observable } from 'rxjs'; import { from, EMPTY } from 'rxjs'; import { filter, mergeMap, startWith, withLatestFrom, takeUntil } from 'rxjs/operators'; -import { addError } from '../../../common/store/app/actions'; -import type { inputsModel } from '../../../common/store/inputs'; -import type { PinnedEventResponse } from '../../../../common/api/timeline'; +import { addError } from '../../common/store/app/actions'; +import type { inputsModel } from '../../common/store/inputs'; +import type { PinnedEventResponse } from '../../../common/api/timeline'; import { pinEvent, endTimelineSaving, @@ -26,7 +26,7 @@ import { import { myEpicTimelineId } from './my_epic_timeline_id'; import { dispatcherTimelinePersistQueue } from './epic_dispatcher_timeline_persistence_queue'; import type { TimelineById } from './types'; -import { persistPinnedEvent } from '../../containers/pinned_event/api'; +import { persistPinnedEvent } from '../containers/pinned_event/api'; type PinnedEventAction = ReturnType; diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.test.ts b/x-pack/plugins/security_solution/public/timelines/store/helpers.test.ts similarity index 98% rename from x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.test.ts rename to x-pack/plugins/security_solution/public/timelines/store/helpers.test.ts index e0ca1b6dc681d..2e3376396fb78 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/helpers.test.ts @@ -6,25 +6,20 @@ */ import { cloneDeep } from 'lodash/fp'; -import type { ColumnHeaderOptions } from '../../../../common/types/timeline'; -import { TimelineTabs, TimelineId } from '../../../../common/types/timeline'; -import { TimelineType, TimelineStatus } from '../../../../common/api/timeline'; - +import type { ColumnHeaderOptions } from '../../../common/types/timeline'; +import { TimelineTabs, TimelineId } from '../../../common/types/timeline'; +import { TimelineType, TimelineStatus } from '../../../common/api/timeline'; import type { DataProvider, DataProvidersAnd, -} from '../../components/timeline/data_providers/data_provider'; -import { - IS_OPERATOR, - DataProviderType, -} from '../../components/timeline/data_providers/data_provider'; -import { defaultColumnHeaderType } from '../../components/timeline/body/column_headers/default_headers'; +} from '../components/timeline/data_providers/data_provider'; +import { IS_OPERATOR, DataProviderType } from '../components/timeline/data_providers/data_provider'; +import { defaultColumnHeaderType } from '../components/timeline/body/column_headers/default_headers'; import { DEFAULT_COLUMN_MIN_WIDTH, RESIZED_COLUMN_MIN_WITH, -} from '../../components/timeline/body/constants'; -import { defaultHeaders } from '../../../common/mock'; - +} from '../components/timeline/body/constants'; +import { defaultHeaders } from '../../common/mock'; import { addNewTimeline, addTimelineProviders, @@ -49,12 +44,12 @@ import { import type { TimelineModel } from './model'; import { timelineDefaults } from './defaults'; import type { TimelineById } from './types'; -import { Direction } from '../../../../common/search_strategy'; +import { Direction } from '../../../common/search_strategy'; import type { FilterManager } from '@kbn/data-plugin/public'; -jest.mock('../../../common/utils/normalize_time_range'); -jest.mock('../../../common/utils/default_date_settings', () => { - const actual = jest.requireActual('../../../common/utils/default_date_settings'); +jest.mock('../../common/utils/normalize_time_range'); +jest.mock('../../common/utils/default_date_settings', () => { + const actual = jest.requireActual('../../common/utils/default_date_settings'); return { ...actual, DEFAULT_FROM_MOMENT: new Date('2020-10-27T11:37:31.655Z'), diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts b/x-pack/plugins/security_solution/public/timelines/store/helpers.ts similarity index 97% rename from x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts rename to x-pack/plugins/security_solution/public/timelines/store/helpers.ts index ca890b9ac3ec4..3206bb96e89ad 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/helpers.ts @@ -6,24 +6,21 @@ */ import { getOr, omit, uniq, isEmpty, isEqualWith, cloneDeep, union } from 'lodash/fp'; - import { v4 as uuidv4 } from 'uuid'; - import type { Filter } from '@kbn/es-query'; - -import type { SessionViewConfig, ExpandedDetailTimeline } from '../../../../common/types'; -import type { TimelineNonEcsData } from '../../../../common/search_strategy'; -import type { Sort } from '../../components/timeline/body/sort'; +import type { SessionViewConfig, ExpandedDetailTimeline } from '../../../common/types'; +import type { TimelineNonEcsData } from '../../../common/search_strategy'; +import type { Sort } from '../components/timeline/body/sort'; import type { DataProvider, QueryOperator, QueryMatch, -} from '../../components/timeline/data_providers/data_provider'; +} from '../components/timeline/data_providers/data_provider'; import { DataProviderType, IS_OPERATOR, EXISTS_OPERATOR, -} from '../../components/timeline/data_providers/data_provider'; +} from '../components/timeline/data_providers/data_provider'; import type { ColumnHeaderOptions, TimelineEventsType, @@ -31,25 +28,22 @@ import type { TimelinePersistInput, ToggleDetailPanel, SortColumnTimeline, -} from '../../../../common/types/timeline'; -import type { RowRendererId, TimelineTypeLiteral } from '../../../../common/api/timeline'; -import { TimelineId } from '../../../../common/types/timeline'; -import { TimelineStatus, TimelineType } from '../../../../common/api/timeline'; -import { normalizeTimeRange } from '../../../common/utils/normalize_time_range'; +} from '../../../common/types/timeline'; +import type { RowRendererId, TimelineTypeLiteral } from '../../../common/api/timeline'; +import { TimelineId } from '../../../common/types/timeline'; +import { TimelineStatus, TimelineType } from '../../../common/api/timeline'; +import { normalizeTimeRange } from '../../common/utils/normalize_time_range'; import { getTimelineManageDefaults, timelineDefaults } from './defaults'; import type { KqlMode, TimelineModel } from './model'; import type { TimelineById, TimelineModelSettings } from './types'; -import { - DEFAULT_FROM_MOMENT, - DEFAULT_TO_MOMENT, -} from '../../../common/utils/default_date_settings'; +import { DEFAULT_FROM_MOMENT, DEFAULT_TO_MOMENT } from '../../common/utils/default_date_settings'; import { DEFAULT_COLUMN_MIN_WIDTH, RESIZED_COLUMN_MIN_WITH, -} from '../../components/timeline/body/constants'; -import { activeTimeline } from '../../containers/active_timeline_context'; -import type { ResolveTimelineConfig } from '../../components/open_timeline/types'; -import { getDisplayValue } from '../../components/timeline/data_providers/helpers'; +} from '../components/timeline/body/constants'; +import { activeTimeline } from '../containers/active_timeline_context'; +import type { ResolveTimelineConfig } from '../components/open_timeline/types'; +import { getDisplayValue } from '../components/timeline/data_providers/helpers'; export const isNotNull = (value: T | null): value is T => value !== null; interface AddTimelineNoteParams { diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/index.ts b/x-pack/plugins/security_solution/public/timelines/store/index.ts similarity index 100% rename from x-pack/plugins/security_solution/public/timelines/store/timeline/index.ts rename to x-pack/plugins/security_solution/public/timelines/store/index.ts diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/manage_timeline_id.tsx b/x-pack/plugins/security_solution/public/timelines/store/manage_timeline_id.tsx similarity index 100% rename from x-pack/plugins/security_solution/public/timelines/store/timeline/manage_timeline_id.tsx rename to x-pack/plugins/security_solution/public/timelines/store/manage_timeline_id.tsx diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts b/x-pack/plugins/security_solution/public/timelines/store/model.ts similarity index 96% rename from x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts rename to x-pack/plugins/security_solution/public/timelines/store/model.ts index 47e15be86e2a1..0bd6f4806f06b 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/model.ts @@ -8,11 +8,11 @@ import type { FilterManager } from '@kbn/data-plugin/public'; import type { Filter } from '@kbn/es-query'; import type { SavedSearch } from '@kbn/saved-search-plugin/common'; -import type { ExpandedDetailTimeline, SessionViewConfig } from '../../../../common/types'; +import type { ExpandedDetailTimeline, SessionViewConfig } from '../../../common/types'; import type { EqlOptionsSelected, TimelineNonEcsData, -} from '../../../../common/search_strategy/timeline'; +} from '../../../common/search_strategy/timeline'; import type { TimelineTabs, ScrollToTopEvent, @@ -21,14 +21,14 @@ import type { DataProvider, SerializedFilterQuery, TimelineEventsType, -} from '../../../../common/types/timeline'; +} from '../../../common/types/timeline'; import type { RowRendererId, TimelineStatus, TimelineType, PinnedEvent, -} from '../../../../common/api/timeline'; -import type { ResolveTimelineConfig } from '../../components/open_timeline/types'; +} from '../../../common/api/timeline'; +import type { ResolveTimelineConfig } from '../components/open_timeline/types'; export type KqlMode = 'filter' | 'search'; diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/my_epic_timeline_id.ts b/x-pack/plugins/security_solution/public/timelines/store/my_epic_timeline_id.ts similarity index 100% rename from x-pack/plugins/security_solution/public/timelines/store/timeline/my_epic_timeline_id.ts rename to x-pack/plugins/security_solution/public/timelines/store/my_epic_timeline_id.ts diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts b/x-pack/plugins/security_solution/public/timelines/store/reducer.ts similarity index 99% rename from x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts rename to x-pack/plugins/security_solution/public/timelines/store/reducer.ts index 6947b207874a7..1ae41833a14bb 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/reducer.ts @@ -106,7 +106,7 @@ import { import type { TimelineState } from './types'; import { EMPTY_TIMELINE_BY_ID } from './types'; -import { TimelineType } from '../../../../common/api/timeline'; +import { TimelineType } from '../../../common/api/timeline'; export const initialTimelineState: TimelineState = { timelineById: EMPTY_TIMELINE_BY_ID, diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/selectors.ts b/x-pack/plugins/security_solution/public/timelines/store/selectors.ts similarity index 97% rename from x-pack/plugins/security_solution/public/timelines/store/timeline/selectors.ts rename to x-pack/plugins/security_solution/public/timelines/store/selectors.ts index d4078373aaff1..f22dcb3b96fe8 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/selectors.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/selectors.ts @@ -6,7 +6,7 @@ */ import { createSelector } from 'reselect'; -import type { State } from '../../../common/store/types'; +import type { State } from '../../common/store/types'; import type { TimelineModel } from './model'; import type { InsertTimeline, TimelineById } from './types'; diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/types.ts b/x-pack/plugins/security_solution/public/timelines/store/types.ts similarity index 87% rename from x-pack/plugins/security_solution/public/timelines/store/timeline/types.ts rename to x-pack/plugins/security_solution/public/timelines/store/types.ts index 2d45367b937b8..741b906dcdebe 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/types.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/types.ts @@ -7,11 +7,11 @@ import type { FilterManager } from '@kbn/data-plugin/public'; import type { TableById } from '@kbn/securitysolution-data-table'; -import type { RootEpicDependencies } from '../../../common/store/epic'; -import type { ColumnHeaderOptions, SortColumnTimeline } from '../../../../common/types'; -import type { RowRendererId } from '../../../../common/api/timeline'; -import type { inputsModel } from '../../../common/store/inputs'; -import type { NotesById } from '../../../common/store/app/model'; +import type { RootEpicDependencies } from '../../common/store/epic'; +import type { ColumnHeaderOptions, SortColumnTimeline } from '../../../common/types'; +import type { RowRendererId } from '../../../common/api/timeline'; +import type { inputsModel } from '../../common/store/inputs'; +import type { NotesById } from '../../common/store/app/model'; import type { TimelineModel } from './model'; diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/roles_users/serverless/es_serverless_resources/roles.yml b/x-pack/plugins/security_solution/scripts/endpoint/common/roles_users/serverless/es_serverless_resources/roles.yml index 050e6db921095..8d469904160cd 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/roles_users/serverless/es_serverless_resources/roles.yml +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/roles_users/serverless/es_serverless_resources/roles.yml @@ -25,19 +25,19 @@ viewer: cluster: [] indices: - names: - - '.siem-signals*' - - '.lists-*' - - '.items-*' + - ".siem-signals*" + - ".lists-*" + - ".items-*" privileges: - - 'read' - - 'view_index_metadata' + - "read" + - "view_index_metadata" allow_restricted_indices: false - names: - - '.alerts*' - - '.preview.alerts*' + - ".alerts*" + - ".preview.alerts*" privileges: - - 'read' - - 'view_index_metadata' + - "read" + - "view_index_metadata" allow_restricted_indices: false - names: - apm-*-transaction* @@ -49,19 +49,21 @@ viewer: - packetbeat-* - winlogbeat-* - metrics-endpoint.metadata_current_* - - '.fleet-agents*' - - '.fleet-actions*' - - 'risk-score.risk-score-*' + - ".fleet-agents*" + - ".fleet-actions*" + - "risk-score.risk-score-*" + - ".asset-criticality.asset-criticality-*" privileges: - read applications: - - application: 'kibana-.kibana' + - application: "kibana-.kibana" privileges: - feature_ml.read - feature_siem.read - feature_siem.read_alerts - feature_siem.endpoint_list_read - feature_securitySolutionCases.read + - feature_securitySolutionAssistant.all - feature_actions.read - feature_builtInAlerts.read - feature_osquery.read @@ -71,7 +73,7 @@ viewer: - feature_graph.all - feature_maps.all - feature_visualize.all - resources: '*' + resources: "*" run_as: [] # modeled after t3_analyst @@ -79,14 +81,14 @@ editor: cluster: [] indices: - names: - - '.siem-signals*' - - '.lists-*' - - '.items-*' + - ".siem-signals*" + - ".lists-*" + - ".items-*" privileges: - - 'read' - - 'view_index_metadata' - - 'write' - - 'maintenance' + - "read" + - "view_index_metadata" + - "write" + - "maintenance" allow_restricted_indices: false - names: - apm-*-transaction* @@ -101,19 +103,24 @@ editor: - read - write - names: - - '.internal.alerts*' - - '.alerts*' - - '.internal.preview.alerts*' - - '.preview.alerts*' - - 'risk-score.risk-score-*' + - ".internal.alerts*" + - ".alerts*" + - ".internal.preview.alerts*" + - ".preview.alerts*" + - "risk-score.risk-score-*" + privileges: + - "read" + - "view_index_metadata" + - "write" + - "maintenance" + - names: + - ".asset-criticality.asset-criticality-*" privileges: - - 'read' - - 'view_index_metadata' - - 'write' - - 'maintenance' + - "read" + - "write" allow_restricted_indices: false applications: - - application: 'kibana-.kibana' + - application: "kibana-.kibana" privileges: - feature_ml.read - feature_siem.all @@ -130,6 +137,7 @@ editor: - feature_siem.actions_log_management_all # Response actions history - feature_siem.file_operations_all - feature_securitySolutionCases.all + - feature_securitySolutionAssistant.all - feature_actions.read - feature_builtInAlerts.all - feature_osquery.all @@ -139,15 +147,15 @@ editor: - feature_graph.all - feature_maps.all - feature_visualize.all - resources: '*' + resources: "*" run_as: [] t1_analyst: cluster: indices: - names: - - '.alerts-security*' - - '.siem-signals-*' + - ".alerts-security*" + - ".siem-signals-*" privileges: - read - write @@ -162,13 +170,14 @@ t1_analyst: - packetbeat-* - winlogbeat-* - metrics-endpoint.metadata_current_* - - '.fleet-agents*' - - '.fleet-actions*' + - ".fleet-agents*" + - ".fleet-actions*" - risk-score.risk-score-* + - .asset-criticality.asset-criticality-* privileges: - read applications: - - application: 'kibana-.kibana' + - application: "kibana-.kibana" privileges: - feature_ml.read - feature_siem.read @@ -186,7 +195,7 @@ t1_analyst: - feature_graph.all - feature_maps.all - feature_visualize.all - resources: '*' + resources: "*" t2_analyst: cluster: @@ -215,8 +224,13 @@ t2_analyst: - risk-score.risk-score-* privileges: - read + - names: + - .asset-criticality.asset-criticality-* + privileges: + - read + - write applications: - - application: 'kibana-.kibana' + - application: "kibana-.kibana" privileges: - feature_ml.read - feature_siem.read @@ -234,7 +248,7 @@ t2_analyst: - feature_graph.all - feature_maps.all - feature_visualize.all - resources: '*' + resources: "*" t3_analyst: cluster: @@ -248,6 +262,7 @@ t3_analyst: - logs-* - packetbeat-* - winlogbeat-* + - .asset-criticality.asset-criticality-* privileges: - read - write @@ -257,6 +272,7 @@ t3_analyst: privileges: - read - write + - maintenance - names: - .lists* - .items* @@ -271,7 +287,7 @@ t3_analyst: privileges: - read applications: - - application: 'kibana-.kibana' + - application: "kibana-.kibana" privileges: - feature_ml.read - feature_siem.all @@ -298,7 +314,7 @@ t3_analyst: - feature_graph.all - feature_maps.all - feature_visualize.all - resources: '*' + resources: "*" threat_intelligence_analyst: cluster: @@ -310,12 +326,17 @@ threat_intelligence_analyst: - endgame-* - filebeat-* - logs-* - - .lists* - - .items* - packetbeat-* - winlogbeat-* privileges: - read + - names: + - .lists* + - .items* + - .asset-criticality.asset-criticality-* + privileges: + - read + - write - names: - .alerts-security* - .siem-signals-* @@ -331,11 +352,10 @@ threat_intelligence_analyst: privileges: - read applications: - - application: 'kibana-.kibana' + - application: "kibana-.kibana" privileges: - feature_ml.read - - feature_siem.read - - feature_siem.read_alerts + - feature_siem.all - feature_siem.endpoint_list_read - feature_siem.blocklist_all - feature_securitySolutionCases.all @@ -349,7 +369,7 @@ threat_intelligence_analyst: - feature_graph.all - feature_maps.all - feature_visualize.all - resources: '*' + resources: "*" rule_author: cluster: @@ -363,6 +383,7 @@ rule_author: - logs-* - packetbeat-* - winlogbeat-* + - .asset-criticality.asset-criticality-* privileges: - read - write @@ -390,7 +411,7 @@ rule_author: privileges: - read applications: - - application: 'kibana-.kibana' + - application: "kibana-.kibana" privileges: - feature_ml.read - feature_siem.all @@ -414,7 +435,7 @@ rule_author: - feature_graph.all - feature_maps.all - feature_visualize.all - resources: '*' + resources: "*" soc_manager: cluster: @@ -428,6 +449,7 @@ soc_manager: - logs-* - packetbeat-* - winlogbeat-* + - .asset-criticality.asset-criticality-* privileges: - read - write @@ -454,7 +476,7 @@ soc_manager: privileges: - read applications: - - application: 'kibana-.kibana' + - application: "kibana-.kibana" privileges: - feature_ml.read - feature_siem.all @@ -483,10 +505,10 @@ soc_manager: - feature_graph.all - feature_maps.all - feature_visualize.all - resources: '*' + resources: "*" detections_admin: - cluster: ['manage_index_templates', 'manage_transform'] + cluster: ["manage_index_templates", "manage_transform"] indices: - names: - apm-*-transaction* @@ -517,8 +539,13 @@ detections_admin: - risk-score.risk-score-* privileges: - all + - names: + - .asset-criticality.asset-criticality-* + privileges: + - read + - write applications: - - application: 'kibana-.kibana' + - application: "kibana-.kibana" privileges: - feature_ml.all - feature_siem.all @@ -535,7 +562,7 @@ detections_admin: - feature_graph.all - feature_maps.all - feature_visualize.all - resources: '*' + resources: "*" platform_engineer: cluster: @@ -559,8 +586,13 @@ platform_engineer: - risk-score.risk-score-* privileges: - all + - names: + - .asset-criticality.asset-criticality-* + privileges: + - read + - write applications: - - application: 'kibana-.kibana' + - application: "kibana-.kibana" privileges: - feature_ml.all - feature_siem.all @@ -587,7 +619,7 @@ platform_engineer: - feature_graph.all - feature_maps.all - feature_visualize.all - resources: '*' + resources: "*" endpoint_operations_analyst: cluster: @@ -620,8 +652,14 @@ endpoint_operations_analyst: privileges: - read - write + - maintenance + - names: + - .asset-criticality.asset-criticality-* + privileges: + - read + - write applications: - - application: 'kibana-.kibana' + - application: "kibana-.kibana" privileges: - feature_ml.read - feature_siem.all @@ -650,7 +688,7 @@ endpoint_operations_analyst: - feature_graph.all - feature_maps.all - feature_visualize.all - resources: '*' + resources: "*" endpoint_policy_manager: cluster: @@ -670,11 +708,16 @@ endpoint_policy_manager: - logs-* - packetbeat-* - winlogbeat-* + - risk-score.risk-score-* + privileges: + - read + - names: - .lists* - .items* - - risk-score.risk-score-* + - .asset-criticality.asset-criticality-* privileges: - read + - write - names: - .alerts-security* - .siem-signals-* @@ -685,7 +728,7 @@ endpoint_policy_manager: - write - manage applications: - - application: 'kibana-.kibana' + - application: "kibana-.kibana" privileges: - feature_ml.all - feature_siem.all @@ -710,4 +753,4 @@ endpoint_policy_manager: - feature_graph.all - feature_maps.all - feature_visualize.all - resources: '*' + resources: "*" \ No newline at end of file diff --git a/x-pack/plugins/security_solution/scripts/endpoint/common/screen/column_layout_formatter.ts b/x-pack/plugins/security_solution/scripts/endpoint/common/screen/column_layout_formatter.ts index 6bdb5f006f296..35a351c4cee27 100644 --- a/x-pack/plugins/security_solution/scripts/endpoint/common/screen/column_layout_formatter.ts +++ b/x-pack/plugins/security_solution/scripts/endpoint/common/screen/column_layout_formatter.ts @@ -6,7 +6,6 @@ */ import stripAnsi from 'strip-ansi'; -import ansiRegex from 'ansi-regex'; import { blue } from 'chalk'; import { DataFormatter } from './data_formatter'; import { SCREEN_ROW_MAX_WIDTH } from './constants'; @@ -84,3 +83,15 @@ export class ColumnLayoutFormatter extends DataFormatter { return colData + (fillCount > 0 ? ' '.repeat(fillCount) : ''); } } + +// this is a copy of the `ansiRegex` module, which is no longer allowed to be `import`ed at the module +// because it is not defined as an ESM module (only dynamic import is supported). Due to its usage, +// which does not allow `async` calls to be `await`ed, it is now being copied here for use locally. +function ansiRegex({ onlyFirst = false } = {}) { + const pattern = [ + '[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)', + '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))', + ].join('|'); + + return new RegExp(pattern, onlyFirst ? undefined : 'g'); +} diff --git a/x-pack/plugins/security_solution/scripts/run_cypress/parallel_serverless.ts b/x-pack/plugins/security_solution/scripts/run_cypress/parallel_serverless.ts index 22b51692eb33f..f3a7070aa7e33 100644 --- a/x-pack/plugins/security_solution/scripts/run_cypress/parallel_serverless.ts +++ b/x-pack/plugins/security_solution/scripts/run_cypress/parallel_serverless.ts @@ -61,7 +61,7 @@ const DEFAULT_CONFIGURATION: Readonly = [ const DEFAULT_REGION = 'aws-eu-west-1'; const PROJECT_NAME_PREFIX = 'kibana-cypress-security-solution-ephemeral'; -const BASE_ENV_URL = 'https://global.qa.cld.elstc.co'; +const BASE_ENV_URL = 'https://console.qa.cld.elstc.co'; let log: ToolingLog; const API_HEADERS = Object.freeze({ 'kbn-xsrf': 'cypress-creds', @@ -571,6 +571,7 @@ ${JSON.stringify(cypressConfigFile, null, 2)} KIBANA_PASSWORD: credentials.password, CLOUD_SERVERLESS: true, + IS_SERVERLESS: true, }; if (process.env.DEBUG && !process.env.CI) { @@ -582,6 +583,7 @@ ${JSON.stringify(cypressConfigFile, null, 2)} ---------------------------------------------- `); } + process.env.TEST_CLOUD_HOST_NAME = new URL(BASE_ENV_URL).hostname; if (isOpen) { await cypress.open({ diff --git a/x-pack/plugins/security_solution/server/assistant/jest.config.js b/x-pack/plugins/security_solution/server/assistant/jest.config.js new file mode 100644 index 0000000000000..787dae7ce8d68 --- /dev/null +++ b/x-pack/plugins/security_solution/server/assistant/jest.config.js @@ -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. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../../../..', + roots: ['/x-pack/plugins/security_solution/server/assistant'], + coverageDirectory: + '/target/kibana-coverage/jest/x-pack/plugins/security_solution/server/assistant', + coverageReporters: ['text', 'html'], + collectCoverageFrom: [ + '/x-pack/plugins/security_solution/server/assistant/**/*.{ts,tsx}', + ], + moduleNameMapper: require('../__mocks__/module_name_map'), +}; diff --git a/x-pack/plugins/security_solution/server/assistant/tools/alert_counts/alert_counts_tool.test.ts b/x-pack/plugins/security_solution/server/assistant/tools/alert_counts/alert_counts_tool.test.ts new file mode 100644 index 0000000000000..715d656944c86 --- /dev/null +++ b/x-pack/plugins/security_solution/server/assistant/tools/alert_counts/alert_counts_tool.test.ts @@ -0,0 +1,162 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; 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 { KibanaRequest } from '@kbn/core-http-server'; +import type { DynamicTool } from 'langchain/tools'; +import { omit } from 'lodash/fp'; + +import type { RequestBody } from '@kbn/elastic-assistant-plugin/server/lib/langchain/types'; +import { ALERT_COUNTS_TOOL } from './alert_counts_tool'; +import type { RetrievalQAChain } from 'langchain/chains'; + +describe('AlertCountsTool', () => { + const alertsIndexPattern = 'alerts-index'; + const esClient = { + search: jest.fn().mockResolvedValue({}), + } as unknown as ElasticsearchClient; + const replacements = { key: 'value' }; + const request = { + body: { + assistantLangChain: false, + alertsIndexPattern: '.alerts-security.alerts-default', + allow: ['@timestamp', 'cloud.availability_zone', 'user.name'], + allowReplacement: ['user.name'], + replacements, + size: 20, + }, + } as unknown as KibanaRequest; + const assistantLangChain = true; + const chain = {} as unknown as RetrievalQAChain; + const modelExists = true; + const rest = { + assistantLangChain, + chain, + modelExists, + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('isSupported', () => { + it('returns false when the alertsIndexPattern is undefined', () => { + const params = { + esClient, + request, + ...rest, + }; + + expect(ALERT_COUNTS_TOOL.isSupported(params)).toBe(false); + }); + + it('returns false when the request is missing required anonymization parameters', () => { + const requestMissingAnonymizationParams = { + body: { + assistantLangChain: false, + alertsIndexPattern: '.alerts-security.alerts-default', + size: 20, + }, + } as unknown as KibanaRequest; + const params = { + esClient, + request: requestMissingAnonymizationParams, + ...rest, + }; + + expect(ALERT_COUNTS_TOOL.isSupported(params)).toBe(false); + }); + + it('returns true if alertsIndexPattern is defined and request includes required anonymization parameters', () => { + const params = { + alertsIndexPattern, + esClient, + request, + ...rest, + }; + + expect(ALERT_COUNTS_TOOL.isSupported(params)).toBe(true); + }); + }); + + describe('getTool', () => { + it('returns a `DynamicTool` with a `func` that calls `esClient.search()` with the expected query', async () => { + const tool: DynamicTool = ALERT_COUNTS_TOOL.getTool({ + alertsIndexPattern, + esClient, + replacements, + request, + ...rest, + }) as DynamicTool; + + await tool.func(''); + + expect(esClient.search).toHaveBeenCalledWith({ + aggs: { statusBySeverity: { terms: { field: 'kibana.alert.severity' } } }, + index: ['alerts-index'], + query: { + bool: { + filter: [ + { + bool: { + filter: [{ match_phrase: { 'kibana.alert.workflow_status': 'open' } }], + must_not: [{ exists: { field: 'kibana.alert.building_block_type' } }], + }, + }, + { range: { '@timestamp': { gte: 'now/d', lte: 'now/d' } } }, + ], + }, + }, + size: 0, + }); + }); + + it('returns null when the request is missing required anonymization parameters', () => { + const requestWithMissingParams = omit('body.allow', request) as unknown as KibanaRequest< + unknown, + unknown, + RequestBody + >; + + const tool = ALERT_COUNTS_TOOL.getTool({ + alertsIndexPattern, + esClient, + replacements, + request: requestWithMissingParams, + ...rest, + }); + + expect(tool).toBeNull(); + }); + + it('returns null when the alertsIndexPattern is undefined', () => { + const tool = ALERT_COUNTS_TOOL.getTool({ + // alertsIndexPattern is undefined + esClient, + replacements, + request, + + ...rest, + }); + + expect(tool).toBeNull(); + }); + + it('returns a tool instance with the expected tags', () => { + const tool = ALERT_COUNTS_TOOL.getTool({ + alertsIndexPattern, + esClient, + replacements, + request, + + ...rest, + }) as DynamicTool; + + expect(tool.tags).toEqual(['alerts', 'alerts-count']); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/assistant/tools/alert_counts/alert_counts_tool.ts b/x-pack/plugins/security_solution/server/assistant/tools/alert_counts/alert_counts_tool.ts new file mode 100644 index 0000000000000..e1c265c3dc239 --- /dev/null +++ b/x-pack/plugins/security_solution/server/assistant/tools/alert_counts/alert_counts_tool.ts @@ -0,0 +1,47 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { SearchResponse } from '@elastic/elasticsearch/lib/api/types'; +import { DynamicTool } from 'langchain/tools'; + +import { requestHasRequiredAnonymizationParams } from '@kbn/elastic-assistant-plugin/server/lib/langchain/helpers'; +import type { AssistantTool, AssistantToolParams } from '@kbn/elastic-assistant-plugin/server'; +import { getAlertsCountQuery } from './get_alert_counts_query'; +import { APP_UI_ID } from '../../../../common'; + +export interface AlertCountsToolParams extends AssistantToolParams { + alertsIndexPattern: string; +} +export const ALERT_COUNTS_TOOL_DESCRIPTION = + 'Call this for the counts of last 24 hours of open alerts in the environment, grouped by their severity'; + +export const ALERT_COUNTS_TOOL: AssistantTool = { + id: 'alert-counts-tool', + name: 'AlertCountsTool', + description: ALERT_COUNTS_TOOL_DESCRIPTION, + sourceRegister: APP_UI_ID, + isSupported: (params: AssistantToolParams): params is AlertCountsToolParams => { + const { request, alertsIndexPattern } = params; + return requestHasRequiredAnonymizationParams(request) && alertsIndexPattern != null; + }, + getTool(params: AssistantToolParams) { + if (!this.isSupported(params)) return null; + const { alertsIndexPattern, esClient } = params as AlertCountsToolParams; + return new DynamicTool({ + name: 'AlertCountsTool', + description: ALERT_COUNTS_TOOL_DESCRIPTION, + func: async () => { + const query = getAlertsCountQuery(alertsIndexPattern); + + const result = await esClient.search(query); + + return JSON.stringify(result); + }, + tags: ['alerts', 'alerts-count'], + }); + }, +}; diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/tools/alert_counts/get_alert_counts_query.test.ts b/x-pack/plugins/security_solution/server/assistant/tools/alert_counts/get_alert_counts_query.test.ts similarity index 100% rename from x-pack/plugins/elastic_assistant/server/lib/langchain/tools/alert_counts/get_alert_counts_query.test.ts rename to x-pack/plugins/security_solution/server/assistant/tools/alert_counts/get_alert_counts_query.test.ts diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/tools/alert_counts/get_alert_counts_query.ts b/x-pack/plugins/security_solution/server/assistant/tools/alert_counts/get_alert_counts_query.ts similarity index 100% rename from x-pack/plugins/elastic_assistant/server/lib/langchain/tools/alert_counts/get_alert_counts_query.ts rename to x-pack/plugins/security_solution/server/assistant/tools/alert_counts/get_alert_counts_query.ts diff --git a/x-pack/plugins/security_solution/server/assistant/tools/esql_language_knowledge_base/esql_language_knowledge_base_tool.test.ts b/x-pack/plugins/security_solution/server/assistant/tools/esql_language_knowledge_base/esql_language_knowledge_base_tool.test.ts new file mode 100644 index 0000000000000..19e3bf7e7b40a --- /dev/null +++ b/x-pack/plugins/security_solution/server/assistant/tools/esql_language_knowledge_base/esql_language_knowledge_base_tool.test.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 { RetrievalQAChain } from 'langchain/chains'; +import type { DynamicTool } from 'langchain/tools'; +import { ESQL_KNOWLEDGE_BASE_TOOL } from './esql_language_knowledge_base_tool'; +import type { ElasticsearchClient } from '@kbn/core-elasticsearch-server'; +import type { KibanaRequest } from '@kbn/core-http-server'; +import type { RequestBody } from '@kbn/elastic-assistant-plugin/server/lib/langchain/types'; + +describe('EsqlLanguageKnowledgeBaseTool', () => { + const chain = {} as RetrievalQAChain; + const esClient = { + search: jest.fn().mockResolvedValue({}), + } as unknown as ElasticsearchClient; + const request = { + body: { + assistantLangChain: false, + alertsIndexPattern: '.alerts-security.alerts-default', + allow: ['@timestamp', 'cloud.availability_zone', 'user.name'], + allowReplacement: ['user.name'], + replacements: { key: 'value' }, + size: 20, + }, + } as unknown as KibanaRequest; + const rest = { + chain, + esClient, + request, + }; + + describe('isSupported', () => { + it('returns false if assistantLangChain is false', () => { + const params = { + assistantLangChain: false, + modelExists: true, + ...rest, + }; + + expect(ESQL_KNOWLEDGE_BASE_TOOL.isSupported(params)).toBe(false); + }); + + it('returns false if modelExists is false (the ELSER model is not installed)', () => { + const params = { + assistantLangChain: true, + modelExists: false, // <-- ELSER model is not installed + ...rest, + }; + + expect(ESQL_KNOWLEDGE_BASE_TOOL.isSupported(params)).toBe(false); + }); + + it('returns true if assistantLangChain and modelExists are true', () => { + const params = { + assistantLangChain: true, + modelExists: true, + ...rest, + }; + + expect(ESQL_KNOWLEDGE_BASE_TOOL.isSupported(params)).toBe(true); + }); + }); + + describe('getTool', () => { + it('returns null if assistantLangChain is false', () => { + const tool = ESQL_KNOWLEDGE_BASE_TOOL.getTool({ + assistantLangChain: false, + modelExists: true, + ...rest, + }); + + expect(tool).toBeNull(); + }); + + it('returns null if modelExists is false (the ELSER model is not installed)', () => { + const tool = ESQL_KNOWLEDGE_BASE_TOOL.getTool({ + assistantLangChain: true, + modelExists: false, // <-- ELSER model is not installed + ...rest, + }); + + expect(tool).toBeNull(); + }); + + it('should return a Tool instance if assistantLangChain and modelExists are true', () => { + const tool = ESQL_KNOWLEDGE_BASE_TOOL.getTool({ + assistantLangChain: true, + modelExists: true, + ...rest, + }); + + expect(tool?.name).toEqual('ESQLKnowledgeBaseTool'); + }); + + it('should return a tool with the expected tags', () => { + const tool = ESQL_KNOWLEDGE_BASE_TOOL.getTool({ + assistantLangChain: true, + modelExists: true, + ...rest, + }) as DynamicTool; + + expect(tool.tags).toEqual(['esql', 'query-generation', 'knowledge-base']); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/assistant/tools/esql_language_knowledge_base/esql_language_knowledge_base_tool.ts b/x-pack/plugins/security_solution/server/assistant/tools/esql_language_knowledge_base/esql_language_knowledge_base_tool.ts new file mode 100644 index 0000000000000..3dc7dfd8d976c --- /dev/null +++ b/x-pack/plugins/security_solution/server/assistant/tools/esql_language_knowledge_base/esql_language_knowledge_base_tool.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ChainTool } from 'langchain/tools'; +import type { AssistantTool, AssistantToolParams } from '@kbn/elastic-assistant-plugin/server'; +import { APP_UI_ID } from '../../../../common'; + +export type EsqlKnowledgeBaseToolParams = AssistantToolParams; + +export const ESQL_KNOWLEDGE_BASE_TOOL: AssistantTool = { + id: 'esql-knowledge-base-tool', + name: 'ESQLKnowledgeBaseTool', + description: + 'Call this for knowledge on how to build an ESQL query, or answer questions about the ES|QL query language.', + sourceRegister: APP_UI_ID, + isSupported: (params: AssistantToolParams): params is EsqlKnowledgeBaseToolParams => { + const { assistantLangChain, modelExists } = params; + return assistantLangChain && modelExists; + }, + getTool(params: AssistantToolParams) { + if (!this.isSupported(params)) return null; + const { chain } = params as EsqlKnowledgeBaseToolParams; + return new ChainTool({ + name: 'ESQLKnowledgeBaseTool', + description: + 'Call this for knowledge on how to build an ESQL query, or answer questions about the ES|QL query language.', + chain, + tags: ['esql', 'query-generation', 'knowledge-base'], + }); + }, +}; diff --git a/x-pack/plugins/security_solution/server/assistant/tools/index.test.ts b/x-pack/plugins/security_solution/server/assistant/tools/index.test.ts new file mode 100644 index 0000000000000..047c84ceddf3b --- /dev/null +++ b/x-pack/plugins/security_solution/server/assistant/tools/index.test.ts @@ -0,0 +1,22 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { getAssistantTools } from '.'; + +describe('getAssistantTools', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should return an array of applicable tools', () => { + const tools = getAssistantTools(); + + const minExpectedTools = 3; // 3 tools are currently implemented + + expect(tools.length).toBeGreaterThanOrEqual(minExpectedTools); + }); +}); diff --git a/x-pack/plugins/security_solution/server/assistant/tools/index.ts b/x-pack/plugins/security_solution/server/assistant/tools/index.ts new file mode 100644 index 0000000000000..790e674a4b390 --- /dev/null +++ b/x-pack/plugins/security_solution/server/assistant/tools/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { AssistantTool } from '@kbn/elastic-assistant-plugin/server'; +import { ESQL_KNOWLEDGE_BASE_TOOL } from './esql_language_knowledge_base/esql_language_knowledge_base_tool'; +import { OPEN_AND_ACKNOWLEDGED_ALERTS_TOOL } from './open_and_acknowledged_alerts/open_and_acknowledged_alerts_tool'; +import { ALERT_COUNTS_TOOL } from './alert_counts/alert_counts_tool'; + +export const getAssistantTools = (): AssistantTool[] => [ + ALERT_COUNTS_TOOL, + ESQL_KNOWLEDGE_BASE_TOOL, + OPEN_AND_ACKNOWLEDGED_ALERTS_TOOL, +]; diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/tools/open_alerts/get_open_alerts_query.test.ts b/x-pack/plugins/security_solution/server/assistant/tools/open_and_acknowledged_alerts/get_open_and_acknowledged_alerts_query.test.ts similarity index 69% rename from x-pack/plugins/elastic_assistant/server/lib/langchain/tools/open_alerts/get_open_alerts_query.test.ts rename to x-pack/plugins/security_solution/server/assistant/tools/open_and_acknowledged_alerts/get_open_and_acknowledged_alerts_query.test.ts index 673b1cae326fb..a0cf067099e92 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/langchain/tools/open_alerts/get_open_alerts_query.test.ts +++ b/x-pack/plugins/security_solution/server/assistant/tools/open_and_acknowledged_alerts/get_open_and_acknowledged_alerts_query.test.ts @@ -5,15 +5,15 @@ * 2.0. */ -import { getOpenAlertsQuery } from './get_open_alerts_query'; +import { getOpenAndAcknowledgedAlertsQuery } from './get_open_and_acknowledged_alerts_query'; -describe('getOpenAlertsQuery', () => { +describe('getOpenAndAcknowledgedAlertsQuery', () => { it('returns the expected query', () => { const alertsIndexPattern = 'alerts-*'; const allow = ['field1', 'field2']; const size = 10; - const query = getOpenAlertsQuery({ alertsIndexPattern, allow, size }); + const query = getOpenAndAcknowledgedAlertsQuery({ alertsIndexPattern, allow, size }); expect(query).toEqual({ allow_no_indices: true, @@ -30,8 +30,20 @@ describe('getOpenAlertsQuery', () => { must: [], filter: [ { - match_phrase: { - 'kibana.alert.workflow_status': 'open', + bool: { + should: [ + { + match_phrase: { + 'kibana.alert.workflow_status': 'open', + }, + }, + { + match_phrase: { + 'kibana.alert.workflow_status': 'acknowledged', + }, + }, + ], + minimum_should_match: 1, }, }, { diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/tools/open_alerts/get_open_alerts_query.ts b/x-pack/plugins/security_solution/server/assistant/tools/open_and_acknowledged_alerts/get_open_and_acknowledged_alerts_query.ts similarity index 73% rename from x-pack/plugins/elastic_assistant/server/lib/langchain/tools/open_alerts/get_open_alerts_query.ts rename to x-pack/plugins/security_solution/server/assistant/tools/open_and_acknowledged_alerts/get_open_and_acknowledged_alerts_query.ts index 4b8e1afb23ee0..9fffaf85d1f21 100644 --- a/x-pack/plugins/elastic_assistant/server/lib/langchain/tools/open_alerts/get_open_alerts_query.ts +++ b/x-pack/plugins/security_solution/server/assistant/tools/open_and_acknowledged_alerts/get_open_and_acknowledged_alerts_query.ts @@ -5,7 +5,7 @@ * 2.0. */ -export const getOpenAlertsQuery = ({ +export const getOpenAndAcknowledgedAlertsQuery = ({ alertsIndexPattern, allow, size, @@ -28,8 +28,20 @@ export const getOpenAlertsQuery = ({ must: [], filter: [ { - match_phrase: { - 'kibana.alert.workflow_status': 'open', + bool: { + should: [ + { + match_phrase: { + 'kibana.alert.workflow_status': 'open', + }, + }, + { + match_phrase: { + 'kibana.alert.workflow_status': 'acknowledged', + }, + }, + ], + minimum_should_match: 1, }, }, { diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/tools/open_alerts/helpers.test.ts b/x-pack/plugins/security_solution/server/assistant/tools/open_and_acknowledged_alerts/helpers.test.ts similarity index 100% rename from x-pack/plugins/elastic_assistant/server/lib/langchain/tools/open_alerts/helpers.test.ts rename to x-pack/plugins/security_solution/server/assistant/tools/open_and_acknowledged_alerts/helpers.test.ts diff --git a/x-pack/plugins/elastic_assistant/server/lib/langchain/tools/open_alerts/helpers.ts b/x-pack/plugins/security_solution/server/assistant/tools/open_and_acknowledged_alerts/helpers.ts similarity index 100% rename from x-pack/plugins/elastic_assistant/server/lib/langchain/tools/open_alerts/helpers.ts rename to x-pack/plugins/security_solution/server/assistant/tools/open_and_acknowledged_alerts/helpers.ts diff --git a/x-pack/plugins/security_solution/server/assistant/tools/open_and_acknowledged_alerts/open_and_acknowledged_alerts_tool.test.ts b/x-pack/plugins/security_solution/server/assistant/tools/open_and_acknowledged_alerts/open_and_acknowledged_alerts_tool.test.ts new file mode 100644 index 0000000000000..c485c001704eb --- /dev/null +++ b/x-pack/plugins/security_solution/server/assistant/tools/open_and_acknowledged_alerts/open_and_acknowledged_alerts_tool.test.ts @@ -0,0 +1,298 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license 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 { KibanaRequest } from '@kbn/core-http-server'; +import type { DynamicTool } from 'langchain/tools'; +import { omit } from 'lodash/fp'; + +import { OPEN_AND_ACKNOWLEDGED_ALERTS_TOOL } from './open_and_acknowledged_alerts_tool'; +import type { RequestBody } from '@kbn/elastic-assistant-plugin/server/lib/langchain/types'; +import { MAX_SIZE } from './helpers'; +import type { RetrievalQAChain } from 'langchain/chains'; +import { mockAlertsFieldsApi } from '@kbn/elastic-assistant-plugin/server/__mocks__/alerts'; + +describe('OpenAndAcknowledgedAlertsTool', () => { + const alertsIndexPattern = 'alerts-index'; + const esClient = { + search: jest.fn().mockResolvedValue(mockAlertsFieldsApi), + } as unknown as ElasticsearchClient; + const replacements = { key: 'value' }; + const request = { + body: { + assistantLangChain: false, + alertsIndexPattern: '.alerts-security.alerts-default', + allow: ['@timestamp', 'cloud.availability_zone', 'user.name'], + allowReplacement: ['user.name'], + replacements, + size: 20, + }, + } as unknown as KibanaRequest; + const assistantLangChain = true; + const chain = {} as unknown as RetrievalQAChain; + const modelExists = true; + const rest = { + assistantLangChain, + esClient, + chain, + modelExists, + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('isSupported', () => { + it('returns false when alertsIndexPattern is undefined', () => { + const params = { + request, + size: 20, + ...rest, + }; + + expect(OPEN_AND_ACKNOWLEDGED_ALERTS_TOOL.isSupported(params)).toBe(false); + }); + + it('returns false when the request is missing required anonymization parameters', () => { + const requestMissingAnonymizationParams = { + body: { + assistantLangChain: false, + alertsIndexPattern: '.alerts-security.alerts-default', + size: 20, + }, + } as unknown as KibanaRequest; + const params = { + request: requestMissingAnonymizationParams, + ...rest, + }; + + expect(OPEN_AND_ACKNOWLEDGED_ALERTS_TOOL.isSupported(params)).toBe(false); + }); + + it('returns false when size is undefined', () => { + const params = { + alertsIndexPattern, + allow: request.body.allow, + allowReplacement: request.body.allowReplacement, + onNewReplacements: jest.fn(), + replacements, + request, + ...rest, + }; + + expect(OPEN_AND_ACKNOWLEDGED_ALERTS_TOOL.isSupported(params)).toBe(false); + }); + + it('returns false when size is out of range', () => { + const params = { + alertsIndexPattern, + allow: request.body.allow, + allowReplacement: request.body.allowReplacement, + onNewReplacements: jest.fn(), + replacements, + request, + size: MAX_SIZE + 1, // <-- size is out of range + + ...rest, + }; + + expect(OPEN_AND_ACKNOWLEDGED_ALERTS_TOOL.isSupported(params)).toBe(false); + }); + + it('returns true when anonymization fields, alertsIndexPattern, and size within reange is provided', () => { + const params = { + alertsIndexPattern, + size: 20, + request, + ...rest, + }; + + expect(OPEN_AND_ACKNOWLEDGED_ALERTS_TOOL.isSupported(params)).toBe(true); + }); + }); + describe('getTool', () => { + it('returns a `DynamicTool` with a `func` that calls `esClient.search()` with the expected query', async () => { + const tool: DynamicTool = OPEN_AND_ACKNOWLEDGED_ALERTS_TOOL.getTool({ + alertsIndexPattern, + allow: request.body.allow, + allowReplacement: request.body.allowReplacement, + onNewReplacements: jest.fn(), + replacements, + request, + size: request.body.size, + ...rest, + }) as DynamicTool; + + await tool.func(''); + + expect(esClient.search).toHaveBeenCalledWith({ + allow_no_indices: true, + body: { + _source: false, + fields: [ + { + field: '@timestamp', + include_unmapped: true, + }, + { + field: 'cloud.availability_zone', + include_unmapped: true, + }, + { + field: 'user.name', + include_unmapped: true, + }, + ], + query: { + bool: { + filter: [ + { + bool: { + filter: [ + { + bool: { + should: [ + { + match_phrase: { + 'kibana.alert.workflow_status': 'open', + }, + }, + { + match_phrase: { + 'kibana.alert.workflow_status': 'acknowledged', + }, + }, + ], + minimum_should_match: 1, + }, + }, + { + range: { + '@timestamp': { + format: 'strict_date_optional_time', + gte: 'now-1d/d', + lte: 'now/d', + }, + }, + }, + ], + must: [], + must_not: [ + { + exists: { + field: 'kibana.alert.building_block_type', + }, + }, + ], + should: [], + }, + }, + ], + }, + }, + runtime_mappings: {}, + size: 20, + sort: [ + { + 'kibana.alert.risk_score': { + order: 'desc', + }, + }, + { + '@timestamp': { + order: 'desc', + }, + }, + ], + }, + ignore_unavailable: true, + index: ['alerts-index'], + }); + }); + + it('returns null when the request is missing required anonymization parameters', () => { + const requestWithMissingParams = omit('body.allow', request) as unknown as KibanaRequest< + unknown, + unknown, + RequestBody + >; + + const tool = OPEN_AND_ACKNOWLEDGED_ALERTS_TOOL.getTool({ + alertsIndexPattern, + allow: requestWithMissingParams.body.allow, + allowReplacement: requestWithMissingParams.body.allowReplacement, + onNewReplacements: jest.fn(), + replacements, + request: requestWithMissingParams, + size: requestWithMissingParams.body.size, + ...rest, + }); + + expect(tool).toBeNull(); + }); + + it('returns null when alertsIndexPattern is undefined', () => { + const tool = OPEN_AND_ACKNOWLEDGED_ALERTS_TOOL.getTool({ + // alertsIndexPattern is undefined + allow: request.body.allow, + allowReplacement: request.body.allowReplacement, + onNewReplacements: jest.fn(), + replacements, + request, + size: request.body.size, + ...rest, + }); + + expect(tool).toBeNull(); + }); + + it('returns null when size is undefined', () => { + const tool = OPEN_AND_ACKNOWLEDGED_ALERTS_TOOL.getTool({ + alertsIndexPattern, + allow: request.body.allow, + allowReplacement: request.body.allowReplacement, + onNewReplacements: jest.fn(), + replacements, + request, + ...rest, + // size is undefined + }); + + expect(tool).toBeNull(); + }); + + it('returns null when size out of range', () => { + const tool = OPEN_AND_ACKNOWLEDGED_ALERTS_TOOL.getTool({ + alertsIndexPattern, + allow: request.body.allow, + allowReplacement: request.body.allowReplacement, + onNewReplacements: jest.fn(), + replacements, + request, + size: MAX_SIZE + 1, // <-- size is out of range + ...rest, + }); + + expect(tool).toBeNull(); + }); + + it('returns a tool instance with the expected tags', () => { + const tool = OPEN_AND_ACKNOWLEDGED_ALERTS_TOOL.getTool({ + alertsIndexPattern, + allow: request.body.allow, + allowReplacement: request.body.allowReplacement, + onNewReplacements: jest.fn(), + replacements, + request, + size: request.body.size, + ...rest, + }) as DynamicTool; + + expect(tool.tags).toEqual(['alerts', 'open-and-acknowledged-alerts']); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/assistant/tools/open_and_acknowledged_alerts/open_and_acknowledged_alerts_tool.ts b/x-pack/plugins/security_solution/server/assistant/tools/open_and_acknowledged_alerts/open_and_acknowledged_alerts_tool.ts new file mode 100644 index 0000000000000..210fdd42c1555 --- /dev/null +++ b/x-pack/plugins/security_solution/server/assistant/tools/open_and_acknowledged_alerts/open_and_acknowledged_alerts_tool.ts @@ -0,0 +1,93 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { SearchResponse } from '@elastic/elasticsearch/lib/api/types'; +import { getAnonymizedValue, transformRawData } from '@kbn/elastic-assistant-common'; +import { DynamicTool } from 'langchain/tools'; +import { requestHasRequiredAnonymizationParams } from '@kbn/elastic-assistant-plugin/server/lib/langchain/helpers'; + +import type { AssistantTool, AssistantToolParams } from '@kbn/elastic-assistant-plugin/server'; +import { getOpenAndAcknowledgedAlertsQuery } from './get_open_and_acknowledged_alerts_query'; +import { getRawDataOrDefault, sizeIsOutOfRange } from './helpers'; +import { APP_UI_ID } from '../../../../common'; + +export interface OpenAndAcknowledgedAlertsToolParams extends AssistantToolParams { + alertsIndexPattern: string; + size: number; +} + +export const OPEN_AND_ACKNOWLEDGED_ALERTS_TOOL_DESCRIPTION = + 'Call this for knowledge about the latest n open and acknowledged alerts (sorted by `kibana.alert.risk_score`) in the environment, or when answering questions about open alerts'; + +/** + * Returns a tool for querying open and acknowledged alerts, or null if the + * request doesn't have all the required parameters. + */ +export const OPEN_AND_ACKNOWLEDGED_ALERTS_TOOL: AssistantTool = { + id: 'open-and-acknowledged-alerts-tool', + name: 'OpenAndAcknowledgedAlertsTool', + description: OPEN_AND_ACKNOWLEDGED_ALERTS_TOOL_DESCRIPTION, + sourceRegister: APP_UI_ID, + isSupported: (params: AssistantToolParams): params is OpenAndAcknowledgedAlertsToolParams => { + const { alertsIndexPattern, request, size } = params; + return ( + requestHasRequiredAnonymizationParams(request) && + alertsIndexPattern != null && + size != null && + !sizeIsOutOfRange(size) + ); + }, + getTool(params: AssistantToolParams) { + if (!this.isSupported(params)) return null; + + const { + alertsIndexPattern, + allow, + allowReplacement, + esClient, + onNewReplacements, + replacements, + size, + } = params as OpenAndAcknowledgedAlertsToolParams; + return new DynamicTool({ + name: 'OpenAndAcknowledgedAlertsTool', + description: OPEN_AND_ACKNOWLEDGED_ALERTS_TOOL_DESCRIPTION, + func: async () => { + const query = getOpenAndAcknowledgedAlertsQuery({ + alertsIndexPattern, + allow: allow ?? [], + size, + }); + + const result = await esClient.search(query); + + // Accumulate replacements locally so we can, for example use the same + // replacement for a hostname when we see it in multiple alerts: + let localReplacements = { ...replacements }; + const localOnNewReplacements = (newReplacements: Record) => { + localReplacements = { ...localReplacements, ...newReplacements }; // update the local state + + onNewReplacements?.(localReplacements); // invoke the callback with the latest replacements + }; + + return JSON.stringify( + result.hits?.hits?.map((x) => + transformRawData({ + allow: allow ?? [], + allowReplacement: allowReplacement ?? [], + currentReplacements: localReplacements, // <-- the latest local replacements + getAnonymizedValue, + onNewReplacements: localOnNewReplacements, // <-- the local callback + rawData: getRawDataOrDefault(x.fields), + }) + ) + ); + }, + tags: ['alerts', 'open-and-acknowledged-alerts'], + }); + }, +}; diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver.ts index ac9fb318d4c96..01d3ad006405f 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver.ts @@ -50,6 +50,6 @@ export const registerResolverRoutes = async ( validate: validateEntities, options: { authRequired: true }, }, - handleEntities() + handleEntities(config.experimentalFeatures) ); }; diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/entity/handler.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/entity/handler.ts index a605226ddfb95..80fa208f1889a 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/entity/handler.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/entity/handler.ts @@ -7,6 +7,7 @@ import type { RequestHandler } from '@kbn/core/server'; import type { TypeOf } from '@kbn/config-schema'; +import type { ExperimentalFeatures } from '../../../../../common'; import { EXCLUDE_COLD_AND_FROZEN_TIERS_IN_ANALYZER } from '../../../../../common/constants'; import type { validateEntities } from '../../../../../common/endpoint/schema/resolver'; import type { ResolverEntityIndex } from '../../../../../common/endpoint/types'; @@ -17,7 +18,9 @@ import { createSharedFilters } from '../utils/shared_filters'; * This is used to get an 'entity_id' which is an internal-to-Resolver concept, from an `_id`, which * is the artificial ID generated by ES for each document. */ -export function handleEntities(): RequestHandler> { +export function handleEntities( + experimentalFeatures: ExperimentalFeatures +): RequestHandler> { return async (context, request, response) => { const { query: { _id, indices }, @@ -50,7 +53,10 @@ export function handleEntities(): RequestHandler>) { +const toArray = (input: T | T[]) => ([] as T[]).concat(input); + +export function resolverEntity( + hits: Array>, + experimentalFeatures: ExperimentalFeatures | undefined +) { const responseBody: ResolverEntityIndex = []; + const supportedSchemas = getSupportedSchemas(experimentalFeatures); for (const hit of hits) { for (const supportedSchema of supportedSchemas) { let foundSchema = true; @@ -20,7 +27,12 @@ export function resolverEntity(hits: Array>) { const fieldValue = getFieldAsString(hit._source, constraint.field); // track that all the constraints are true, if one of them is false then this schema is not valid so mark it // that we did not find the schema - foundSchema = foundSchema && fieldValue?.toLowerCase() === constraint.value.toLowerCase(); + + foundSchema = + foundSchema && + toArray(constraint.value).some( + (constraintValue) => constraintValue.toLowerCase() === fieldValue?.toLowerCase() + ); } if (foundSchema && id !== undefined && id !== '') { diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/entity/utils/supported_schemas.ts b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/entity/utils/supported_schemas.ts index 788e7f3ae7713..de95c8fc9f022 100644 --- a/x-pack/plugins/security_solution/server/endpoint/routes/resolver/entity/utils/supported_schemas.ts +++ b/x-pack/plugins/security_solution/server/endpoint/routes/resolver/entity/utils/supported_schemas.ts @@ -6,6 +6,7 @@ */ import _ from 'lodash'; +import type { ExperimentalFeatures } from '../../../../../../common'; import type { ResolverSchema } from '../../../../../../common/endpoint/types'; interface SupportedSchema { @@ -17,7 +18,7 @@ interface SupportedSchema { /** * A constraint to search for in the documented returned by Elasticsearch */ - constraints: Array<{ field: string; value: string }>; + constraints: Array<{ field: string; value: string | string[] }>; /** * Schema to return to the frontend so that it can be passed in to call to the /tree API @@ -29,59 +30,90 @@ interface SupportedSchema { * This structure defines the preset supported schemas for a resolver graph. We'll probably want convert this * implementation to something similar to how row renderers is implemented. */ -export const supportedSchemas: SupportedSchema[] = [ - { - name: 'endpoint', - constraints: [ - { - field: 'agent.type', - value: 'endpoint', + +export const getSupportedSchemas = ( + experimentalFeatures: ExperimentalFeatures | undefined +): SupportedSchema[] => { + const sentinelOneDataInAnalyzerEnabled = experimentalFeatures?.sentinelOneDataInAnalyzerEnabled; + + const supportedFileBeatDataSets = [ + ...(sentinelOneDataInAnalyzerEnabled + ? ['sentinel_one_cloud_funnel.event', 'sentinel_one.alert'] + : []), + ]; + + return [ + { + name: 'endpoint', + constraints: [ + { + field: 'agent.type', + value: 'endpoint', + }, + ], + schema: { + id: 'process.entity_id', + parent: 'process.parent.entity_id', + ancestry: 'process.Ext.ancestry', + name: 'process.name', }, - ], - schema: { - id: 'process.entity_id', - parent: 'process.parent.entity_id', - ancestry: 'process.Ext.ancestry', - name: 'process.name', }, - }, - { - name: 'winlogbeat', - constraints: [ - { - field: 'agent.type', - value: 'winlogbeat', + { + name: 'winlogbeat', + constraints: [ + { + field: 'agent.type', + value: 'winlogbeat', + }, + { + field: 'event.module', + value: 'sysmon', + }, + ], + schema: { + id: 'process.entity_id', + parent: 'process.parent.entity_id', + name: 'process.name', }, - { - field: 'event.module', - value: 'sysmon', - }, - ], - schema: { - id: 'process.entity_id', - parent: 'process.parent.entity_id', - name: 'process.name', }, - }, - { - name: 'sysmonViaFilebeat', - constraints: [ - { - field: 'agent.type', - value: 'filebeat', + { + name: 'sysmonViaFilebeat', + constraints: [ + { + field: 'agent.type', + value: 'filebeat', + }, + { + field: 'event.dataset', + value: 'windows.sysmon_operational', + }, + ], + schema: { + id: 'process.entity_id', + parent: 'process.parent.entity_id', + name: 'process.name', }, - { - field: 'event.dataset', - value: 'windows.sysmon_operational', + }, + { + name: 'filebeat', + constraints: [ + { + field: 'agent.type', + value: 'filebeat', + }, + { + field: 'event.dataset', + value: supportedFileBeatDataSets, + }, + ], + schema: { + id: 'process.entity_id', + parent: 'process.parent.entity_id', + name: 'process.name', }, - ], - schema: { - id: 'process.entity_id', - parent: 'process.parent.entity_id', - name: 'process.name', }, - }, -]; + ]; +}; export function getFieldAsString(doc: unknown, field: string): string | undefined { const value = _.get(doc, field); 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 index 7dd7cd75a6304..7032a0d7b4725 100644 --- 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 @@ -9,6 +9,7 @@ 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, @@ -56,7 +57,7 @@ export const updateCases = async ({ const attachments = caseIDs.map(() => ({ type: AttachmentType.actions, - comment: createActionPayload.comment || '', + comment: createActionPayload.comment || EMPTY_COMMENT, actions: { targets, type: createActionPayload.command, @@ -74,3 +75,10 @@ export const updateCases = async ({ ); } }; + +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/prebuilt_rules/api/review_rule_upgrade/review_rule_upgrade_route.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/review_rule_upgrade/review_rule_upgrade_route.ts index b0d53f2c43a9d..1fcb36b592d56 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/review_rule_upgrade/review_rule_upgrade_route.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/prebuilt_rules/api/review_rule_upgrade/review_rule_upgrade_route.ts @@ -100,6 +100,11 @@ const calculateRuleInfos = (results: CalculateRuleDiffResult[]): RuleUpgradeInfo const targetRule: RuleResponse = { ...convertPrebuiltRuleAssetToRuleResponse(targetVersion), id: installedCurrentVersion.id, + revision: installedCurrentVersion.revision + 1, + created_at: installedCurrentVersion.created_at, + created_by: installedCurrentVersion.created_by, + updated_at: new Date().toISOString(), + updated_by: installedCurrentVersion.updated_by, }; return { diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/deprecation.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/deprecation.ts index 8e956e0e48aa6..46a9af76da4f0 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/deprecation.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_management/api/deprecation.ts @@ -16,7 +16,8 @@ import { DETECTION_ENGINE_RULES_BULK_ACTION } from '../../../../../common/consta * @returns string */ export const buildDeprecatedBulkEndpointMessage = (path: string) => { - const docsLink = getDocLinks({ kibanaBranch: 'main' }).siem.ruleApiOverview; + const docsLink = getDocLinks({ kibanaBranch: 'main', buildFlavor: 'traditional' }).siem + .ruleApiOverview; return `Deprecated endpoint: ${path} API is deprecated since v8.2. Please use the ${DETECTION_ENGINE_RULES_BULK_ACTION} API instead. See ${docsLink} for more detail.`; }; diff --git a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/wrap_suppressed_threshold_alerts.ts b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/wrap_suppressed_threshold_alerts.ts index a3df4faa14c56..e2fa437a03543 100644 --- a/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/wrap_suppressed_threshold_alerts.ts +++ b/x-pack/plugins/security_solution/server/lib/detection_engine/rule_types/threshold/wrap_suppressed_threshold_alerts.ts @@ -6,6 +6,7 @@ */ import objectHash from 'object-hash'; +import sortBy from 'lodash/sortBy'; import type { SuppressionFieldsLatest } from '@kbn/rule-registry-plugin/common/schemas'; import { @@ -79,9 +80,7 @@ export const wrapSuppressedThresholdALerts = ({ completeRule.ruleParams.ruleId ); - const suppressedValues = Object.entries(bucket.key) - .map(([key, value]) => value) - .sort((a, b) => a.localeCompare(b)); + const suppressedValues = sortBy(Object.entries(bucket.key).map(([_, value]) => value)); const id = objectHash([ hit._index, diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/asset_criticality_data_client.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/asset_criticality_data_client.ts index 6bb003df0fa85..afddfd6893240 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/asset_criticality_data_client.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/asset_criticality_data_client.ts @@ -8,7 +8,7 @@ import type { Logger, ElasticsearchClient } from '@kbn/core/server'; import { mappingFromFieldMap } from '@kbn/alerting-plugin/common'; import type { AssetCriticalityRecord } from '../../../../common/api/entity_analytics/asset_criticality'; import { createOrUpdateIndex } from '../utils/create_or_update_index'; -import { getAssetCriticalityIndex } from '../../../../common/asset_criticality'; +import { getAssetCriticalityIndex } from '../../../../common/entity_analytics/asset_criticality'; import { assetCriticalityFieldMap } from './configurations'; interface AssetCriticalityClientOpts { diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/get_user_asset_criticality_privileges.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/get_user_asset_criticality_privileges.ts new file mode 100644 index 0000000000000..7c77ca722ea78 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/get_user_asset_criticality_privileges.ts @@ -0,0 +1,27 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import type { KibanaRequest } from '@kbn/core/server'; +import type { SecurityPluginStart } from '@kbn/security-plugin/server'; +import { checkAndFormatPrivileges } from '../utils/check_and_format_privileges'; +import { ASSET_CRITICALITY_REQUIRED_ES_INDEX_PRIVILEGES } from '../../../../common/entity_analytics/asset_criticality'; + +export const getUserAssetCriticalityPrivileges = async ( + request: KibanaRequest, + security: SecurityPluginStart +) => { + return checkAndFormatPrivileges({ + request, + security, + privilegesToCheck: { + elasticsearch: { + cluster: [], + index: ASSET_CRITICALITY_REQUIRED_ES_INDEX_PRIVILEGES, + }, + }, + }); +}; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/index.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/index.ts index 8a5f62ccff079..df67a16928d48 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/index.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/index.ts @@ -9,3 +9,4 @@ export { assetCriticalityStatusRoute } from './status'; export { assetCriticalityUpsertRoute } from './upsert'; export { assetCriticalityGetRoute } from './get'; export { assetCriticalityDeleteRoute } from './delete'; +export { assetCriticalityPrivilegesRoute } from './privileges'; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/privileges.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/privileges.ts new file mode 100644 index 0000000000000..4351659afa774 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/asset_criticality/routes/privileges.ts @@ -0,0 +1,55 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import type { Logger, StartServicesAccessor } from '@kbn/core/server'; +import { buildSiemResponse } from '@kbn/lists-plugin/server/routes/utils'; +import { transformError } from '@kbn/securitysolution-es-utils'; +import { ASSET_CRITICALITY_PRIVILEGES_URL, APP_ID } from '../../../../../common/constants'; +import type { SecuritySolutionPluginRouter } from '../../../../types'; +import { checkAndInitAssetCriticalityResources } from '../check_and_init_asset_criticality_resources'; +import { getUserAssetCriticalityPrivileges } from '../get_user_asset_criticality_privileges'; + +import type { StartPlugins } from '../../../../plugin'; +export const assetCriticalityPrivilegesRoute = ( + router: SecuritySolutionPluginRouter, + getStartServices: StartServicesAccessor, + logger: Logger +) => { + router.versioned + .get({ + access: 'internal', + path: ASSET_CRITICALITY_PRIVILEGES_URL, + options: { + tags: ['access:securitySolution', `access:${APP_ID}-entity-analytics`], + }, + }) + .addVersion( + { + version: '1', + validate: false, + }, + async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + try { + await checkAndInitAssetCriticalityResources(context, logger); + + const [_, { security }] = await getStartServices(); + const body = await getUserAssetCriticalityPrivileges(request, security); + + return response.ok({ + body, + }); + } catch (e) { + const error = transformError(e); + + return siemResponse.error({ + statusCode: error.statusCode, + body: error.message, + }); + } + } + ); +}; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.ts index e649fa24b2139..88c911a6d7789 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_data_client.ts @@ -7,12 +7,12 @@ import type { Logger, ElasticsearchClient, SavedObjectsClientContract } from '@kbn/core/server'; import type { TaskManagerStartContract } from '@kbn/task-manager-plugin/server'; -import type { InitRiskEngineResult } from '../../../../common/risk_engine'; +import type { InitRiskEngineResult } from '../../../../common/entity_analytics/risk_engine'; import { RiskEngineStatus, MAX_SPACES_COUNT, RiskScoreEntity, -} from '../../../../common/risk_engine'; +} from '../../../../common/entity_analytics/risk_engine'; import { removeLegacyTransforms, getLegacyTransforms } from '../utils/transforms'; import { updateSavedObjectAttribute, diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_privileges.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_privileges.test.ts new file mode 100644 index 0000000000000..f464292713420 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_privileges.test.ts @@ -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 type { EntityAnalyticsPrivileges } from '../../../../common/api/entity_analytics/common'; +import { _getMissingPrivilegesMessage } from './risk_engine_privileges'; + +describe('_getMissingPrivilegesMessage', () => { + it('should create correct message for user with no cluster privileges', () => { + const noClusterPrivileges: EntityAnalyticsPrivileges = { + privileges: { + elasticsearch: { + cluster: { + manage_index_templates: false, + manage_transform: false, + }, + index: { + 'risk-score.risk-score-*': { + read: true, + write: true, + }, + }, + }, + }, + has_all_required: false, + }; + + const result = _getMissingPrivilegesMessage(noClusterPrivileges); + + expect(result).toMatchInlineSnapshot( + `"User is missing risk engine privileges. Missing cluster privileges: manage_index_templates, manage_transform."` + ); + }); + + it('should create correct message for user with no index privileges', () => { + const noIndexPrivileges: EntityAnalyticsPrivileges = { + privileges: { + elasticsearch: { + cluster: { + manage_index_templates: true, + manage_transform: true, + }, + index: { + 'risk-score.risk-score-*': { + read: false, + write: false, + }, + }, + }, + }, + has_all_required: false, + }; + + const result = _getMissingPrivilegesMessage(noIndexPrivileges); + + expect(result).toMatchInlineSnapshot( + `"User is missing risk engine privileges. Missing index privileges for index \\"risk-score.risk-score-*\\": read, write. "` + ); + }); + + it('should create correct message for user with no cluster or index privileges', () => { + const noClusterOrIndexPrivileges: EntityAnalyticsPrivileges = { + privileges: { + elasticsearch: { + cluster: { + manage_index_templates: false, + manage_transform: false, + }, + index: { + 'risk-score.risk-score-*': { + read: false, + write: false, + }, + }, + }, + }, + has_all_required: false, + }; + + const result = _getMissingPrivilegesMessage(noClusterOrIndexPrivileges); + + expect(result).toMatchInlineSnapshot( + `"User is missing risk engine privileges. Missing index privileges for index \\"risk-score.risk-score-*\\": read, write. Missing cluster privileges: manage_index_templates, manage_transform."` + ); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_privileges.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_privileges.ts new file mode 100644 index 0000000000000..df7914e5b242b --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/risk_engine_privileges.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 { + IKibanaResponse, + KibanaRequest, + KibanaResponseFactory, + StartServicesAccessor, +} from '@kbn/core/server'; +import { buildSiemResponse } from '@kbn/lists-plugin/server/routes'; +import type { SecurityPluginStart } from '@kbn/security-plugin/server'; +import { i18n } from '@kbn/i18n'; +import type { EntityAnalyticsPrivileges } from '../../../../common/api/entity_analytics/common'; +import type { SecuritySolutionPluginStartDependencies } from '../../../plugin_contract'; +import type { SecuritySolutionRequestHandlerContext } from '../../../types'; +import { + RISK_ENGINE_REQUIRED_ES_CLUSTER_PRIVILEGES, + RISK_ENGINE_REQUIRED_ES_INDEX_PRIVILEGES, + getMissingRiskEnginePrivileges, +} from '../../../../common/entity_analytics/risk_engine'; +import { checkAndFormatPrivileges } from '../utils/check_and_format_privileges'; + +export const getUserRiskEnginePrivileges = async ( + request: KibanaRequest, + security: SecurityPluginStart +) => { + return checkAndFormatPrivileges({ + request, + security, + privilegesToCheck: { + elasticsearch: { + cluster: RISK_ENGINE_REQUIRED_ES_CLUSTER_PRIVILEGES, + index: RISK_ENGINE_REQUIRED_ES_INDEX_PRIVILEGES, + }, + }, + }); +}; + +export const _getMissingPrivilegesMessage = (riskEnginePrivileges: EntityAnalyticsPrivileges) => { + const { indexPrivileges, clusterPrivileges } = getMissingRiskEnginePrivileges( + riskEnginePrivileges.privileges + ); + + const indexPrivilegesMessage = indexPrivileges + .map(([indexName, privileges]) => + i18n.translate('xpack.securitySolution.entityAnalytics.riskEngine.missingIndexPrivilege', { + defaultMessage: 'Missing index privileges for index "{indexName}": {privileges}.', + values: { + indexName, + privileges: privileges.join(', '), + }, + }) + ) + .join('\n'); + + const clusterPrivilegesMessage = !clusterPrivileges.length + ? '' + : i18n.translate('xpack.securitySolution.entityAnalytics.riskEngine.missingClusterPrivilege', { + defaultMessage: 'Missing cluster privileges: {privileges}.', + values: { + privileges: clusterPrivileges.join(', '), + }, + }); + + const unauthorizedMessage = i18n.translate( + 'xpack.securitySolution.entityAnalytics.riskEngine.unauthorized', + { + defaultMessage: 'User is missing risk engine privileges.', + } + ); + + return `${unauthorizedMessage} ${indexPrivilegesMessage} ${clusterPrivilegesMessage}`; +}; + +/** + * This function is used to check if the user has the required privileges to access the risk engine. + * It is used to wrap a risk engine route handler which requires full access to the risk engine. + * @param getStartServices - Kibana's start services accessor + * @param handler - The route handler to wrap + **/ +export const withRiskEnginePrivilegeCheck = ( + getStartServices: StartServicesAccessor, + handler: ( + context: SecuritySolutionRequestHandlerContext, + request: KibanaRequest, + response: KibanaResponseFactory + ) => Promise +) => { + return async ( + context: SecuritySolutionRequestHandlerContext, + request: KibanaRequest, + response: KibanaResponseFactory + ) => { + const [_, { security }] = await getStartServices(); + const privileges = await getUserRiskEnginePrivileges(request, security); + if (!privileges.has_all_required) { + const siemResponse = buildSiemResponse(response); + return siemResponse.error({ + statusCode: 403, + body: _getMissingPrivilegesMessage(privileges), + }); + } + return handler(context, request, response); + }; +}; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/disable.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/disable.test.ts index eb7ae66c2c3d3..429b13f3a3738 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/disable.test.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/disable.test.ts @@ -7,7 +7,7 @@ import { taskManagerMock } from '@kbn/task-manager-plugin/server/mocks'; import { riskEngineDisableRoute } from './disable'; - +import { riskEnginePrivilegesMock } from './risk_engine_privileges.mock'; import { RISK_ENGINE_DISABLE_URL } from '../../../../../common/constants'; import { serverMock, @@ -48,9 +48,13 @@ describe('risk score disable route', () => { describe('when task manager is available', () => { beforeEach(() => { - getStartServicesMock = jest - .fn() - .mockResolvedValue([{}, { taskManager: mockTaskManagerStart }]); + getStartServicesMock = jest.fn().mockResolvedValue([ + {}, + { + taskManager: mockTaskManagerStart, + security: riskEnginePrivilegesMock.createMockSecurityStartWithFullRiskEngineAccess(), + }, + ]); riskEngineDisableRoute(server.router, getStartServicesMock); }); @@ -84,7 +88,13 @@ describe('risk score disable route', () => { describe('when task manager is unavailable', () => { beforeEach(() => { - getStartServicesMock = jest.fn().mockResolvedValueOnce([{}, { taskManager: undefined }]); + getStartServicesMock = jest.fn().mockResolvedValue([ + {}, + { + taskManager: undefined, + security: riskEnginePrivilegesMock.createMockSecurityStartWithFullRiskEngineAccess(), + }, + ]); riskEngineDisableRoute(server.router, getStartServicesMock); }); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/disable.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/disable.ts index dde7ddbee9e83..4f9ffc99057be 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/disable.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/disable.ts @@ -12,6 +12,7 @@ import { RISK_ENGINE_DISABLE_URL, APP_ID } from '../../../../../common/constants import type { StartPlugins } from '../../../../plugin'; import type { SecuritySolutionPluginRouter } from '../../../../types'; import { TASK_MANAGER_UNAVAILABLE_ERROR } from './translations'; +import { withRiskEnginePrivilegeCheck } from '../risk_engine_privileges'; export const riskEngineDisableRoute = ( router: SecuritySolutionPluginRouter, @@ -25,31 +26,34 @@ export const riskEngineDisableRoute = ( tags: ['access:securitySolution', `access:${APP_ID}-entity-analytics`], }, }) - .addVersion({ version: '1', validate: {} }, async (context, request, response) => { - const siemResponse = buildSiemResponse(response); + .addVersion( + { version: '1', validate: {} }, + withRiskEnginePrivilegeCheck(getStartServices, async (context, request, response) => { + const siemResponse = buildSiemResponse(response); - const [_, { taskManager }] = await getStartServices(); - const securitySolution = await context.securitySolution; - const riskEngineClient = securitySolution.getRiskEngineDataClient(); + const [_, { taskManager }] = await getStartServices(); + const securitySolution = await context.securitySolution; + const riskEngineClient = securitySolution.getRiskEngineDataClient(); - if (!taskManager) { - return siemResponse.error({ - statusCode: 400, - body: TASK_MANAGER_UNAVAILABLE_ERROR, - }); - } + if (!taskManager) { + return siemResponse.error({ + statusCode: 400, + body: TASK_MANAGER_UNAVAILABLE_ERROR, + }); + } - try { - await riskEngineClient.disableRiskEngine({ taskManager }); - return response.ok({ body: { success: true } }); - } catch (e) { - const error = transformError(e); + try { + await riskEngineClient.disableRiskEngine({ taskManager }); + return response.ok({ body: { success: true } }); + } catch (e) { + const error = transformError(e); - return siemResponse.error({ - statusCode: error.statusCode, - body: { message: error.message, full_error: JSON.stringify(e) }, - bypassErrorFormat: true, - }); - } - }); + return siemResponse.error({ + statusCode: error.statusCode, + body: { message: error.message, full_error: JSON.stringify(e) }, + bypassErrorFormat: true, + }); + } + }) + ); }; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/enable.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/enable.test.ts index 02ccf01dc6166..4932de0b2d270 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/enable.test.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/enable.test.ts @@ -7,7 +7,6 @@ import { taskManagerMock } from '@kbn/task-manager-plugin/server/mocks'; import { riskEngineEnableRoute } from './enable'; - import { RISK_ENGINE_ENABLE_URL } from '../../../../../common/constants'; import { serverMock, @@ -15,6 +14,7 @@ import { requestMock, } from '../../../detection_engine/routes/__mocks__'; import { riskEngineDataClientMock } from '../risk_engine_data_client.mock'; +import { riskEnginePrivilegesMock } from './risk_engine_privileges.mock'; describe('risk score enable route', () => { let server: ReturnType; @@ -48,9 +48,13 @@ describe('risk score enable route', () => { describe('when task manager is available', () => { beforeEach(() => { - getStartServicesMock = jest - .fn() - .mockResolvedValue([{}, { taskManager: mockTaskManagerStart }]); + getStartServicesMock = jest.fn().mockResolvedValue([ + {}, + { + taskManager: mockTaskManagerStart, + security: riskEnginePrivilegesMock.createMockSecurityStartWithFullRiskEngineAccess(), + }, + ]); riskEngineEnableRoute(server.router, getStartServicesMock); }); @@ -84,7 +88,13 @@ describe('risk score enable route', () => { describe('when task manager is unavailable', () => { beforeEach(() => { - getStartServicesMock = jest.fn().mockResolvedValueOnce([{}, { taskManager: undefined }]); + getStartServicesMock = jest.fn().mockResolvedValue([ + {}, + { + taskManager: undefined, + security: riskEnginePrivilegesMock.createMockSecurityStartWithFullRiskEngineAccess(), + }, + ]); riskEngineEnableRoute(server.router, getStartServicesMock); }); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/enable.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/enable.ts index bebc8b7236bb8..e63a914efadb9 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/enable.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/enable.ts @@ -12,6 +12,7 @@ import { RISK_ENGINE_ENABLE_URL, APP_ID } from '../../../../../common/constants' import { TASK_MANAGER_UNAVAILABLE_ERROR } from './translations'; import type { StartPlugins } from '../../../../plugin'; import type { SecuritySolutionPluginRouter } from '../../../../types'; +import { withRiskEnginePrivilegeCheck } from '../risk_engine_privileges'; export const riskEngineEnableRoute = ( router: SecuritySolutionPluginRouter, @@ -25,29 +26,32 @@ export const riskEngineEnableRoute = ( tags: ['access:securitySolution', `access:${APP_ID}-entity-analytics`], }, }) - .addVersion({ version: '1', validate: {} }, async (context, request, response) => { - const siemResponse = buildSiemResponse(response); - const [_, { taskManager }] = await getStartServices(); - const securitySolution = await context.securitySolution; - const riskEngineClient = securitySolution.getRiskEngineDataClient(); - if (!taskManager) { - return siemResponse.error({ - statusCode: 400, - body: TASK_MANAGER_UNAVAILABLE_ERROR, - }); - } + .addVersion( + { version: '1', validate: {} }, + withRiskEnginePrivilegeCheck(getStartServices, async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + const [_, { taskManager }] = await getStartServices(); + const securitySolution = await context.securitySolution; + const riskEngineClient = securitySolution.getRiskEngineDataClient(); + if (!taskManager) { + return siemResponse.error({ + statusCode: 400, + body: TASK_MANAGER_UNAVAILABLE_ERROR, + }); + } - try { - await riskEngineClient.enableRiskEngine({ taskManager }); - return response.ok({ body: { success: true } }); - } catch (e) { - const error = transformError(e); + try { + await riskEngineClient.enableRiskEngine({ taskManager }); + return response.ok({ body: { success: true } }); + } catch (e) { + const error = transformError(e); - return siemResponse.error({ - statusCode: error.statusCode, - body: { message: error.message, full_error: JSON.stringify(e) }, - bypassErrorFormat: true, - }); - } - }); + return siemResponse.error({ + statusCode: error.statusCode, + body: { message: error.message, full_error: JSON.stringify(e) }, + bypassErrorFormat: true, + }); + } + }) + ); }; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/init.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/init.ts index a76af277949fd..ffc6608505804 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/init.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/init.ts @@ -13,7 +13,7 @@ import type { StartPlugins } from '../../../../plugin'; import { TASK_MANAGER_UNAVAILABLE_ERROR } from './translations'; import type { SecuritySolutionPluginRouter } from '../../../../types'; import type { InitRiskEngineResultResponse } from '../../types'; - +import { withRiskEnginePrivilegeCheck } from '../risk_engine_privileges'; export const riskEngineInitRoute = ( router: SecuritySolutionPluginRouter, getStartServices: StartServicesAccessor @@ -26,59 +26,62 @@ export const riskEngineInitRoute = ( tags: ['access:securitySolution', `access:${APP_ID}-entity-analytics`], }, }) - .addVersion({ version: '1', validate: {} }, async (context, request, response) => { - const siemResponse = buildSiemResponse(response); - const securitySolution = await context.securitySolution; - const [_, { taskManager }] = await getStartServices(); - const riskEngineDataClient = securitySolution.getRiskEngineDataClient(); - const riskScoreDataClient = securitySolution.getRiskScoreDataClient(); - const spaceId = securitySolution.getSpaceId(); + .addVersion( + { version: '1', validate: {} }, + withRiskEnginePrivilegeCheck(getStartServices, async (context, request, response) => { + const siemResponse = buildSiemResponse(response); + const securitySolution = await context.securitySolution; + const [_, { taskManager }] = await getStartServices(); + const riskEngineDataClient = securitySolution.getRiskEngineDataClient(); + const riskScoreDataClient = securitySolution.getRiskScoreDataClient(); + const spaceId = securitySolution.getSpaceId(); - try { - if (!taskManager) { - return siemResponse.error({ - statusCode: 400, - body: TASK_MANAGER_UNAVAILABLE_ERROR, + try { + if (!taskManager) { + return siemResponse.error({ + statusCode: 400, + body: TASK_MANAGER_UNAVAILABLE_ERROR, + }); + } + + const initResult = await riskEngineDataClient.init({ + taskManager, + namespace: spaceId, + riskScoreDataClient, }); - } - const initResult = await riskEngineDataClient.init({ - taskManager, - namespace: spaceId, - riskScoreDataClient, - }); + const initResultResponse: InitRiskEngineResultResponse = { + risk_engine_enabled: initResult.riskEngineEnabled, + risk_engine_resources_installed: initResult.riskEngineResourcesInstalled, + risk_engine_configuration_created: initResult.riskEngineConfigurationCreated, + legacy_risk_engine_disabled: initResult.legacyRiskEngineDisabled, + errors: initResult.errors, + }; - const initResultResponse: InitRiskEngineResultResponse = { - risk_engine_enabled: initResult.riskEngineEnabled, - risk_engine_resources_installed: initResult.riskEngineResourcesInstalled, - risk_engine_configuration_created: initResult.riskEngineConfigurationCreated, - legacy_risk_engine_disabled: initResult.legacyRiskEngineDisabled, - errors: initResult.errors, - }; + if ( + !initResult.riskEngineEnabled || + !initResult.riskEngineResourcesInstalled || + !initResult.riskEngineConfigurationCreated + ) { + return siemResponse.error({ + statusCode: 400, + body: { + message: initResultResponse.errors.join('\n'), + full_error: initResultResponse, + }, + bypassErrorFormat: true, + }); + } + return response.ok({ body: { result: initResultResponse } }); + } catch (e) { + const error = transformError(e); - if ( - !initResult.riskEngineEnabled || - !initResult.riskEngineResourcesInstalled || - !initResult.riskEngineConfigurationCreated - ) { return siemResponse.error({ - statusCode: 400, - body: { - message: initResultResponse.errors.join('\n'), - full_error: initResultResponse, - }, + statusCode: error.statusCode, + body: { message: error.message, full_error: JSON.stringify(e) }, bypassErrorFormat: true, }); } - return response.ok({ body: { result: initResultResponse } }); - } catch (e) { - const error = transformError(e); - - return siemResponse.error({ - statusCode: error.statusCode, - body: { message: error.message, full_error: JSON.stringify(e) }, - bypassErrorFormat: true, - }); - } - }); + }) + ); }; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/privileges.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/privileges.ts index d035119ec0f1e..62b9bf4584aed 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/privileges.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/privileges.ts @@ -12,7 +12,7 @@ import { RISK_ENGINE_PRIVILEGES_URL, APP_ID } from '../../../../../common/consta import type { StartPlugins } from '../../../../plugin'; import type { SecuritySolutionPluginRouter } from '../../../../types'; -import { getUserRiskEnginePrivileges } from '../get_user_risk_engine_privileges'; +import { getUserRiskEnginePrivileges } from '../risk_engine_privileges'; export const riskEnginePrivilegesRoute = ( router: SecuritySolutionPluginRouter, diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_engine_privileges.mock.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_engine_privileges.mock.ts new file mode 100644 index 0000000000000..10c772cfcf05e --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/routes/risk_engine_privileges.mock.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 { securityMock } from '@kbn/security-plugin/server/mocks'; + +const createMockSecurityStartWithFullRiskEngineAccess = () => { + const mockSecurityStart = securityMock.createStart(); + + const mockCheckPrivileges = jest.fn().mockResolvedValue({ + hasAllRequested: true, + privileges: { + elasticsearch: { + cluster: ['manage', 'monitor'], + index: { + 'index-name': ['read'], + }, + }, + }, + }); + + mockSecurityStart.authz.checkPrivilegesDynamicallyWithRequest = jest + .fn() + .mockReturnValue(mockCheckPrivileges); + + return mockSecurityStart; +}; + +export const riskEnginePrivilegesMock = { + createMockSecurityStartWithFullRiskEngineAccess, +}; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.mock.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.mock.ts index 4b0d298ca4201..20db1f8d61735 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.mock.ts @@ -9,7 +9,7 @@ import { ALERT_RISK_SCORE, ALERT_RULE_NAME, } from '@kbn/rule-registry-plugin/common/technical_rule_data_field_names'; -import { RiskCategories } from '../../../../common/risk_engine'; +import { RiskCategories } from '../../../../common/entity_analytics/risk_engine'; import type { CalculateRiskScoreAggregations, CalculateScoresResponse, diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.ts index 996adec9c4908..a877bfd0f8e60 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/calculate_risk_scores.ts @@ -22,8 +22,8 @@ import type { IdentifierType, RiskWeights, RiskScore, -} from '../../../../common/risk_engine'; -import { RiskCategories } from '../../../../common/risk_engine'; +} from '../../../../common/entity_analytics/risk_engine'; +import { RiskCategories } from '../../../../common/entity_analytics/risk_engine'; import { withSecuritySpan } from '../../../utils/with_security_span'; import { getAfterKeyForIdentifierType, getFieldForIdentifierAgg } from './helpers'; import { diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/configurations.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/configurations.ts index 4ed0533292246..2077a152e07b4 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/configurations.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/configurations.ts @@ -5,8 +5,11 @@ * 2.0. */ import type { FieldMap } from '@kbn/alerts-as-data-utils'; -import type { IdentifierType } from '../../../../common/risk_engine'; -import { RiskScoreEntity, riskScoreBaseIndexName } from '../../../../common/risk_engine'; +import type { IdentifierType } from '../../../../common/entity_analytics/risk_engine'; +import { + RiskScoreEntity, + riskScoreBaseIndexName, +} from '../../../../common/entity_analytics/risk_engine'; import type { IIndexPatternString } from '../utils/create_datastream'; const commonRiskFields: FieldMap = { diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/helpers.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/helpers.ts index 617b2c5a03c10..59b8b0535f97d 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/helpers.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/helpers.ts @@ -5,7 +5,11 @@ * 2.0. */ -import type { AfterKey, AfterKeys, IdentifierType } from '../../../../common/risk_engine'; +import type { + AfterKey, + AfterKeys, + IdentifierType, +} from '../../../../common/entity_analytics/risk_engine'; import type { CalculateAndPersistScoresResponse } from '../types'; export const getFieldForIdentifierAgg = (identifierType: IdentifierType): string => diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_engine_data_writer.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_engine_data_writer.ts index 8e859449bb32d..e43b44ab01894 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_engine_data_writer.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_engine_data_writer.ts @@ -7,7 +7,7 @@ import type { BulkOperationContainer } from '@elastic/elasticsearch/lib/api/types'; import type { Logger, ElasticsearchClient } from '@kbn/core/server'; -import type { IdentifierType, RiskScore } from '../../../../common/risk_engine'; +import type { IdentifierType, RiskScore } from '../../../../common/entity_analytics/risk_engine'; interface WriterBulkResponse { errors: string[]; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.ts index 0b8cea72d25c2..54a2dc9f78c82 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_data_client.ts @@ -24,7 +24,7 @@ import { import { createDataStream } from '../utils/create_datastream'; import type { RiskEngineDataWriter as Writer } from './risk_engine_data_writer'; import { RiskEngineDataWriter } from './risk_engine_data_writer'; -import { getRiskScoreLatestIndex } from '../../../../common/risk_engine'; +import { getRiskScoreLatestIndex } from '../../../../common/entity_analytics/risk_engine'; import { getLatestTransformId, createTransform } from '../utils/transforms'; import { getRiskInputsIndex } from './get_risk_inputs_index'; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_service.mock.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_service.mock.ts index 68b5b55dbda42..e72852f6ea47a 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_service.mock.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_score_service.mock.ts @@ -6,7 +6,7 @@ */ import type { RiskScoreService } from './risk_score_service'; -import type { RiskScore } from '../../../../common/risk_engine'; +import type { RiskScore } from '../../../../common/entity_analytics/risk_engine'; const createRiskScoreMock = (overrides: Partial = {}): RiskScore => ({ '@timestamp': '2023-02-15T00:15:19.231Z', diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.test.ts index 40b3ee800b1bd..86bdc0d0e6be0 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.test.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { RiskWeightTypes, RiskCategories } from '../../../../common/risk_engine'; +import { RiskWeightTypes, RiskCategories } from '../../../../common/entity_analytics/risk_engine'; import { buildCategoryAssignment, buildCategoryWeights, diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.ts index 225887f2dce55..f0af4360b8631 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/risk_weights.ts @@ -12,8 +12,8 @@ import type { RiskCategoryRiskWeight, RiskWeight, RiskWeights, -} from '../../../../common/risk_engine'; -import { RiskCategories, RiskWeightTypes } from '../../../../common/risk_engine'; +} from '../../../../common/entity_analytics/risk_engine'; +import { RiskCategories, RiskWeightTypes } from '../../../../common/entity_analytics/risk_engine'; const RISK_CATEGORIES = Object.values(RiskCategories); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/calculation.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/calculation.ts index bb77d999aef4b..1822c038b7d1d 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/calculation.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/calculation.ts @@ -13,7 +13,7 @@ import { DEFAULT_RISK_SCORE_PAGE_SIZE, RISK_SCORE_CALCULATION_URL, } from '../../../../../common/constants'; -import { riskScoreCalculationRequestSchema } from '../../../../../common/risk_engine/risk_score_calculation/request_schema'; +import { riskScoreCalculationRequestSchema } from '../../../../../common/entity_analytics/risk_engine/risk_score_calculation/request_schema'; import type { SecuritySolutionPluginRouter } from '../../../../types'; import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; import { riskScoreServiceFactory } from '../risk_score_service'; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.test.ts index 09f6335077c86..9a525a5bae0d5 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.test.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.test.ts @@ -8,7 +8,10 @@ import { loggerMock } from '@kbn/logging-mocks'; import { RISK_SCORE_PREVIEW_URL } from '../../../../../common/constants'; -import { RiskCategories, RiskWeightTypes } from '../../../../../common/risk_engine'; +import { + RiskCategories, + RiskWeightTypes, +} from '../../../../../common/entity_analytics/risk_engine'; import { serverMock, requestContextMock, diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.ts index eaf54cf5e5dea..13f3ee8a9df07 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/routes/preview.ts @@ -14,7 +14,7 @@ import { DEFAULT_RISK_SCORE_PAGE_SIZE, RISK_SCORE_PREVIEW_URL, } from '../../../../../common/constants'; -import { riskScorePreviewRequestSchema } from '../../../../../common/risk_engine/risk_score_preview/request_schema'; +import { riskScorePreviewRequestSchema } from '../../../../../common/entity_analytics/risk_engine/risk_score_preview/request_schema'; import type { SecuritySolutionPluginRouter } from '../../../../types'; import { buildRouteValidation } from '../../../../utils/build_validation/route_validation'; import { riskScoreServiceFactory } from '../risk_score_service'; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/helpers.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/helpers.ts index b2db9b348b55e..0fbb0ef2c8652 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/helpers.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/helpers.ts @@ -14,7 +14,7 @@ import { } from '@kbn/core/server'; import { addSpaceIdToPath } from '@kbn/spaces-plugin/server'; -import type { Range } from '../../../../../common/risk_engine'; +import type { Range } from '../../../../../common/entity_analytics/risk_engine'; export const convertDateToISOString = (dateString: string): string => { const date = datemath.parse(dateString); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/risk_scoring_task.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/risk_scoring_task.ts index 61a733907fb38..a1539167bbaf4 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/risk_scoring_task.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_score/tasks/risk_scoring_task.ts @@ -18,7 +18,7 @@ import type { TaskManagerStartContract, } from '@kbn/task-manager-plugin/server'; import type { AnalyticsServiceSetup } from '@kbn/core-analytics-server'; -import type { AfterKeys, IdentifierType } from '../../../../../common/risk_engine'; +import type { AfterKeys, IdentifierType } from '../../../../../common/entity_analytics/risk_engine'; import type { StartPlugins } from '../../../../plugin'; import { type RiskScoreService, riskScoreServiceFactory } from '../risk_score_service'; import { RiskEngineDataClient } from '../../risk_engine/risk_engine_data_client'; @@ -31,7 +31,7 @@ import { } from './state'; import { INTERVAL, SCOPE, TIMEOUT, TYPE, VERSION } from './constants'; import { buildScopedInternalSavedObjectsClientUnsafe, convertRangeToISO } from './helpers'; -import { RiskScoreEntity } from '../../../../../common/risk_engine/types'; +import { RiskScoreEntity } from '../../../../../common/entity_analytics/risk_engine/types'; import { RISK_SCORE_EXECUTION_SUCCESS_EVENT, RISK_SCORE_EXECUTION_ERROR_EVENT, diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/types.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/types.ts index 5968b00c28b1d..acdb1982011d2 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/types.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/types.ts @@ -14,7 +14,7 @@ import type { Range, RiskEngineStatus, RiskScore, -} from '../../../common/risk_engine'; +} from '../../../common/entity_analytics/risk_engine'; export interface CalculateScoresParams { afterKeys: AfterKeys; @@ -98,16 +98,6 @@ export interface DisableRiskEngineResponse { success: boolean; } -export interface RiskEnginePrivilegesResponse { - privileges: { - elasticsearch: { - cluster: Record; - index: Record>; - }; - }; - has_all_required: boolean; -} - export interface CalculateRiskScoreAggregations { user?: { after_key: AfterKey; diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/utils/check_and_format_privileges.test.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/utils/check_and_format_privileges.test.ts new file mode 100644 index 0000000000000..0001dfdac4143 --- /dev/null +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/utils/check_and_format_privileges.test.ts @@ -0,0 +1,149 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { _formatPrivileges } from './check_and_format_privileges'; + +describe('_formatPrivileges', () => { + it('should correctly format elasticsearch index privileges', () => { + const privileges = { + elasticsearch: { + cluster: [], + index: { + index1: [ + { + privilege: 'read', + authorized: true, + }, + { + privilege: 'write', + authorized: true, + }, + ], + index2: [ + { + privilege: 'read', + authorized: true, + }, + { + privilege: 'write', + authorized: true, + }, + ], + }, + }, + kibana: [], + }; + + const result = _formatPrivileges(privileges); + + expect(result).toEqual({ + elasticsearch: { + index: { + index1: { + read: true, + write: true, + }, + index2: { + read: true, + write: true, + }, + }, + }, + }); + }); + + it('should correctly format elasticsearch cluster privileges', () => { + const privileges = { + elasticsearch: { + cluster: [ + { + privilege: 'manage', + authorized: true, + }, + { + privilege: 'monitor', + authorized: true, + }, + ], + index: {}, + }, + kibana: [], + }; + + const result = _formatPrivileges(privileges); + + expect(result).toEqual({ + elasticsearch: { + cluster: { + manage: true, + monitor: true, + }, + }, + }); + }); + + it('should correctly format elasticsearch cluster and index privileges', () => { + const privileges = { + elasticsearch: { + cluster: [ + { + privilege: 'manage', + authorized: true, + }, + { + privilege: 'monitor', + authorized: true, + }, + ], + index: { + index1: [ + { + privilege: 'read', + authorized: true, + }, + { + privilege: 'write', + authorized: true, + }, + ], + index2: [ + { + privilege: 'read', + authorized: true, + }, + { + privilege: 'write', + authorized: true, + }, + ], + }, + }, + kibana: [], + }; + + const result = _formatPrivileges(privileges); + + expect(result).toEqual({ + elasticsearch: { + cluster: { + manage: true, + monitor: true, + }, + index: { + index1: { + read: true, + write: true, + }, + index2: { + read: true, + write: true, + }, + }, + }, + }); + }); +}); diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/get_user_risk_engine_privileges.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/utils/check_and_format_privileges.ts similarity index 53% rename from x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/get_user_risk_engine_privileges.ts rename to x-pack/plugins/security_solution/server/lib/entity_analytics/utils/check_and_format_privileges.ts index d399b628c4adf..52348ad1be879 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/risk_engine/get_user_risk_engine_privileges.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/utils/check_and_format_privileges.ts @@ -6,13 +6,12 @@ */ import type { KibanaRequest } from '@kbn/core/server'; -import type { SecurityPluginStart } from '@kbn/security-plugin/server'; -import type { RiskEnginePrivilegesResponse } from '../types'; -import { - RISK_ENGINE_REQUIRED_ES_CLUSTER_PRIVILEGES, - RISK_ENGINE_REQUIRED_ES_INDEX_PRIVILEGES, -} from '../../../../common/risk_engine'; - +import type { + CheckPrivilegesPayload, + CheckPrivilegesResponse, + SecurityPluginStart, +} from '@kbn/security-plugin/server'; +import type { EntityAnalyticsPrivileges } from '../../../../common/api/entity_analytics/common'; const groupPrivilegesByName = ( privileges: Array<{ privilege: PrivilegeName; @@ -25,18 +24,9 @@ const groupPrivilegesByName = ( }, {}); }; -export async function getUserRiskEnginePrivileges( - request: KibanaRequest, - security: SecurityPluginStart -): Promise { - const checkPrivileges = security.authz.checkPrivilegesDynamicallyWithRequest(request); - const { privileges, hasAllRequested } = await checkPrivileges({ - elasticsearch: { - cluster: RISK_ENGINE_REQUIRED_ES_CLUSTER_PRIVILEGES, - index: RISK_ENGINE_REQUIRED_ES_INDEX_PRIVILEGES, - }, - }); - +export const _formatPrivileges = ( + privileges: CheckPrivilegesResponse['privileges'] +): EntityAnalyticsPrivileges['privileges'] => { const clusterPrivilegesByPrivilege = groupPrivilegesByName(privileges.elasticsearch.cluster); const indexPrivilegesByIndex = Object.entries(privileges.elasticsearch.index).reduce< @@ -47,12 +37,37 @@ export async function getUserRiskEnginePrivileges( }, {}); return { - privileges: { - elasticsearch: { - cluster: clusterPrivilegesByPrivilege, - index: indexPrivilegesByIndex, - }, + elasticsearch: { + ...(Object.keys(indexPrivilegesByIndex).length > 0 + ? { + index: indexPrivilegesByIndex, + } + : {}), + ...(Object.keys(clusterPrivilegesByPrivilege).length > 0 + ? { + cluster: clusterPrivilegesByPrivilege, + } + : {}), }, + }; +}; + +interface CheckAndFormatPrivilegesOpts { + request: KibanaRequest; + security: SecurityPluginStart; + privilegesToCheck: Pick; +} + +export async function checkAndFormatPrivileges({ + request, + security, + privilegesToCheck, +}: CheckAndFormatPrivilegesOpts): Promise { + const checkPrivileges = security.authz.checkPrivilegesDynamicallyWithRequest(request); + const { privileges, hasAllRequested } = await checkPrivileges(privilegesToCheck); + + return { + privileges: _formatPrivileges(privileges), has_all_required: hasAllRequested, }; } diff --git a/x-pack/plugins/security_solution/server/lib/entity_analytics/utils/create_datastream.ts b/x-pack/plugins/security_solution/server/lib/entity_analytics/utils/create_datastream.ts index 3ca4572d945ed..491cb5e61122b 100644 --- a/x-pack/plugins/security_solution/server/lib/entity_analytics/utils/create_datastream.ts +++ b/x-pack/plugins/security_solution/server/lib/entity_analytics/utils/create_datastream.ts @@ -6,8 +6,7 @@ */ // This file is a copy of x-pack/plugins/alerting/server/alerts_service/lib/create_concrete_write_index.ts -// original function create index instead of datastream, and their have plan to use datastream in the future -// so we probably should remove this file and use the original when datastream will be supported +// The original function created an index, while here we create a datastream. If and when responseOps develops first-party code to work with datastreams (https://github.com/elastic/kibana/issues/140403), this file should be removed. import { get } from 'lodash'; import type { IndicesSimulateIndexTemplateResponse } from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/__mocks__/index.ts b/x-pack/plugins/security_solution/server/lib/telemetry/__mocks__/index.ts index 153512b4d68b5..97a0aa16a1518 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/__mocks__/index.ts @@ -85,6 +85,7 @@ export const createMockTelemetryReceiver = ( openPointInTime: jest.fn().mockReturnValue(Promise.resolve('test-pit-id')), getAlertsIndex: jest.fn().mockReturnValue('alerts-*'), fetchDiagnosticAlertsBatch: jest.fn().mockReturnValue(diagnosticsAlert ?? jest.fn()), + getExperimentalFeatures: jest.fn().mockReturnValue(undefined), fetchEndpointMetrics: jest.fn().mockReturnValue(stubEndpointMetricsResponse), fetchEndpointPolicyResponses: jest.fn(), fetchPrebuiltRuleAlertsBatch: jest.fn().mockReturnValue(prebuiltRuleAlertsResponse), diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/helpers.ts b/x-pack/plugins/security_solution/server/lib/telemetry/helpers.ts index 3bd6a0d595a79..32ac1cf7f4dd0 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/helpers.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/helpers.ts @@ -382,7 +382,7 @@ export class TelemetryTimelineFetcher { const eventId = event._source ? event._source['event.id'] : 'unknown'; const alertUUID = event._source ? event._source['kibana.alert.uuid'] : 'unknown'; - const entities = resolverEntity([event]); + const entities = resolverEntity([event], this.receiver.getExperimentalFeatures()); // Build Tree const tree = await this.receiver.buildProcessTree( diff --git a/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts b/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts index 3d17310c6035d..b3dc89d613d67 100644 --- a/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts +++ b/x-pack/plugins/security_solution/server/lib/telemetry/receiver.ts @@ -44,6 +44,7 @@ import type { } from '@kbn/fleet-plugin/server'; import type { ExceptionListClient } from '@kbn/lists-plugin/server'; import moment from 'moment'; +import type { ExperimentalFeatures } from '../../../common'; import type { EndpointAppContextService } from '../../endpoint/endpoint_app_context_services'; import { exceptionListItemToTelemetryEntry, @@ -197,6 +198,8 @@ export interface ITelemetryReceiver { fetchValueListMetaData(interval: number): Promise; getAlertsIndex(): string | undefined; + + getExperimentalFeatures(): ExperimentalFeatures | undefined; } export class TelemetryReceiver implements ITelemetryReceiver { @@ -211,6 +214,7 @@ export class TelemetryReceiver implements ITelemetryReceiver { private clusterInfo?: ESClusterInfo; private processTreeFetcher?: Fetcher; private packageService?: PackageService; + private experimentalFeatures: ExperimentalFeatures | undefined; private readonly maxRecords = 10_000 as const; constructor(logger: Logger) { @@ -235,7 +239,7 @@ export class TelemetryReceiver implements ITelemetryReceiver { this.soClient = core?.savedObjects.createInternalRepository() as unknown as SavedObjectsClientContract; this.clusterInfo = await this.fetchClusterInfo(); - + this.experimentalFeatures = endpointContextService?.experimentalFeatures; const elasticsearch = core?.elasticsearch.client as unknown as IScopedClusterClient; this.processTreeFetcher = new Fetcher(elasticsearch); } @@ -248,6 +252,10 @@ export class TelemetryReceiver implements ITelemetryReceiver { return this.alertsIndex; } + public getExperimentalFeatures(): ExperimentalFeatures | undefined { + return this.experimentalFeatures; + } + public async fetchDetectionRulesPackageVersion(): Promise { return this.packageService?.asInternalUser.getInstallation(PREBUILT_RULES_PACKAGE_NAME); } diff --git a/x-pack/plugins/security_solution/server/plugin.ts b/x-pack/plugins/security_solution/server/plugin.ts index ec01a2800dff8..227a7ea7e1439 100644 --- a/x-pack/plugins/security_solution/server/plugin.ts +++ b/x-pack/plugins/security_solution/server/plugin.ts @@ -46,7 +46,7 @@ import { AppClientFactory } from './client'; import type { ConfigType } from './config'; import { createConfig } from './config'; import { initUiSettings } from './ui_settings'; -import { APP_ID, DEFAULT_ALERTS_INDEX, SERVER_APP_ID } from '../common/constants'; +import { APP_ID, APP_UI_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'; @@ -109,8 +109,12 @@ import { import { AppFeaturesService } from './lib/app_features_service/app_features_service'; import { registerRiskScoringTask } from './lib/entity_analytics/risk_score/tasks/risk_scoring_task'; import { registerProtectionUpdatesNoteRoutes } from './endpoint/routes/protection_updates_note'; -import { latestRiskScoreIndexPattern, allRiskScoreIndexPattern } from '../common/risk_engine'; +import { + latestRiskScoreIndexPattern, + allRiskScoreIndexPattern, +} from '../common/entity_analytics/risk_engine'; import { isEndpointPackageV2 } from '../common/endpoint/utils/package_v2'; +import { getAssistantTools } from './assistant/tools'; export type { SetupPlugins, StartPlugins, PluginSetup, PluginStart } from './plugin_contract'; @@ -506,6 +510,9 @@ export class Plugin implements ISecuritySolutionPlugin { this.licensing$ = plugins.licensing.license$; + // Assistant Tool and Feature Registration + plugins.elasticAssistant.registerTools(APP_UI_ID, getAssistantTools()); + if (this.lists && plugins.taskManager && plugins.fleet) { // Exceptions, Artifacts and Manifests start const taskManager = plugins.taskManager; diff --git a/x-pack/plugins/security_solution/server/plugin_contract.ts b/x-pack/plugins/security_solution/server/plugin_contract.ts index b59d9aac37efe..8370e405c6807 100644 --- a/x-pack/plugins/security_solution/server/plugin_contract.ts +++ b/x-pack/plugins/security_solution/server/plugin_contract.ts @@ -41,6 +41,7 @@ import type { CloudExperimentsPluginStart } from '@kbn/cloud-experiments-plugin/ import type { SharePluginStart } from '@kbn/share-plugin/server'; import type { GuidedOnboardingPluginSetup } from '@kbn/guided-onboarding-plugin/server'; import type { PluginSetup as UnifiedSearchServerPluginSetup } from '@kbn/unified-search-plugin/server'; +import type { ElasticAssistantPluginStart } from '@kbn/elastic-assistant-plugin/server'; import type { AppFeaturesService } from './lib/app_features_service/app_features_service'; import type { ExperimentalFeatures } from '../common'; @@ -72,6 +73,7 @@ export interface SecuritySolutionPluginStartDependencies { cloudExperiments?: CloudExperimentsPluginStart; data: DataPluginStart; dataViews: DataViewsPluginStart; + elasticAssistant: ElasticAssistantPluginStart; eventLog: IEventLogClientService; fleet?: FleetPluginStart; licensing: LicensingPluginStart; diff --git a/x-pack/plugins/security_solution/server/routes/index.ts b/x-pack/plugins/security_solution/server/routes/index.ts index 0adf8f3949d99..ce863fb861076 100644 --- a/x-pack/plugins/security_solution/server/routes/index.ts +++ b/x-pack/plugins/security_solution/server/routes/index.ts @@ -73,6 +73,7 @@ import { assetCriticalityUpsertRoute, assetCriticalityGetRoute, assetCriticalityDeleteRoute, + assetCriticalityPrivilegesRoute, } from '../lib/entity_analytics/asset_criticality/routes'; export const initRoutes = ( @@ -173,5 +174,6 @@ export const initRoutes = ( assetCriticalityUpsertRoute(router, logger); assetCriticalityGetRoute(router, logger); assetCriticalityDeleteRoute(router, logger); + assetCriticalityPrivilegesRoute(router, getStartServices, logger); } }; diff --git a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/managed_details/index.ts b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/managed_details/index.ts index a454573164008..70686e373acae 100644 --- a/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/managed_details/index.ts +++ b/x-pack/plugins/security_solution/server/search_strategy/security_solution/factory/users/managed_details/index.ts @@ -16,15 +16,14 @@ import { buildManagedUserDetailsQuery } from './query.managed_user_details.dsl'; import type { UsersQueries } from '../../../../../../common/search_strategy/security_solution/users'; import type { ManagedUserHits, - ManagedUserHit, ManagedUserDetailsStrategyResponse, ManagedUserFields, + ManagedUserDatasetKey, } from '../../../../../../common/search_strategy/security_solution/users/managed_details'; -import { ManagedUserDatasetKey } from '../../../../../../common/search_strategy/security_solution/users/managed_details'; interface ManagedUserBucket { key: ManagedUserDatasetKey; - latest_hit: SearchResponse; + latest_hit: SearchResponse; } export const managedUserDetails: SecuritySolutionFactory = { @@ -43,24 +42,10 @@ export const managedUserDetails: SecuritySolutionFactory { - acc[bucket.key] = bucket.latest_hit.hits.hits[0] as unknown as ManagedUserHit; - return acc; - }, - {} as ManagedUserHits - ); - - if (buckets.length === 0) { - return { - ...response, - inspect, - users: { - [ManagedUserDatasetKey.ENTRA]: undefined, - [ManagedUserDatasetKey.OKTA]: undefined, - }, - }; - } + const managedUsers = buckets.reduce((acc, bucket) => { + acc[bucket.key] = bucket.latest_hit.hits.hits[0]; + return acc; + }, {}); return { ...response, diff --git a/x-pack/plugins/security_solution/tsconfig.json b/x-pack/plugins/security_solution/tsconfig.json index f123d8d80ae56..2158fe97996dc 100644 --- a/x-pack/plugins/security_solution/tsconfig.json +++ b/x-pack/plugins/security_solution/tsconfig.json @@ -84,6 +84,7 @@ "@kbn/kibana-react-plugin", "@kbn/ecs-data-quality-dashboard", "@kbn/elastic-assistant", + "@kbn/elastic-assistant-plugin", "@kbn/data-views-plugin", "@kbn/datemath", "@kbn/ui-theme", @@ -182,6 +183,8 @@ "@kbn/shared-ux-error-boundary", "@kbn/zod-helpers", "@kbn/core-http-common", - "@kbn/stack-connectors-plugin" + "@kbn/search-errors", + "@kbn/stack-connectors-plugin", + "@kbn/elastic-assistant-common" ] } diff --git a/x-pack/test/detection_engine_api_integration/utils/data_generator/types.ts b/x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/__mocks__/content_wrapper.tsx similarity index 66% rename from x-pack/test/detection_engine_api_integration/utils/data_generator/types.ts rename to x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/__mocks__/content_wrapper.tsx index bbf54be68cfee..442ef3a598bd7 100644 --- a/x-pack/test/detection_engine_api_integration/utils/data_generator/types.ts +++ b/x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/__mocks__/content_wrapper.tsx @@ -5,6 +5,6 @@ * 2.0. */ -export type IndexingInterval = [string | Date, string | Date]; +import React from 'react'; -export type Document = Record; +export const ContentWrapper = ({ children }: { children: React.ReactElement }) => <>{children}; diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/add_integration_image.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/add_integration_image.tsx new file mode 100644 index 0000000000000..b9917c40e4056 --- /dev/null +++ b/x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/add_integration_image.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 from 'react'; +import connectToDataSources from '../../images/connect_to_existing_sources.png'; +import { ADD_INTEGRATIONS_IMAGE_TITLE } from '../../translations'; +import { ContentWrapper } from './content_wrapper'; + +const AddIntegrationsImageComponent = () => { + return ( + + {ADD_INTEGRATIONS_IMAGE_TITLE} + + ); +}; + +export const AddIntegrationsImage = React.memo(AddIntegrationsImageComponent); diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/content_wrapper.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/content_wrapper.tsx new file mode 100644 index 0000000000000..49b4765cbeb33 --- /dev/null +++ b/x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/content_wrapper.tsx @@ -0,0 +1,25 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { useStepContentStyles } from '../../styles/step_content.styles'; + +const ContentWrapperComponent: React.FC<{ children: React.ReactElement; shadow?: boolean }> = ({ + children, + shadow = true, +}) => { + const { getRightContentStyles } = useStepContentStyles(); + const rightContentStyles = getRightContentStyles({ shadow }); + + return ( +
+ {children} +
+ ); +}; + +export const ContentWrapper = React.memo(ContentWrapperComponent); diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/create_project_step_image.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/create_project_step_image.tsx new file mode 100644 index 0000000000000..268863033f9e8 --- /dev/null +++ b/x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/create_project_step_image.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 from 'react'; +import createProjects from '../../images/create_projects.png'; +import { CREATE_PROJECT_TITLE } from '../../translations'; +import { ContentWrapper } from './content_wrapper'; + +const CreateProjectImageComponent = () => ( + + {CREATE_PROJECT_TITLE} + +); + +export const CreateProjectImage = React.memo(CreateProjectImageComponent); diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/enable_rule_image.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/enable_rule_image.tsx new file mode 100644 index 0000000000000..2ef54493b7033 --- /dev/null +++ b/x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/enable_rule_image.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 from 'react'; +import enablePrebuiltRules from '../../images/enable_prebuilt_rules.png'; +import { ENABLE_RULES } from '../../translations'; +import { ContentWrapper } from './content_wrapper'; + +const EnableRuleImageComponent = () => ( + + {ENABLE_RULES} + +); + +export const EnableRuleImage = React.memo(EnableRuleImageComponent); diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/overview_video_description.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/overview_video_description.tsx similarity index 96% rename from x-pack/plugins/security_solution_serverless/public/get_started/card_step/overview_video_description.tsx rename to x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/overview_video_description.tsx index de8f272a0c25d..975119bf74e4f 100644 --- a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/overview_video_description.tsx +++ b/x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/overview_video_description.tsx @@ -6,7 +6,7 @@ */ import React from 'react'; -import { WATCH_VIDEO_DESCRIPTION1, WATCH_VIDEO_DESCRIPTION2 } from '../translations'; +import { WATCH_VIDEO_DESCRIPTION1, WATCH_VIDEO_DESCRIPTION2 } from '../../translations'; const OverviewVideoDescriptionComponent = () => ( <> diff --git a/x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/video.test.tsx b/x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/video.test.tsx new file mode 100644 index 0000000000000..a02c89399ac99 --- /dev/null +++ b/x-pack/plugins/security_solution_serverless/public/get_started/card_step/content/video.test.tsx @@ -0,0 +1,81 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import React from 'react'; +import { render, fireEvent, screen } from '@testing-library/react'; +import { Video } from './video'; +import { OverviewSteps, QuickStartSectionCardsId, SectionId } from '../../types'; +import type { EuiFlexGroupProps } from '@elastic/eui'; +import { useStepContext } from '../../context/step_context'; +import { WATCH_VIDEO_BUTTON_TITLE } from '../../translations'; +import { defaultExpandedCards } from '../../storage'; + +jest.mock('../../context/step_context'); +jest.mock('./content_wrapper'); + +jest.mock('@elastic/eui', () => ({ + EuiFlexGroup: ({ children, onClick }: EuiFlexGroupProps) => { + return ( + // eslint-disable-next-line jsx-a11y/click-events-have-key-events +
+ {children} +
+ ); + }, + EuiFlexItem: ({ children }: { children: React.ReactElement }) =>
{children}
, + EuiIcon: () => , + useEuiTheme: () => ({ euiTheme: { colors: { fullShade: '#000', emptyShade: '#fff' } } }), +})); + +describe('Video Component', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it('renders overlay if step is not completed', () => { + const { getByTestId } = render(